Imported Upstream version 4.101 upstream/4.101
authorAnas Nashif <anas.nashif@intel.com>
Tue, 30 Oct 2012 19:51:59 +0000 (12:51 -0700)
committerAnas Nashif <anas.nashif@intel.com>
Tue, 30 Oct 2012 19:51:59 +0000 (12:51 -0700)
456 files changed:
AUTHORS [new file with mode: 0644]
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]
Makefile.in [new file with mode: 0644]
Makefile.tools [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
acinclude.m4 [new file with mode: 0644]
aclocal.m4 [new file with mode: 0644]
alert/main.c [new file with mode: 0644]
alert/server.c [new file with mode: 0644]
alert/server.h [new file with mode: 0644]
attrib/att-database.h [new file with mode: 0644]
attrib/att.c [new file with mode: 0644]
attrib/att.h [new file with mode: 0644]
attrib/client.c [new file with mode: 0644]
attrib/client.h [new file with mode: 0644]
attrib/gatt-service.c [new file with mode: 0644]
attrib/gatt-service.h [new file with mode: 0644]
attrib/gatt.c [new file with mode: 0644]
attrib/gatt.h [new file with mode: 0644]
attrib/gattrib.c [new file with mode: 0644]
attrib/gattrib.h [new file with mode: 0644]
attrib/gatttool.c [new file with mode: 0644]
attrib/gatttool.h [new file with mode: 0644]
attrib/interactive.c [new file with mode: 0644]
attrib/utils.c [new file with mode: 0644]
audio/a2dp-codecs.h [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/avctp.c [new file with mode: 0644]
audio/avctp.h [new file with mode: 0644]
audio/avdtp.c [new file with mode: 0644]
audio/avdtp.h [new file with mode: 0644]
audio/avrcp.c [new file with mode: 0644]
audio/avrcp.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/gstpragma.h [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/main.c [new file with mode: 0644]
audio/manager.c [new file with mode: 0644]
audio/manager.h [new file with mode: 0644]
audio/media.c [new file with mode: 0644]
audio/media.h [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-maemo5.c [new file with mode: 0644]
audio/telephony-maemo6.c [new file with mode: 0644]
audio/telephony-ofono.c [new file with mode: 0644]
audio/telephony.h [new file with mode: 0644]
audio/transport.c [new file with mode: 0644]
audio/transport.h [new file with mode: 0644]
audio/unix.c [new file with mode: 0644]
audio/unix.h [new file with mode: 0644]
bluez.pc.in [new file with mode: 0644]
btio/btio.c [new file with mode: 0644]
btio/btio.h [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/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]
compile [new file with mode: 0755]
config.guess [new file with mode: 0755]
config.h.in [new file with mode: 0644]
config.sub [new file with mode: 0755]
configure [new file with mode: 0755]
configure.ac [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]
depcomp [new file with mode: 0755]
deviceinfo/deviceinfo.c [new file with mode: 0644]
deviceinfo/deviceinfo.h [new file with mode: 0644]
deviceinfo/main.c [new file with mode: 0644]
deviceinfo/manager.c [new file with mode: 0644]
deviceinfo/manager.h [new file with mode: 0644]
doc/adapter-api.txt [new file with mode: 0644]
doc/agent-api.txt [new file with mode: 0644]
doc/assigned-numbers.txt [new file with mode: 0644]
doc/attribute-api.txt [new file with mode: 0644]
doc/audio-api.txt [new file with mode: 0644]
doc/control-api.txt [new file with mode: 0644]
doc/device-api.txt [new file with mode: 0644]
doc/health-api.txt [new file with mode: 0644]
doc/hfp-api.txt [new file with mode: 0644]
doc/input-api.txt [new file with mode: 0644]
doc/manager-api.txt [new file with mode: 0644]
doc/media-api.txt [new file with mode: 0644]
doc/network-api.txt [new file with mode: 0644]
doc/sap-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]
emulator/btdev.c [new file with mode: 0644]
emulator/btdev.h [new file with mode: 0644]
emulator/main.c [new file with mode: 0644]
emulator/server.c [new file with mode: 0644]
emulator/server.h [new file with mode: 0644]
emulator/vhci.c [new file with mode: 0644]
emulator/vhci.h [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/polkit.c [new file with mode: 0644]
gdbus/watch.c [new file with mode: 0644]
health/hdp.c [new file with mode: 0644]
health/hdp.h [new file with mode: 0644]
health/hdp_main.c [new file with mode: 0644]
health/hdp_manager.c [new file with mode: 0644]
health/hdp_manager.h [new file with mode: 0644]
health/hdp_types.h [new file with mode: 0644]
health/hdp_util.c [new file with mode: 0644]
health/hdp_util.h [new file with mode: 0644]
health/mcap.c [new file with mode: 0644]
health/mcap.h [new file with mode: 0644]
health/mcap_internal.h [new file with mode: 0644]
health/mcap_lib.h [new file with mode: 0644]
health/mcap_sync.c [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]
install-sh [new file with mode: 0755]
lib/a2mp.h [new file with mode: 0644]
lib/bluetooth.c [new file with mode: 0644]
lib/bluetooth.h [new file with mode: 0644]
lib/bnep.h [new file with mode: 0644]
lib/cmtp.h [new file with mode: 0644]
lib/hci.c [new file with mode: 0644]
lib/hci.h [new file with mode: 0644]
lib/hci_lib.h [new file with mode: 0644]
lib/hidp.h [new file with mode: 0644]
lib/l2cap.h [new file with mode: 0644]
lib/mgmt.h [new file with mode: 0644]
lib/rfcomm.h [new file with mode: 0644]
lib/sco.h [new file with mode: 0644]
lib/sdp.c [new file with mode: 0644]
lib/sdp.h [new file with mode: 0644]
lib/sdp_lib.h [new file with mode: 0644]
lib/uuid.c [new file with mode: 0644]
lib/uuid.h [new file with mode: 0644]
ltmain.sh [new file with mode: 0644]
mgmt/main.c [new file with mode: 0644]
missing [new file with mode: 0755]
monitor/bt.h [new file with mode: 0644]
monitor/btsnoop.c [new file with mode: 0644]
monitor/btsnoop.h [new file with mode: 0644]
monitor/control.c [new file with mode: 0644]
monitor/control.h [new file with mode: 0644]
monitor/hcidump.c [new file with mode: 0644]
monitor/hcidump.h [new file with mode: 0644]
monitor/main.c [new file with mode: 0644]
monitor/mainloop.c [new file with mode: 0644]
monitor/mainloop.h [new file with mode: 0644]
monitor/packet.c [new file with mode: 0644]
monitor/packet.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/adaptername.c [new file with mode: 0644]
plugins/dbusoob.c [new file with mode: 0644]
plugins/external-dummy.c [new file with mode: 0644]
plugins/formfactor.c [new file with mode: 0644]
plugins/gatt-example.c [new file with mode: 0644]
plugins/hal.c [new file with mode: 0644]
plugins/hciops.c [new file with mode: 0644]
plugins/maemo6.c [new file with mode: 0644]
plugins/mgmtops.c [new file with mode: 0644]
plugins/pnat.c [new file with mode: 0644]
plugins/service.c [new file with mode: 0644]
plugins/storage.c [new file with mode: 0644]
plugins/wiimote.c [new file with mode: 0644]
proximity/immalert.c [new file with mode: 0644]
proximity/immalert.h [new file with mode: 0644]
proximity/linkloss.c [new file with mode: 0644]
proximity/linkloss.h [new file with mode: 0644]
proximity/main.c [new file with mode: 0644]
proximity/manager.c [new file with mode: 0644]
proximity/manager.h [new file with mode: 0644]
proximity/monitor.c [new file with mode: 0644]
proximity/monitor.h [new file with mode: 0644]
proximity/proximity.conf [new file with mode: 0644]
proximity/reporter.c [new file with mode: 0644]
proximity/reporter.h [new file with mode: 0644]
sap/main.c [new file with mode: 0644]
sap/manager.c [new file with mode: 0644]
sap/manager.h [new file with mode: 0644]
sap/sap-dummy.c [new file with mode: 0644]
sap/sap-u8500.c [new file with mode: 0644]
sap/sap.h [new file with mode: 0644]
sap/server.c [new file with mode: 0644]
sap/server.h [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_armv6.c [new file with mode: 0644]
sbc/sbc_primitives_armv6.h [new file with mode: 0644]
sbc/sbc_primitives_iwmmxt.c [new file with mode: 0644]
sbc/sbc_primitives_iwmmxt.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/bluetooth-hid2hci.rules [new file with mode: 0644]
scripts/bluetooth-serial.rules [new file with mode: 0644]
scripts/bluetooth_serial [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/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/attio.h [new file with mode: 0644]
src/attrib-server.c [new file with mode: 0644]
src/attrib-server.h [new file with mode: 0644]
src/bluetooth.conf [new file with mode: 0644]
src/bluetooth.service.in [new file with mode: 0644]
src/bluetooth.ver [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/device.c [new file with mode: 0644]
src/device.h [new file with mode: 0644]
src/eir.c [new file with mode: 0644]
src/eir.h [new file with mode: 0644]
src/error.c [new file with mode: 0644]
src/error.h [new file with mode: 0644]
src/event.c [new file with mode: 0644]
src/event.h [new file with mode: 0644]
src/genbuiltin [new file with mode: 0755]
src/glib-helper.c [new file with mode: 0644]
src/glib-helper.h [new file with mode: 0644]
src/hcid.h [new file with mode: 0644]
src/log.c [new file with mode: 0644]
src/log.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/oob.c [new file with mode: 0644]
src/oob.h [new file with mode: 0644]
src/org.bluez.service [new file with mode: 0644]
src/oui.c [new file with mode: 0644]
src/oui.h [new file with mode: 0644]
src/plugin.c [new file with mode: 0644]
src/plugin.h [new file with mode: 0644]
src/ppoll.h [new file with mode: 0644]
src/rfkill.c [new file with mode: 0644]
src/sdp-client.c [new file with mode: 0644]
src/sdp-client.h [new file with mode: 0644]
src/sdp-xml.c [new file with mode: 0644]
src/sdp-xml.h [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/storage.c [new file with mode: 0644]
src/storage.h [new file with mode: 0644]
src/textfile.c [new file with mode: 0644]
src/textfile.h [new file with mode: 0644]
src/uinput.h [new file with mode: 0644]
test/agent.c [new file with mode: 0644]
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/gaptest.c [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/ipctest.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/mpris-player.c [new file with mode: 0644]
test/rctest.1 [new file with mode: 0644]
test/rctest.c [new file with mode: 0644]
test/sap-client [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-endpoint [new file with mode: 0755]
test/simple-player [new file with mode: 0755]
test/simple-service [new file with mode: 0755]
test/test-adapter [new file with mode: 0755]
test/test-attrib [new file with mode: 0755]
test/test-audio [new file with mode: 0755]
test/test-device [new file with mode: 0755]
test/test-discovery [new file with mode: 0755]
test/test-health [new file with mode: 0755]
test/test-health-sink [new file with mode: 0755]
test/test-input [new file with mode: 0755]
test/test-manager [new file with mode: 0755]
test/test-nap [new file with mode: 0755]
test/test-network [new file with mode: 0755]
test/test-oob [new file with mode: 0755]
test/test-proximity [new file with mode: 0755]
test/test-sap-server [new file with mode: 0755]
test/test-serial [new file with mode: 0755]
test/test-serial-proxy [new file with mode: 0755]
test/test-service [new file with mode: 0755]
test/test-telephony [new file with mode: 0755]
test/test-textfile.c [new file with mode: 0644]
test/test-thermometer [new file with mode: 0755]
test/uuidtest.c [new file with mode: 0644]
thermometer/main.c [new file with mode: 0644]
thermometer/manager.c [new file with mode: 0644]
thermometer/manager.h [new file with mode: 0644]
thermometer/thermometer.c [new file with mode: 0644]
thermometer/thermometer.h [new file with mode: 0644]
time/main.c [new file with mode: 0644]
time/server.c [new file with mode: 0644]
time/server.h [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/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_ath3k.c [new file with mode: 0644]
tools/hciattach_intel.c [new file with mode: 0644]
tools/hciattach_qualcomm.c [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/kword.c [new file with mode: 0644]
tools/kword.h [new file with mode: 0644]
tools/l2ping.8 [new file with mode: 0644]
tools/l2ping.c [new file with mode: 0644]
tools/lexer.c [new file with mode: 0644]
tools/lexer.l [new file with mode: 0644]
tools/parser.c [new file with mode: 0644]
tools/parser.h [new file with mode: 0644]
tools/parser.y [new file with mode: 0644]
tools/ppporc.c [new file with mode: 0644]
tools/rfcomm.1 [new file with mode: 0644]
tools/rfcomm.c [new file with mode: 0644]
tools/rfcomm.conf [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]
unit/test-eir.c [new file with mode: 0644]
ylwrap [new file with mode: 0755]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..2eaa329
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,61 @@
+Maxim Krasnyansky <maxk@qualcomm.com>
+Marcel Holtmann <marcel@holtmann.org>
+Stephen Crane <steve.crane@rococosoft.com>
+Jean Tourrilhes <jt@hpl.hp.com>
+Jan Beutel <j.beutel@ieee.org>
+Ilguiz Latypov <ilatypov@superbt.com>
+Thomas Moser <thomas.moser@tmoser.ch>
+Nils Faerber <nils@kernelconcepts.de>
+Martin Leopold <martin@leopold.dk>
+Wolfgang Heidrich <wolfgang.heidrich@esk.fhg.de>
+Fabrizio Gennari <fabrizio.gennari@philips.com>
+Brad Midgley <bmidgley@xmission.com>
+Henryk Ploetz <henryk@ploetzli.ch>
+Philip Blundell <pb@nexus.co.uk>
+Johan Hedberg <johan.hedberg@intel.com>
+Claudio Takahasi <claudio.takahasi@indt.org.br>
+Eduardo Rocha <eduardo.rocha@indt.org.br>
+Denis Kenzior <denis.kenzior@trolltech.com>
+Frederic Dalleau <frederic.dalleau@access-company.com>
+Frederic Danis <frederic.danis@access-company.com>
+Luiz Augusto von Dentz <luiz.dentz@gmail.com>
+Fabien Chevalier <fabchevalier@free.fr>
+Ohad Ben-Cohen <ohad@bencohen.org>
+Daniel Gollub <dgollub@suse.de>
+Tom Patzig <tpatzig@suse.de>
+Kai Vehmanen <kai.vehmanen@nokia.com>
+Vinicius Gomes <vinicius.gomes@openbossa.org>
+Alok Barsode <alok.barsode@azingo.com>
+Bastien Nocera <hadess@hadess.net>
+Albert Huang <albert@csail.mit.edu>
+Glenn Durfee <gdurfee@google.com>
+David Woodhouse <david.woodhouse@intel.com>
+Christian Hoene <hoene@uni-tuebingen.de>
+Pekka Pessi <pekka.pessi@nokia.com>
+Siarhei Siamashka <siarhei.siamashka@nokia.com>
+Nick Pelly <npelly@google.com>
+Lennart Poettering <lennart@poettering.net>
+Gustavo Padovan <gustavo@padovan.org>
+Marc-Andre Lureau <marc-andre.lureau@nokia.com>
+Bea Lam <bea.lam@nokia.com>
+Zygo Blaxell <zygo.blaxell@xandros.com>
+Forrest Zhao <forrest.zhao@intel.com>
+Scott Talbot <psyc@stalbot.com>
+Ilya Rubtsov <lusyaru@gmail.com>
+Mario Limonciello <mario_limonciello@dell.com>
+Filippo Giunchedi <filippo@esaurito.net>
+Jaikumar Ganesh <jaikumar@google.com>
+Elvis Pfutzenreuter <epx@signove.com>
+Santiago Carot-Nemesio <scarot@libresoft.es>
+José Antonio Santos Cadenas <jcaden@libresoft.es>
+Francisco Alecrim <francisco.alecrim@openbossa.org>
+Daniel Orstadius <daniel.orstadius@gmail.com>
+Anderson Briglia <anderson.briglia@openbossa.org>
+Anderson Lizardo <anderson.lizardo@openbossa.org>
+Bruna Moreira <bruna.moreira@openbossa.org>
+Brian Gix <bgix@codeaurora.org>
+Andre Guedes <andre.guedes@openbossa.org>
+Sheldon Demario <sheldon.demario@openbossa.org>
+Lucas De Marchi <lucas.demarchi@profusion.mobi>
+Szymon Janc <szymon.janc@tieto.com>
+Syam Sidhardhan <s.syam@samsung.com>
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..0ea7db6
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1722 @@
+ver 4.101:
+       Fix issue with missing BlueZ service file.
+       Fix issue with aborting A2DP setup during AVDTP start.
+       Fix issue with handling of multiple A2DP indication.
+       Fix issue with handling AVDTP abort with invalid SEID.
+       Fix issue with rejecting AVDTP abort commands.
+       Add support for handling AVDTP command collision.
+
+ver 4.100:
+       Fix issue with crashing when SCO connection fails.
+       Fix issue with HFP gateway failing on first GSM connection.
+       Fix issue with AVRCP and handling of vendor commands.
+       Fix issue with handling AVRCP subunit info command.
+       Fix issue with missing capability for AVRCP track reached end.
+       Fix issue with AVDTP signaling and GStreamer SBC NULL check.
+       Fix issue with AVDTP Reconfigure Reject message.
+       Fix issue with incorrect EIR length parsing.
+       Fix issue with SDP disconnect for HIDSDPDisable.
+       Fix issue with SDP interoperability with Mac OS X Lion.
+       Fix issue with reverse SDP discovery with some devices.
+       Fix issue with discovering state during power off operation.
+       Add support for AVRCP Volume Changed notifications.
+       Add support for AVRCP Set Absolute Volume handling.
+       Add support for display legacy PIN code agent method.
+       Add support for multiple media transports per endpoint.
+       Add support for discovering device information characteristics.
+       Add support for vendor source for Device ID setting.
+       Add support for immediate alert server.
+       Add support for link loss server.
+
+       Notes:
+       This version requires D-Bus 1.4 or later.
+       This version requires GLib 2.28 or later.
+
+ver 4.99:
+       Fix issue with missing retries for BNEP connection setup.
+       Fix issue with not showing name if first EIR has no details.
+       Fix issue with running SDP discovery for LE devices.
+       Add support for GATT using 128-bit Bluetooth UUIDs.
+       Add support for retrieving key size information.
+       Add support for storing Long Term Keys.
+       Add support for Proximity Reporter API.
+       Add support for KeyboardDisplay IO capability.
+       Add support for version 1.0 of management API.
+       Add support for monitoring interface.
+
+ver 4.98:
+       Fix issue with adapter list upon initialization failure.
+       Fix issue with missing legacy property for Low Energy.
+       Fix issue with missing EIR information handling.
+       Fix issue with device address type tracking.
+       Fix issue with alert level characteristic.
+       Fix issue with headset shutdown handling.
+       Fix issue with Wiimote address handling.
+       Add support for advanced l2test options.
+       Add support for attribute protocol and multiple adapters.
+
+ver 4.97:
+       Update support for proximity profile.
+       Fix issue with SBC audio decoding quality.
+       Fix multiple issues with HFP support.
+       Fix multiple issues with A2DP support.
+       Fix multiple issues with AVDTP support.
+       Fix multiple issues with AVRCP support.
+       Add support for AVRCP meta-data transfer.
+       Add support for Bluetooth based thermometers.
+
+ver 4.96:
+       Fix issue with race condition in AVDTP stream start.
+       Fix issue with global adapter offline switching.
+       Fix issue with pairing and No Bonding devices.
+       Add support for Nintendo Wii Remote pairing.
+
+ver 4.95:
+       Fix issue with AVCTP replies with invalid PID.
+       Fix issue with AVRCP and unknown packet types.
+       Fix issue with AVRCP not using NOT_IMPLEMENTED correctly.
+       Fix issue with AVDTP discovery if all endpoints are in use.
+       Fix issue with invalid memory writes and media support.
+       Fix issue with not removing device alias and unbonding.
+       Fix issue with device disconnects and offline mode handling.
+       Add support for setting adapter name based on machine-info.
+       Add support for systemd service configuration.
+
+ver 4.94:
+       Fix issue with invalid read of memory in various modules.
+       Fix issue with buffer overflow when sending AVDTP commands.
+       Fix issue with response to vendor dependent AVRCP commands.
+       Fix issue with headset when not able to reply with ERROR.
+       Fix issue with crash when creating a device from storage.
+       Fix issue with handling non UTF-8 devices names.
+       Add support for improved discovery procedure.
+
+ver 4.93:
+       Fix issue with property type and Health Main channel.
+       Fix issue with crash when removing devices.
+       Add support for hid2hci and udev integration.
+
+ver 4.92:
+       Fix issue with handling of A2DP suspend response.
+       Fix issue with crashing when acquiring A2DP stream.
+       Fix issue with missing check for valid SCO before shutdown.
+       Fix issue with waiting for POLLERR when disconnecting SCO.
+       Fix issue with disconnect after primary service discovery.
+       Fix issue with attribute interface registration.
+       Add support for primary services over BR/EDR.
+       Add support for flushable packets of A2DP media.
+
+ver 4.91:
+       Fix issue with LMP version string and hciconfig.
+       Fix issue with missing discovery signal when scanning.
+       Fix issue with wrong state and canceling name resolving.
+       Fix issue with missing check during adapter initialization.
+       Fix issue with missing protocol not supported error and A2DP.
+       Fix issue with crash during driver unregistering and A2DP.
+       Fix issue with crash when receiving AVDTP close command.
+       Fix issue with remote SEP handling when A2DP codec changes.
+       Fix issue with SCO hangup handling and state changes.
+       Fix issue with security level and MCAP instances.
+       Fix issue with memory leak and HDP data channels.
+       Add support for discover characteristics by UUID to gatttool.
+       Add initial support for Out-of-Band association model.
+       Add initial support for SIM Access Profile.
+
+ver 4.90:
+       Fix issue with setting of global mode property.
+       Fix issue with handling of RequestSession responses.
+       Fix issue with TP_BNEP_CTRL_BV_01_C qualification test.
+       Fix issue with too short AVDTP request timeout.
+       Add support for SIM Access Profile manager.
+       Add support for new UUID utility functions.
+       Add support for attribute server notifications.
+       Add support for client characteristic configuration.
+       Update support for interactive GATT utility.
+
+ver 4.89:
+       Fix issue with name resolving when discovery is suspended.
+       Fix issue with parsing flags of advertising report.
+       Fix issue with SEP handling if interface is disabled.
+       Fix issue with device object creation on disconnect event.
+       Fix issue with indicators whenever the driver is initialized.
+       Fix issue with call indicator when parsing call info reply.
+       Fix issue with crash and allowed GATT MTU was too large.
+       Add support for SDP record of Primary GATT services.
+       Add support for interactive mode for GATT utility.
+
+ver 4.88:
+       Fix issue with HID channel reference count handling.
+       Fix issue with daemon exit on badly formatted AT+VTS.
+       Fix issue with crash while parsing of endpoint properties.
+       Fix issue with possible crash on AVDTP Suspend request timeout.
+       Fix issue with stopping inquiry before adapter is initialized.
+       Fix issue with creating device object when connection fails.
+       Fix issue with sending HCIDEVUP when adapter is already up.
+       Fix issue with handling bonding IO channel closing.
+       Fix agent cancellation in security mode 3 situations.
+       Update pairing code to support management interface.
+
+ver 4.87:
+       Fix issue with initialization when adapter is already up.
+       Fix issue with attribute server MTU and incoming connections.
+       Fix issue with duplicate characteristics after discovery.
+
+ver 4.86:
+       Revert wrong fix for SDP PDU size error response.
+       Fix various memory leaks in A2DP and AVDTP support.
+       Add Routing property to MediaTransport interface
+       Add proper tracking mechanism to NREC status.
+       Add READ_BLOB_REQUEST support to attribute server.
+
+ver 4.85:
+       Fix issue with event mask setting for older adapters.
+       Fix issue with device creation and pairing failures.
+       Add support for telephony support via oFono.
+       Add support for characteristic security level.
+       Update support for service registration.
+
+ver 4.84:
+       Fix issue with wrong parameters and device found signals.
+       Fix issue with leaking EIR data if RSSI does not change.
+       Fix issue with adapter initialization state.
+       Fix issue with closing of SDP server sockets.
+
+ver 4.83:
+       Fix issue with already connected HFP/HSP endpoints.
+       Fix missing reply when create device is canceled.
+       Fix memory leak within the attribute server.
+       Fix memory leak with unused extended inquiry name.
+       Fix setting paired state when device->authr is false.
+       Fix clearing authentication request for renewed keys.
+       Add support for storing link keys in runtime memory.
+       Update support for primary service discovery.
+
+ver 4.82:
+       Fix crash with mmap of files with multiples of page size.
+       Fix HFP response and hold (AT+BTRH) command response.
+       Fix device creation error response when powered off.
+       Fix device removal when connecting/browsing fails.
+       Add initial attribute permission implementation.
+       Add AVDTP SRC stream send buffer size verification.
+       Add support for setting link policy based on features.
+
+ver 4.81:
+       Fix issue with telephony driver initialization.
+       Fix issue with adapter services list initialization.
+       Fix crash after simultaneous authentication requests.
+       Add support for primary service search on device creation.
+
+ver 4.80:
+       Fix legacy link key storing for some buggy adapters.
+       Fix invalid memory access when EIR field length is zero.
+       Fix adapter initialization to wait for kernel HCI commands.
+       Fix initialization of adapters which are already up.
+       Fix possible race condition when initializing adapters.
+       Fix possible crashes when attempting to connect AVDTP.
+       Fix not aborting sink stream configuration on disconnect.
+       Fix not indicating disconnected state when connecting to AVDTP.
+       Fix not dropping AVDTP session when canceling stream setup.
+       Fix AVDTP abort not being send when the state is idle.
+       Fix regression with Low Energy and interleave discovery.
+       Add a new configuration option to disable Low Energy support.
+       Add iwmmxt optimization for SBC for ARM PXA series CPUs.
+       Update support for GATT Primary Service Discovery.
+       Update MCAP and HDP support.
+
+ver 4.79:
+       Fix issue with adapter initialization race condition.
+       Update new Bluetooth Management interface support.
+
+ver 4.78:
+       Fix various issues with AVDTP timer handling.
+       Fix various issues with handling of mode changes.
+       Fix issue with audio disconnect watch in connecting state.
+       Fix issue with handling call waiting indicators in telephony.
+       Fix issue with handling UUID parameter and RegisterEndpoint.
+       Add initial support for Bluetooth Management interface.
+       Add support for Application property to HealthChannel.
+
+ver 4.77:
+       Fix issue with device name and accessing already freed memory.
+       Fix issue with handling CHLD=0 command for handsfree.
+       Fix issue with manager properties and no adapters.
+       Fix issue with properties and broken service records.
+       Fix issue with A2DP playback and sample rate changes.
+       Update MCAP and HDP support.
+
+ver 4.76:
+       Fix issue in telephony driver with hanging up held call.
+       Fix issue in telephony driver with notifications when on hold.
+       Fix issue with blocking on setconf confirmation callback.
+       Fix issue with not always signaling new streams as sinks.
+       Fix issue with errors in case of endpoint request timeout.
+       Fix issue with HFP/HSP microphone and speaker gain values.
+       Add source if the device attempt to configure local sink stream.
+       Add PSM option for GATT/ATT over BR/EDR on gatttool.
+       Add support for GATT/ATT Attribute Write Request.
+       Update MCAP and HDP support.
+
+ver 4.75:
+       Fix use of uninitialized variable on legacy pairing.
+       Fix mismatch of attribute protocol opcode.
+
+ver 4.74:
+       Fix regression for Legacy Pairing.
+       Fix wrong PSM value for attribute protocol.
+       Fix issue with RSSI field in advertising reports.
+       Add support for Add BR/EDR and LE interleaved discovery.
+       Add support for GATT write characteristic value option.
+       Add support for specifying download address for AR300x.
+
+ver 4.73:
+       Fix problem with EIR data when setting the name.
+       Fix reading local name from command complete event.
+       Fix registering local endpoints with disabled socket interface.
+       Add support for more HCI operations using ops infrastructure.
+       Add support for GATT characteristic hierarchy.
+       Add support for GATT indications.
+
+ver 4.72:
+       Fix memory leak while connecting BTIO channels.
+       Fix crash with GStreamer plugin if SBC is not supported.
+       Fix issue with GATT server stop sending notifications.
+       Fix issue with GATT and dealing with the minimum MTU size.
+       Fix issue with file descriptor leak in GATT client.
+       Add support for UUID 128-bit handling in attribute client.
+       Add support for encoders/decoders for MTU Exchange.
+       Add support for the MTU Exchange procedure to the server.
+       Add support for a per channel MTU to the ATT server.
+       Add support for Characteristic interface.
+       Add support for new Media API and framework.
+       Add initial support for HDP plugin.
+
+ver 4.71:
+       Fix compilation when SBC support in not enabled.
+       Fix crash with RequestSession and application disconnects.
+       Fix memory leak and possible crash when removing audio device.
+       Fix issue with closing stream of locked sep when reconfiguring.
+       Fix issue where discovery could interfere with bonding.
+       Fix issue with Connected status when PS3 BD remote connects.
+       Fix issue with lifetime of fake input devices.
+       Add support for compile time option of oui.txt path.
+       Add support for printing IEEE1284 device ID for CUPS.
+       Add plugin for setting adapter class via DMI.
+       Add more features for attribute protocol and profile.
+       Add initial support for MCAP.
+
+ver 4.70:
+       Fix incoming call indication handling when in WAITING state.
+       Fix various SDP related qualification test case issues.
+       Fix logic to write EIR when SDP records are changed.
+       Fix UTF-8 validity check for remote names in EIR.
+       Add support for UUID-128 extended inquiry response.
+       Add service UUIDs from EIR to the DeviceFound signal.
+       Add fast connectable feature for Handsfree profile.
+       Add HCI command and event definitions for AMP support.
+       Add firmware download support for Qualcommh devices.
+       Add host level support for Atheros AR300x device.
+       Add initial support of ATT and GATT for basic rate.
+
+ver 4.69:
+       Fix issue with calling g_option_context_free() twice.
+       Fix inconsistencies with initial LE commands and events.
+       Add support for telephony ClearLastNumber method.
+       Add support for network server interface.
+
+ver 4.68:
+       Fix initialization of adapters in RAW mode.
+       Fix signal strength for HFP in Maemo's telephony support.
+       Add support for following the radio state via Maemo's MCE.
+       Add initial set of LE commands and events definitions.
+       Add mode option for L2CAP sockets to the BtIO API.
+
+ver 4.67:
+       Fix issue with authentication reply when bonding already completed.
+       Fix issue with not canceling authentication when bonding fails.
+       Fix issue with changed combination keys and temporary storage.
+       Fix issue with sdp_get_supp_feat library function.
+       Fix issue with missing unblock on device removal.
+       Fix issue with not waiting for mode change completion.
+       Add ARMv6 optimized version of analysis filter for SBC encoder.
+
+ver 4.66:
+       Fix regression with full debug enabling via SIGUSR2.
+       Fix redundant speaker/microphone gains being sent.
+       Fix not emitting PropertyChanged for SpeakerGain/MicrophoneGain.
+       Fix issue with storage usage when a record is not found in memory.
+       Fix issue with DiscoverServices not retrieving any records.
+       Fix audio profile disconnection order to match whitepaper.
+       Fix auto-accept confirmation when local agent has NoInputNoOutput.
+       Fix remote just-works SSP when MITM protection is required.
+       Fix performing dedicated bonding without MITM requirement.
+       Add support for storing debug link keys in runtime memory.
+
+ver 4.65:
+       Fix issues with general bonding being default setting now.
+       Fix driver removal upon device removal.
+       Add new "Blocked" property to device objects.
+       Add hciconfig support for blacklisting.
+       Add support for dynamic debug feature.
+
+ver 4.64:
+       Fix invalid memory access in headset_get_nrec function.
+       Fix issue with disconnect event on higher protocol layers.
+       Fix issue with list parsing in sdp_set_supp_features function.
+       Fix device object reference counting for SDP browse requests.
+       Add missing memory checks whenever memory is allocated for SDP.
+       Add support for exporting local services via D-Bus.
+       Add more L2CAP Enhanced Retransmission test options.
+
+ver 4.63:
+       Fix avdtp_abort not canceling pending requests.
+       Fix stale connection when abort gets rejected.
+
+ver 4.62:
+       Fix accidental symbol breakage with inquiry transmit power.
+       Fix using invalid data from previous headset connection.
+       Fix double free on AVDTP Abort response.
+       Fix possible crash while verifying AVDTP version.
+       Fix missing inuse flag when AVDTP stream is configured.
+       Add support for Bluetooth controller types.
+
+ver 4.61:
+       Fix issues with Read Inquiry Response Transmit Power Level.
+       Fix possible invalid read when removing a temporary device.
+       Fix mode restoration when remember_powered is false.
+       Fix conference call releasing in telephony-maemo.
+       Fix segmentation fault with authorization during headset disconnects.
+       Add support for handling unanswered AVDTP request on disconnect.
+       Add support for handling Inquiry Response Transmit Power Level.
+       Add support for caching of remote host features.
+       Add preliminary voice dialing support for HSP.
+
+ver 4.60:
+       Fix voice mailbox number reading from SIM.
+       Fix some races with D-Bus mainloop integration.
+       Add helpers for D-Bus signal watches.
+
+ver 4.59:
+       Add values for Bluetooth 4.0 specification.
+       Add SDP functions for HDP support.
+       Add test scripts for input and audio.
+       Fix missing close on BtIO create_io function.
+       Fix sending incorrect AVDTP commands after timeout occurs.
+       Fix timer removal when device disconnects unexpectedly.
+       Fix Extended Inquiry Response record for Device ID.
+
+ver 4.58:
+       Fix crash when adapter agent exists during authentication.
+       Fix CK-20W quirks for play and pause events.
+
+ver 4.57:
+       Fix unloading of drivers for uninitialized adapters.
+       Fix debug message to use requested and not opened SEID.
+       Fix codec selection for GStreamer plugin.
+       Fix deleting of SDP records during service updates.
+       Fix deleting of SDP records when a device is removed.
+       Fix handling when the SDP record is modified on remote device.
+       Fix potential buffer overflow by using snprintf instead of sprintf.
+       Fix const declarations for some storage function parameters.
+
+ver 4.56:
+       Add missing values from Bluetooth 3.0 specification.
+       Add proper tracking of device paired status.
+       Fix tracking of devices without permanently stored link key.
+       Fix issue with link key removal after connection failures.
+       Fix legacy pairing information based on remote host features.
+       Fix off-by-one issue with AVDTP capability parsing.
+       Fix AVRCP, AVCTP, AVDTP, A2DP and HFP version numbers.
+       Fix agent canceling before calling agent_destroy.
+       Fix service record parsing with an empty UUID list.
+       Fix various SDP related memory leaks.
+
+ver 4.55:
+       Add support for POSIX capabilities dropping.
+       Add special quirk for the Nokia CK-20W car kit.
+       Fix error code handling for AVDTP SetConfiguration response.
+       Fix updating out of range list when RSSI hasn't changed.
+       Fix various memory leaks and unnecessary error checks.
+
+ver 4.54:
+       Add introspection interface to output of introspection calls.
+       Fix stream handling when media transport disconnects prematurely.
+       Fix command timeout handling when there's no stream.
+       Fix headset_suspend_stream behavior for invalid states
+       Fix issue with AVDTP ABORTING state transition.
+       Fix issue with AVDTP suspend while closing.
+
+ver 4.53:
+       Fix issue with telephony connection state notifications.
+       Fix AVDTP stream leak for invalid media transport config.
+       Fix audio connection authorization handling with timeouts.
+       Fix race condition in authorizing audio connections.
+       Fix device authorized setting for AVRCP-only connections.
+       Fix duplicate attempts from device to connect signal channel.
+
+ver 4.52:
+       Add AVCTP support to test utility.
+       Fix AVDTP Abort when transport closes before response.
+       Fix authorization when the audio profiles are slow to connect.
+       Fix potential AVDTP reference leaks.
+
+ver 4.51:
+       Add utility for basic AVDTP testing.
+       Add support for configuring L2CAP FCS option.
+       Fix discovery mode for CUPS 1.4.x and later.
+       Fix global state tracking of audio service.
+       Fix last issues with the new build system.
+
+ver 4.50:
+       Fix issue with missing manual pages in distribution.
+       Fix issue with the configuration and state directories.
+       Fix issue with creating include directory.
+       Fix dependencies of include file generation.
+
+ver 4.49:
+       Add simple test program for basic GAP testing.
+       Add support for confirmation requests to agent example.
+       Add support for full non-recursive build.
+       Add five millisecond delay for Simple Pairing auto-accept.
+       Fix Class of Device setting when InitiallyPowered=false.
+
+ver 4.48:
+       Add library function for comparing UUID values.
+       Add support for creating all plugins as builtins.
+       Add support for async handling of service class changes.
+       Add support for source interface to audio IPC.
+       Fix device name settings when device is off or down.
+       Fix issue with enabled SCO server when not necessary.
+       Fix missing D-Bus access policy for CUPS backend.
+       Fix discovery results of CUPS backend.
+       Fix initialization handling of Maemo telephony.
+
+ver 4.47:
+       Add support for RFKILL unblock handling.
+       Add support for serial proxy configurations.
+       Add support for caching service class updates.
+       Fix issues with updating SDP service records.
+       Fix usage of limited discoverable mode.
+       Remove deprecated methods and signals for AudioSource.
+
+ver 4.46:
+       Add support for A2DP sink role.
+       Fix clearing svc_cache before the adapter is up.
+       Fix various pointer after free usages.
+       Fix various memory leaks.
+
+ver 4.45:
+       Fix UDEV_DATADIR fallback if pkg-config fails.
+       Fix adapter cleanup and setup prototypes.
+       Fix double-free with out-of-range devices.
+       Fix inband ring setting to be per-headset.
+       Fix handling of Maemo CSD startup.
+
+ver 4.44:
+       Add some missing manual pages.
+       Fix missing number prefix when installing udev rules.
+       Fix program prefix used in Bluetooth udev rules.
+       Fix three-way calling indicator order.
+       Fix downgrade/upgrade of callheld indicator.
+       Fix +CIEV sending when indicator value changes.
+       Fix signal handling for Maemo telephony driver.
+       Fix parsing issues with messages from Maemo CSD.
+       Fix issue with duplicate active calls.
+
+ver 4.43:
+       Add support for udev based on-demand startup.
+       Fix verbose error reporting of CUPS backend.
+       Fix various string length issues.
+       Fix issues with Maemo telephony driver.
+       Fix another device setup and temporary flag issue.
+       Fix and update example agent implementation.
+
+ver 4.42:
+       Add TI WL1271 to Texas Instruments chip list.
+       Add special udev mode to bluetoothd.
+       Fix regression when there is no agent registered.
+       Fix error return when bonding socket hang up.
+       Fix SCO server socket for HFP handsfree role.
+       Fix shutdown on SCO socket before closing.
+       Fix shutdown on A2DP audio stream channel before closing.
+       Fix issue with asserting on AVDTP reference count bugs.
+       Fix authorization denied issue with certain headsets.
+       Fix AVRCP UNITINFO and SUBUNIT INFO responses.
+       Fix discovery cancel issues in case SDP discovery fails.
+
+ver 4.41:
+       Fix pairing even if the ACL gets dropped before successful SDP.
+       Fix regression which caused device to be removed after pairing.
+       Fix HSP record fetching when remote device doesn't support it.
+       Fix SDP discovery canceling when clearing hs->pending.
+       Fix headset never connecting on the first attempt.
+       Fix headset state tracking if bt_search_service() fails.
+       Fix maximum headset connection count check.
+       Fix AVDTP Discover timeout handling.
+       Fix also UI_SET_KEYBIT for the new pause and play key codes.
+
+ver 4.40:
+       Add telephony driver for oFono telephony stack.
+       Add support for Dell specific HID proxy switching.
+       Add support for running hid2hci from udev.
+       Add mapping for AVRCP Play and Pause to dedicated key codes.
+       Fix AVRCP keycodes to better match existing X keymap support.
+       Fix various quoting issues within telephony support.
+       Fix memory allocation issue when generating PDUs for SDP.
+       Fix race condition on device removal.
+       Fix non-cancelable issue with CreateDevice method.
+       Fix non-working CancelDiscovery method call.
+
+ver 4.39:
+       Add workaround for dealing with unknown inquiry complete.
+       Fix discovering when using software scheduler.
+       Fix wrong NoInputNoOutput IO capability string.
+       Fix race condition with agent during pairing.
+       Fix agent cancellation for security mode 3 acceptor failure.
+       Fix temporary flag removal when device creation fails.
+       Fix hciattach to use ppoll instead of poll.
+       Fix service class update when adapter is down.
+       Fix service classes race condition during startup.
+       Fix release of audio client before freeing the device.
+
+ver 4.38:
+       Add support for builtin plugins.
+       Add framework for adapter operations.
+       Add constants for Enhanced Retransmission modes.
+       Fix HCI socket leak in device_remove_bonding.
+       Fix various format string issues.
+       Fix crashes with various free functions.
+       Fix issues with Headset and A2DP drivers to load again.
+       Fix sending AVRCP button released passthrough messages
+       Fix bug which prevent input devices to work after restart.
+       Fix issue with interpretation of UUID-128 as channel.
+
+ver 4.37:
+       Add version value for Bluetooth 3.0 devices.
+       Add additional L2CAP extended feature mask bits.
+       Add support for loading plugins in priority order.
+       Add support for more detailed usage of disconnect watches.
+       Add support for AVRCP volume control.
+       Add saturated clipping of SBC decoder output to 16-bit.
+       Fix potentially infinite recursion of adapter_up.
+       Fix SCO handling in the case of an incoming call.
+       Fix input service to use confirm callback.
+       Fix cleanup of temporary device entries from storage.
+
+ver 4.36:
+       Add proper tracking of AVCTP connect attempts.
+       Add support to channel pattern in Serial interface.
+       Fix A2DP sink crash if removing device while connecting.
+       Fix error handling if HFP indicators aren't initialized.
+       Fix segfault while handling an incoming SCO connection.
+       Fix Serial.Disconnect to abort connection attempt.
+
+ver 4.35:
+       Add support for Handsfree profile headset role.
+       Add additional checks for open SEIDs from clients.
+       Fix device removal while audio IPC client is connected.
+       Fix device removal when an authorization request is pending.
+       Fix incoming AVDTP connect while authorization in progress.
+       Fix disconnection timers for audio support.
+       Fix various potential NULL pointer deferences.
+       Fix callheld indicator value for multiple calls.
+       Fix voice number type usage.
+       Fix GDBus watch handling.
+
+ver 4.34:
+       Add support for version checks of plugins.
+       Add support for class property on adapter interface.
+       Add support for second SDP attempt after connection reset.
+       Add support for more detailed audio states.
+       Add support for HFP+A2DP auto connection feature.
+       Add support for new and improved audio IPC.
+       Add program for testing audio IPC interface.
+       Fix various AVDTP qualification related issues.
+       Fix broken SDP AttributeIdList parsing.
+       Fix invalid memory access of SDP URL handling.
+       Fix local class of device race conditions.
+       Fix issue with periodic inquiry on startup.
+       Fix missing temporary devices in some situations.
+       Fix SBC alignment issue for encoding with four subbands.
+
+ver 4.33:
+       Add Paired property to the DeviceFound signals.
+       Add support for Headset profile 1.2 version.
+       Fix broken network configuration when IPv6 is disabled.
+       Fix network regression that caused disconnection.
+       Fix SDP truncation of strings with NULL values.
+       Fix service discovery handling of CUPS helper.
+
+ver 4.32:
+       Fix broken SDP record handling.
+       Fix SDP data buffer parsing.
+       Fix more SDP memory leaks.
+       Fix read scan enable calls.
+       Fix A2DP stream handling.
+
+ver 4.31:
+       Add support for new BtIO helper library.
+       Fix AVDTP session close issue.
+       Fix SDP memory leaks.
+       Fix various uninitialized memory issues.
+       Fix duplicate signal emissions.
+       Fix property changes request handling.
+       Fix class of device storage handling.
+
+ver 4.30:
+       Add CID field to L2CAP socket address structure.
+       Fix reset of authentication requirements after bonding.
+       Fix storing of link keys when using dedicated bonding.
+       Fix storing of pre-Bluetooth 2.1 link keys.
+       Fix resetting trust settings on every reboot.
+       Fix handling of local name changes.
+       Fix memory leaks in hciconfig and hcitool
+
+ver 4.29:
+       Use AVRCP version 1.0 for now.
+       Decrease AVDTP idle timeout to one second.
+       Delay AVRCP connection when remote device connects A2DP.
+       Add workaround for AVDTP stream setup with broken headsets.
+       Add missing three-way calling feature bit for Handsfree.
+       Fix handsfree callheld indicator updating.
+       Fix parsing of all AT commands within the buffer.
+       Fix authentication replies when disconnected.
+       Fix handling of debug combination keys.
+       Fix handling of changed combination keys.
+       Fix handling of link keys when using no bonding.
+       Fix handling of invalid/unknown authentication requirements.
+       Fix closing of L2CAP raw socket used for dedicated bonding.
+
+ver 4.28:
+       Add AVDTP signal fragmentation support.
+       Add more SBC performance optimizations.
+       Add more SBC audio quality improvements.
+       Use native byte order for audio plugins.
+       Set the adapter alias only after checking the EIR data.
+       Fix auto-disconnect issue with explicit A2DP connections.
+       Fix invalid memory access of ALSA plugin.
+       Fix compilation with -Wsign-compare.
+
+ver 4.27:
+       Add more SBC optimization (MMX and ARM NEON).
+       Add BT_SECURITY and BT_DEFER_SETUP definitions.
+       Add support for deferred connection setup.
+       Add support for fragmentation of data packets.
+       Add option to trigger dedicated bonding.
+       Follow MITM requirements from remote device.
+       Require MITM for dedicated bonding if capabilities allow it.
+       Fix IO capabilities for non-pairing and pairing cases.
+       Fix no-bonding connections in non-bondable mode.
+       Fix new pairing detection with SSP.
+       Fix bonding with pre-2.1 devices and newer kernels.
+       Fix LIAC setting while toggling Pairable property.
+       Fix device creation for incoming security mode 3 connects.
+       Fix crash within A2DP with bogus pointer.
+       Fix issue with sdp_copy_record() function.
+       Fix crash with extract_des() if sdp_uuid_extract() fails.
+
+ver 4.26:
+       Use of constant shift in SBC quantization code.
+       Add possibility to analyze 4 blocks at once in encoder.
+       Fix correct handling of frame sizes in the encoder.
+       Fix for big endian problems in SBC codec.
+       Fix audio client socket to always be non-blocking.
+       Update telephony support for Maemo.
+
+ver 4.25:
+       Fix receiving data over the audio control socket.
+       Fix subbands selection for joint-stereo in SBC encoder.
+       Add new SBC analysis filter function.
+
+ver 4.24:
+       Fix signal emissions when removing adapters.
+       Fix missing adapter signals on exit.
+       Add support for bringing adapters down on exit.
+       Add support for RememberPowered option.
+       Add support for verbose compiler warnings.
+       Add more options to SBC encoder.
+
+ver 4.23:
+       Update audio IPC for better codec handling.
+       Fix bitstream optimization for SBC encoder.
+       Fix length header values of IPC messages.
+       Fix multiple coding style violations.
+       Fix FindDevice to handle temporary devices.
+       Add configuration option for DeviceID.
+       Add support for InitiallyPowered option.
+       Add missing signals for manager properties.
+       Add telephony support for Maemo.
+
+ver 4.22:
+       Add deny statements to D-Bus access policy.
+       Add support for LegacyPairing property.
+       Add support for global properties.
+       Add more commands to telephony testing script.
+       Add sender checks for serial and network interfaces.
+       Remove deprecated methods and signals from input interface.
+       Remove deprecated methods and signals from network interface.
+       Remove OffMode option and always use device down.
+
+ver 4.21:
+       Fix adapter initialization logic.
+       Fix adapter setup and start security manager early.
+       Fix usage issue with first_init variable.
+
+ver 4.20:
+       Cleanup session handling.
+       Cleanup mode setting handling.
+       Fix issue with concurrent audio clients.
+       Fix issue with HFP/HSP suspending.
+       Fix AT result code syntax handling.
+       Add Handsfree support for AT+NREC.
+       Add PairableTimeout adapter property.
+
+ver 4.19:
+       Fix installation of manual pages for old daemons.
+       Fix D-Bus signal emmissions for CreateDevice.
+       Fix issues with UUID probing.
+       Fix +BSRF syntax issue.
+       Add Pairable adapter property.
+       Add sdp_copy_record() library function.
+
+ver 4.18:
+       Fix release before close issue with RFCOMM TTYs.
+       Fix Connected property on input interface.
+       Fix DeviceFound signals during initial name resolving.
+       Fix service discovery handling.
+       Fix duplicate UUID detection.
+       Fix SBC gain mismatch and decoding handling.
+       Add more options to SBC encoder and decoder.
+       Add special any adapter object for service interface.
+       Add variable prefix to adapter and device object paths.
+
+ver 4.17:
+       Fix SBC encoder not writing last frame.
+       Fix missing timer for A2DP suspend.
+       Add more supported devices to hid2hci utility.
+       Add additional functionality to Handsfree support.
+
+ver 4.16:
+       Fix wrong parameter usage of watch callbacks.
+       Fix parameters for callback upon path removal.
+       Fix unloading of adapter drivers.
+
+ver 4.15:
+       Fix various A2DP state machine issues.
+       Fix some issues with the Handsfree error reporting.
+       Fix format string warnings with recent GCC versions.
+       Remove dependency on GModule.
+
+ver 4.14:
+       Fix types of property arrays.
+       Fix potential crash with input devices.
+       Fix PS3 BD remote input event generation.
+       Allow dynamic adapter driver registration.
+       Update udev rules.
+
+ver 4.13:
+       Fix service discovery and UUID handling.
+       Fix bonding issues with Simple Pairing.
+       Fix file descriptor misuse of SCO connections.
+       Fix various memory leaks in the device handling.
+       Fix AVCTP disconnect handling.
+       Fix GStreamer modes for MP3 encoding.
+       Add operator selection to Handsfree support.
+
+ver 4.12:
+       Fix crash with missing icon value.
+       Fix error checks of HAL plugin.
+       Fix SCO server socket cleanup on exit.
+       Fix memory leaks from DBusPendingCall.
+       Fix handling of pending authorization requests.
+       Fix missing protocol UUIDs in record pattern.
+
+ver 4.11:
+       Change SCO server socket into a generic one.
+       Add test script for dummy telephony plugin.
+       Fix uninitialized reply of multiple GetProperties methods.
+
+ver 4.10:
+       Fix memory leaks with HAL messages.
+       Add more advanced handsfree features.
+       Add properties to audio, input and network interfaces.
+       Stop device discovery timer on device removal.
+
+ver 4.9:
+       Fix signals for Powered and Discoverable properties.
+       Fix handling of Alias and Icon properties.
+       Fix duplicate entries for service UUIDs.
+
+ver 4.8:
+       Fix retrieving of formfactor value.
+       Fix retrieving of local and remote extended features.
+       Fix potential NULL pointer dereference during pairing.
+       Fix crash with browsing due to a remotely initated pairing.
+
+ver 4.7:
+       Fix pairing and service discovery logic.
+       Fix crashes during suspend and resume.
+       Fix race condition within devdown mode.
+       Add RequestSession and ReleaseSession methods.
+       Add Powered and Discoverable properties.
+       Add Devices property and deprecate ListDevices.
+       Add workaround for a broken carkit from Nokia.
+
+ver 4.6:
+       Fix Device ID record handling.
+       Fix service browsing and storage.
+       Fix authentication and encryption for input devices.
+       Fix adapter name initialization.
+
+ver 4.5:
+       Fix initialization issue with new adapters.
+       Send HID authentication request without blocking.
+       Hide the verbose SDP debug behind SDP_DEBUG.
+       Add extra UUIDs for service discovery.
+       Add SCO server socket listener.
+       Add authorization support to service plugin.
+
+ver 4.4:
+       Add temporary fix for the CUPS compile issue.
+       Add service-api.txt to distribution.
+       Mention the variable prefix of an object path
+
+ver 4.3:
+       Add dummy driver for telephony support.
+       Add support for discovery sessions.
+       Add service plugin for external services.
+       Various cleanups.
+
+ver 4.2:
+       Avoid memory copies in A2DP write routine.
+       Fix broken logic with Simple Pairing check and old kernels.
+       Allow non-bondable and outgoing SDP without agent.
+       Only remove the bonding for non-temporary devices.
+       Cleanup various unnecessary includes.
+       Make more unexported functions static.
+       Add basic infrastructure for gtk-doc support.
+
+ver 4.1:
+       Add 30 seconds timeout to BNEP connection setup phase.
+       Avoid memory copies in A2DP write routine for ALSA.
+       Make sure to include compat/sdp.h in the distribution.
+
+ver 4.0:
+       Initial public release.
+
+ver 3.36:
+       Add init routines for TI BRF chips.
+       Add extra attributes to the serial port record.
+       Add example record for headset audio gateway record.
+       Use Handsfree version 0x0105 for the gateway role.
+       Fix SDP record registration with specific record handles.
+       Fix BCSP sent/receive handling.
+       Fix various includes for cross-compilation.
+       Allow link mode settings for outgoing connections.
+       Allow bonding during periodic inquiry.
+
+ver 3.35:
+       Add two additional company identifiers.
+       Add UUID-128 support for service discovery.
+       Fix usage of friendly names for service discovery.
+       Fix authorization when experiemental is disabled.
+       Fix uninitialized variable in passkey request handling.
+       Enable output of timestamps for l2test and rctest.
+
+ver 3.34:
+       Replace various SDP functions with safe versions.
+       Add additional length validation for incoming SDP packets.
+       Use safe function versions for SDP client handling.
+       Fix issue with RemoveDevice during discovery procedure.
+       Fix collect for non-persistent service records.
+
+ver 3.33:
+       Add functions for reading and writing the link policy settings.
+       Add definition for authentication requirements.
+       Add support for handling Simple Pairing.
+       Add Simple Pairing support to Agent interface.
+       Add ReleaseMode method to Adapter interface.
+       Add DiscoverServices method to Device interface.
+       Remove obsolete code and cleanup the repository.
+       Move over to use the libgdbus API.
+       Enable PIE by default if supported.
+
+ver 3.32:
+       Add OCF constants for synchronous flow control enabling.
+       Add support for switching HID proxy devices from Dell.
+       Add more Bluetooth client/server helper functions.
+       Add support for input service idle timeout option.
+       Fix BNEP reconnection handling.
+       Fix return value for snd_pcm_hw_params() calls.
+       Use upper-case addresses for object paths.
+       Remove HAL support helpers.
+       Remove inotify support.
+       Remove service daemon activation handling.
+       Remove uneeded D-Bus API extension.
+
+ver 3.31:
+       Create device object for all pairing cases.
+       Convert authorization to internal function calls.
+       Add initial support for Headset Audio Gateway role.
+       Add generic Bluetooth helper functions for GLib.
+       Fix endiannes handling of connection handles.
+       Don't optimize when debug is enabled.
+
+ver 3.30:
+       Convert audio service into a plugin.
+       Convert input service into a plugin.
+       Convert serial service into a plugin.
+       Convert network service into a plugin.
+       Emit old device signals when a property is changed.
+       Fix missing DiscoverDevices and CancelDiscovery methods.
+       Add another company identifier.
+       Add basic support for Bluetooth sessions.
+       Add avinfo utility for AVDTP/A2DP classification.
+       Remove build option for deprecated sdpd binary.
+
+ver 3.29:
+       Introduce new D-Bus based API.
+       Add more SBC optimizations.
+       Add support for PS3 remote devices.
+       Fix alignment trap in SDP server.
+       Fix memory leak in sdp_get_uuidseq_attr function.
+
+ver 3.28:
+       Add support for MCAP UUIDs.
+       Add support for role switch for audio service.
+       Add disconnect timer for audio service.
+       Add disconnect detection to ALSA plugin.
+       Add more SBC optimizations.
+       Fix alignment issue of SDP server.
+       Remove support for SDP parsing via expat.
+
+ver 3.27:
+       Update uinput.h with extra key definitions.
+       Add support for input connect/disconnect callbacks.
+       Add ifdefs around some baud rate definitions.
+       Add another company identifier.
+       Add proper HFP service level connection handling.
+       Add basic headset automatic disconnect support.
+       Add support for new SBC API.
+       Fix SBC decoder noise at high bitpools.
+       Use 32-bit multipliers for further SBC optimization.
+       Check for RFCOMM connection state in SCO connect callback.
+       Make use of parameters selected in ALSA plugin.
+
+ver 3.26:
+       Fix compilation issues with UCHAR_MAX, USHRT_MAX and UINT_MAX.
+       Improve handling of different audio transports.
+       Enable services by default and keep old daemons disabled.
+
+ver 3.25:
+       Add limited support for Handsfree profile.
+       Add limited support for MPEG12/MP3 codec.
+       Add basic support for UNITINFO and SUBUNITINFO.
+       Add more SBC optimizations.
+       Fix external service (un)registration.
+       Allow GetInfo and GetAddress to fail.
+
+ver 3.24:
+       Add definitions for MDP.
+       Add TCP connection support for serial proxy.
+       Add fix for Logitech HID proxy switching.
+       Add missing macros, MIN, MAX, ABS and CLAMP.
+       Add more SBC encoder optimizations.
+       Add initial mechanism to handle headset commands.
+       Fix connecting to handsfree profile headsets.
+       Use proper function for checking signal name.
+
+ver 3.23:
+       Fix remote name request handling bug.
+       Fix key search function to honor the mmap area size.
+       Fix Avahi integration of network service.
+       Add new plugin communication for audio service.
+       Enable basic AVRCP support by default.
+       More optimizations to the SBC library.
+       Create common error definitions.
+
+ver 3.22:
+       Add missing include file from audio service.
+       Add SBC conformance test utility.
+       Add basic uinput support for AVRCP.
+       Fix L2CAP socket leak in audio service.
+       Fix buffer usage in GStreamer plugin.
+       Fix remote name request event handling.
+
+ver 3.21:
+       Add constant for Bluetooth socket options level.
+       Add initial AVRCP support.
+       Add A2DP sink support to GStreamer plugin.
+       Fix interoperability with A2DP suspend.
+       Fix sign error in 8-subband encoder.
+       Fix handling of service classes length size.
+       Store Extended Inquiry Response data information.
+       Publish device id information through EIR.
+       Support higher baud rates for Ericcson based chips.
+
+ver 3.20:
+       Fix GStreamer plugin file type detection.
+       Fix potential infinite loop in inotify support.
+       Fix D-Bus signatures for dict handling.
+       Fix issues with service activation.
+       Fix SDP failure handling of audio service.
+       Fix various memory leaks in input service.
+       Add secure device creation method to input service.
+       Add service information methods to serial service.
+       Add config file support to network service.
+       Add scripting capability to network service.
+       Add special on-mode handling.
+       Add optimization for SBC encoder.
+       Add tweaks for D-Bus 1.1.x libraries.
+       Add support for inquiry transmit power level.
+
+ver 3.19:
+       Limit range of bitpool announced while in ACP side.
+       Use poll instead of usleep to wait for worker thread.
+       Use default event mask from the specification.
+       Add L2CAP mode constants.
+       Add HID proxy support for Logitech diNovo Edge dongle.
+       Add refresh option to re-request device names.
+       Show correct connection link type.
+
+ver 3.18:
+       Don't allocate memory for the Bluetooth base UUID.
+       Implement proper locking for headsets.
+       Fix various A2DP SEP locking issues.
+       Fix and cleanup audio stream handling.
+       Fix stream starting if suspend request is pending.
+       Fix A2DP and AVDTP endianess problems.
+       Add network timeout and retransmission support.
+       Add more detailed decoding of EIR elements.
+
+ver 3.17:
+       Fix supported commands bit calculation.
+       Fix crashes in audio and network services.
+       Check PAN source and destination roles.
+       Only export the needed symbols for the plugins.
+
+ver 3.16:
+       Update company identifier list.
+       Add support for headsets with SCO audio over HCI.
+       Add support for auto-create through ALSA plugin.
+       Add support for ALSA plugin parameters.
+       Add GStreamer plugin with SBC decoder and encoder.
+       Fix network service NAP, GN and PANU servers.
+       Set EIR information from SDP database.
+
+ver 3.15:
+       Add A2DP support to the audio service.
+       Add proxy support to the serial service.
+       Extract main service class for later use.
+       Set service classes value from SDP database.
+
+ver 3.14:
+       Add missing signals for the adapter interface.
+       Add definitions and functions for Simple Pairing.
+       Add basic commands for Simple Pairing.
+       Add correct Simple Pairing and EIR interaction.
+       Add missing properties for remote information.
+       Add EPoX endian quirk to the input service.
+       Fix HID descriptor import and storage functions.
+       Fix handling of adapters in raw mode.
+       Fix remote device listing methods.
+
+ver 3.13:
+       Fix some issues with the headset support.
+       Fix concurrent pending connection attempts.
+       Fix usage of devname instead of netdev.
+       Add identifier for Nokia SyncML records.
+       Add command for reading the CSR chip revision.
+       Add generic CSR radio test support.
+       Update HCI command table.
+
+ver 3.12:
+       Add missing HCI command text descriptions
+       Add missing HCI commands structures.
+       Add missing HCI event structures.
+       Add common bachk() function.
+       Add support for limited discovery mode.
+       Add support for setting of event mask.
+       Add GetRemoteServiceIdentifiers method.
+       Add skeleton for local D-Bus server.
+       Add headset gain control methods.
+       Fix various headset implementation issues.
+       Fix various serial port service issues.
+       Fix various input service issues.
+       Let CUPS plugin discover printers in range.
+       Improve the BCM2035 UART init routine.
+       Ignore connection events for non-ACL links.
+
+ver 3.11:
+       Update API documentation.
+       Minimize SDP root records and browse groups.
+       Use same decoder for text and URL strings.
+       Fix URL data size handling.
+       Fix SDP pattern extraction for XML.
+       Fix network connection persistent state.
+       Add network connection helper methods.
+       Add initial version of serial port support.
+       Add class of device tracking.
+
+ver 3.10.1:
+       Add option to disable installation of manual pages.
+       Fix input service encryption setup.
+       Fix serial service methods.
+       Fix network service connection handling.
+       Provide a simple init script.
+
+ver 3.10:
+       Add initial version of network service.
+       Add initial version of serial service.
+       Add initial version of input service.
+       Add initial version of audio service.
+       Add authorization framework.
+       Add integer based SBC library.
+       Add version code for Bluetooth 2.1 specification.
+       Add ESCO_LINK connection type constant.
+       Export sdp_uuid32_to_uuid128() function.
+
+ver 3.9:
+       Add RemoteDeviceDisconnectRequested signal.
+       Add updated service framework.
+       Add embedded GLib library.
+       Add support for using system GLib library.
+       Create internal SDP server library.
+
+ver 3.8:
+       Sort discovered devices list based on their RSSI.
+       Send DiscoverableTimeoutChanged signal.
+       Fix local and remote name validity checking.
+       Add ListRemoteDevices and ListRecentRemoteDevices methods.
+       Add basic integration of confirmation concept.
+       Add support for service record description via XML.
+       Add support for external commands to the RFCOMM utility.
+       Add experimental service and authorization API.
+       Add functions for registering binary records.
+
+ver 3.7:
+       Fix class of device handling.
+       Fix error replies with pairing and security mode 3.
+       Fix disconnect method for RFCOMM connections.
+       Add match pattern for service searches.
+       Add support for prioritized watches.
+       Add additional PDU length checks.
+       Fix CSRC value for partial responses.
+
+ver 3.6.1:
+       Fix IO channel race conditions.
+       Fix pairing issues on big endian systems.
+       Fix pairing issues with page timeout errors.
+       Fix pairing state for security mode 3 requests.
+       Switch to user as default security manager mode.
+
+ver 3.6:
+       Update D-Bus based RFCOMM interface support.
+       Use L2CAP raw sockets for HCI connection creation.
+       Add periodic discovery support to the D-Bus interface.
+       Add initial support for device names via EIR.
+       Add proper UTF-8 validation of device names.
+       Add support for the J-Three keyboard.
+       Fix issues with the asynchronous API for SDP.
+
+ver 3.5:
+       Fix and cleanup watch functionality.
+       Add support for periodic inquiry mode.
+       Add support for asynchronous SDP requests.
+       Add more request owner tracking.
+       Add asynchronous API for SDP.
+       Document pageto and discovto options.
+
+ver 3.4:
+       Improve error reporting for failed HCI commands.
+       Improve handling of CancelBonding.
+       Fixed bonding reply message when disconnected.
+       Fix UUID128 string lookup handling.
+       Fix malloc() versus bt_malloc() usage.
+
+ver 3.3:
+       Don't change inquiry mode for Bluetooth 1.1 adapters.
+       Add udev rules for Bluetooth serial PCMCIA cards.
+       Add Cancel and Release methods for passkey agents.
+       Add GetRemoteClass method.
+       Convert to using ppoll() and pselect().
+       Initialize allocated memory to zero.
+       Remove bcm203x firmware loader.
+       Remove kernel specific timeouts.
+       Add additional private data field for SDP sessions.
+       Add host controller to host flow control defines.
+       Add host number of completed packets defines.
+       Initialize various memory to zero before usage.
+
+ver 3.2:
+       Only check for the low-level D-Bus library.
+       Update possible device minor classes.
+       Fix timeout for pending reply.
+       Add more Inquiry with RSSI quirks.
+       Sleep only 100 msecs for device detection.
+       Don't send BondingCreated on link key renewal.
+       Allow storing of all UTF-8 remote device names.
+       Create storage filenames with a generic function.
+       Fix handling of SDP strings.
+       Add adapter type for SDIO cards.
+       Add features bit for link supervision timeout.
+
+ver 3.1:
+       Add missing placeholders for feature bits.
+       Fix handling of raw mode devices.
+       Fix busy loop in UUID extraction routine.
+       Remove inquiry mode setting.
+       Remove auth and encrypt settings.
+
+ver 3.0:
+       Implement the new BlueZ D-Bus API.
+       Fix broken behavior with EVT_CMD_STATUS.
+       Add features bit for pause encryption.
+       Add additional EIR error code.
+       Add more company identifiers.
+       Add another Phonebook Access identifier.
+       Update sniff subrating data structures.
+
+ver 2.25:
+       Use %jx instead of %llx for uint64_t and int64_t.
+       Allow null-terminated text strings.
+       Add UUID for N-Gage games.
+       Add UUID for Apple Macintosh Attributes.
+       Add Apple attributes and iSync records.
+       Add definitions for Apple Agent.
+       Add support for the Handsfree Audio Gateway service.
+       Add support for choosing a specific record handle.
+       Add support for dialup/telephone connections.
+       Add definitions for Apple Agent.
+       Add support for record handle on service registration.
+
+ver 2.24:
+       Fix display of SDP text and data strings.
+       Add support for device scan property.
+       Add support for additional access protocols.
+       Update the D-Bus policy configuration file.
+
+ver 2.23:
+       Update the new D-Bus interface.
+       Make dfutool ready for big endian architectures.
+       Add support for AVRCP specific service records.
+       Add support for writing complex BCCMD commands.
+       Add the new BCCMD interface utility.
+       Add MicroBCSP implementation from CSR.
+       Add constants and definitions for sniff subrating.
+       Add support for allocation of binary text elements.
+       Add HCI emulation tool.
+       Add fake HID support for old EPoX presenters.
+       Reject connections from unknown HID devices.
+       Fix service discovery deadlocks with Samsung D600 phones.
+
+ver 2.22:
+       Remove D-Bus 0.23 support.
+       Add initial version of the new D-Bus interface.
+       Add support for extended inquiry response commands.
+       Add support for the Logitech diNovo Media Desktop Laser.
+       Add compile time buffer checks (FORTIFY SOURCE).
+       Decode reserved LMP feature bits.
+       Fix errno overwrite problems.
+       Fix profile descriptor problem with Samsung phones.
+
+ver 2.21:
+       Move create_dirs() and create_file() into the textfile library.
+       Let textfile_put() also replace the last key value pair.
+       Fix memory leaks with textfile_get() usage.
+       Fix infinite loops and false positive matches.
+       Don't retrieve stored link keys for RAW devices.
+       Document the putkey and delkey commands.
+       Show supported commands also in clear text.
+       Support volatile changes of the BD_ADDR for CSR chips.
+       Add support for identification of supported commands.
+       Add missing OCF declarations for the security filter.
+       Add two new company identifiers.
+
+ver 2.20:
+       Add UUIDs for video distribution profile.
+       Add UUIDs for phonebook access profile.
+       Add attribute identifier for supported repositories.
+       Add definitions for extended inquiry response.
+       Add functions for extended inquiry response.
+       Add support for extended inquiry response.
+       Add support for HotSync service record.
+       Add support for ActiveSync service record.
+       Add ActiveSync networking support.
+       Fix D-Bus crashes with new API versions.
+
+ver 2.19:
+       Fix the GCC 4.0 warnings.
+       Fix the routing for dealing with raw devices.
+       Fix off by one memory allocation error.
+       Fix security problem with escape characters in device name.
+       Add per device service record functions.
+       Send D-Bus signals for inquiry results and remote name resolves.
+       Add support for device specific SDP records.
+
+ver 2.18:
+       Support D-Bus 0.23 and 0.33 API versions.
+       Support reading of complex BCCMD values.
+       Support minimum and maximum encryption key length.
+       Add support for reading and writing the inquiry scan type.
+       Add definitions for connection accept timeout and scan enable.
+       Add support for inquiry scan type.
+       Add tool for the CSR BCCMD interface.
+       Add first draft of the Audio/Video control utility.
+       Add disconnect timer support for the A2DP ALSA plugin.
+       Make SBC parameters configurable.
+       Replace non-printable characters in device names.
+       Remove hci_vhci.h header file.
+       Remove hci_uart.h header file.
+
+ver 2.17:
+       Set the storage directory through ${localstatedir}.
+       Add the textfile library for ASCII based file access.
+       Add support for return link keys event.
+       Add support for voice setting configuration.
+       Add support for page scan timeout configuration.
+       Add support for storing and deleting of stored link keys.
+       Add support for searching for services with UUID-128.
+       Add support for retrieving all possible service records.
+       Add support for a raw mode view of service records.
+       Add support for HID information caching in hidd.
+       Add support for authentication in pand and dund.
+       Add support for changing BD_ADDR of CSR chips.
+       Add pskey utility for changing CSR persistent storage values.
+       Add the firmware upgrade utility.
+       Add connection caching for the A2DP ALSA plugin.
+       Add functions for stored link keys.
+       Add definitions for PIN type and unit key.
+       Add SDP_WAIT_ON_CLOSE flag for sdp_connect().
+       Include stdio.h in bluetooth.h header file.
+       Include sys/socket.h in the header files.
+
+ver 2.16:
+       Store link keys in ASCII based file format.
+       Support device name caching.
+       Support zero length data sizes in l2test.
+       Change default l2ping data size to 44 bytes.
+       Hide the server record and the public browse group root.
+       Read BD_ADDR if not set and if it is a raw device.
+       Add SDP language attributes.
+       Add support for browsing the L2CAP group.
+       Add support for stored pin codes for outgoing connections.
+       Add support for local commands and extended features.
+       Add support for reading CSR panic and fault codes.
+       Add config option for setting the inquiry mode.
+       Add OUI decoding support.
+       Use unlimited inquiry responses as default.
+       Use cached device names for PIN request.
+       Use the clock offset when getting the remote names.
+       Add function for reading local supported commands.
+       Add function for reading local extended features.
+       Add function for reading remote extended features.
+       Add function for getting the remote name with a clock offset.
+       Add function for extracting the OUI from a BD_ADDR.
+       Add inquiry info structure with RSSI and page scan mode.
+       Fix buffer allocation for features to string conversion.
+       Support inquiry with unlimited number of responses.
+
+ver 2.15:
+       Enable the RFCOMM service level security.
+       Add deprecated functions for reading the name.
+       Add command for reading the clock offset.
+       Add command for reading the clock.
+       Add function for reading the clock.
+       Add function for reading the local Bluetooth address.
+       Add function for reading the local supported features.
+       Don't configure raw devices.
+       Don't set inquiry scan or page scan on raw devices.
+       Don't show extended information for raw devices.
+       Support L2CAP signal sizes bigger than 2048 bytes.
+       Cleanup of the socket handling code of the test programs.
+       Use better way for unaligned access.
+       Remove sdp_internal.h and its usage.
+
+ver 2.14:
+       Make use of additional connection information.
+       Use library function for reading the RSSI.
+       Use library function for reading the link quality.
+       Use library function for reading the transmit power level.
+       Use library functions for the link supervision timeout.
+       Add tool for changing the device address.
+       Add function for reading the RSSI.
+       Add function for reading the link quality.
+       Add function for reading the transmit power level.
+       Add functions for the link supervision timeout.
+       Remove deprecated functions.
+       Update AM_PATH_BLUEZ macro.
+
+ver 2.13:
+       Use file permission 0600 for the link key file.
+       Add support for HID attribute descriptions.
+       Add support for Device ID attributes.
+       Add Device ID and HID attribute definitions.
+       Update the UUID constants and its translations.
+       Update L2CAP socket option definitions.
+       Update connection information definitions.
+       Various whitespace cleanups.
+
+ver 2.12:
+       Inherit the device specific options from the default.
+       Use --device for selecting the source device.
+       Add --nosdp option for devices with resource limitation.
+       Add support and parameter option for secure mode.
+       Add a lot of build ids and hardware revisions.
+       Add service classes and profile ids for WAP.
+       Add simple AM_PATH_BLUEZ macro.
+       Update UUID translation tables.
+       Correct kernel interface for CMTP and HIDP support.
+
+ver 2.11:
+       Initial support for the kernel security manager.
+       Various cleanups to avoid inclusion of kernel headers.
+       Fix output when the CUPS backend is called without arguments.
+       Fix problems with a 64 bit userland.
+       Use Bluetooth library functions if available.
+       Use standard numbering scheme of SDP record handles.
+       Use bit zero for vendor packets in the filter type bitmask.
+       Add SIM Access types for service discovery.
+       Add more audio/video profile translations.
+       Add another company identifier.
+       Add the missing HCI error codes.
+       Add RFCOMM socket options.
+       Add definition for the SECURE link mode.
+       Add functions for reading and writing the inquiry mode.
+       Add functions for AFH related settings and information.
+       Add version identifier for the Bluetooth 2.0 specification.
+       Add a master option to the hidd.
+       Add support for changing the link key of a connection.
+       Add support for requesting encryption on keyboards.
+       Add support for revision information of Digianswer devices.
+       Add support for the Zoom, IBM and TDK PCMCIA cards.
+       Add checks for the OpenOBEX and the ALSA libraries.
+       Add experimental mRouter support.
+
+ver 2.10:
+       Use a define for the configuration directory.
+       Fix string initialization for flags translation.
+       Fix and extend the unaligned access macros.
+       Make compiling with debug information optional.
+       Don't override CFLAGS from configure.
+       Check for usb_get_busses() and usb_interrupt_read().
+       Add optional support for compiling with PIE.
+       Make installation of the init scripts optional.
+       Make compiling with debug information optional.
+       Don't override CFLAGS from configure.
+
+ver 2.9:
+       Retry SDP connect if busy in the CUPS backend.
+       Use packet type and allow role switch in hcitool.
+       Use the functions from the USB library for hid2hci.
+       Add Broadcom firmware loader.
+       Add EPoX endian quirk for buggy keyboards.
+       Add L2CAP info type and info result definitions.
+       Add value for L2CAP_CONF_RFC_MODE.
+       Change RSSI value to signed instead of unsigned.
+       Allow UUID32 values as protocol identifiers.
+       Update the autoconf/automake scripts.
+
+ver 2.8:
+       Use LIBS and LDADD instead of LDFLAGS.
+       Use HIDP subclass field for HID boot protocol.
+       Set olen before calling getsockopt() in pand.
+       Restore signals for dev-up script.
+       Add PID file support for pand.
+       Add size parameter to expand_name() in hcid.
+       Add support for audio source and audio sink SDP records.
+       Add support for HID virtual cable unplug.
+       Add support for AmbiCom BT2000C card.
+       Add defines and UUID's for audio/video profiles.
+       Add AVDTP protocol identifier.
+       Add HIDP subclass field.
+       Add PKGConfig support.
+       Fix the event code of inquiry with RSSI.
+       Remove dummy SDP library.
+
+ver 2.7:
+       Fix display of decoded LMP features.
+       Update company identifiers.
+       Add AFH related types.
+       Add first bits from EDR prototyping specification.
+       Add support for inquiry with RSSI.
+       Add HCRP related SDP functions.
+       Add HIDP header file.
+       Add support for getting the AFH channel map.
+       Add support for AFH mode.
+       Add support for inquiry mode.
+       Add Bluetooth backend for CUPS.
+       Add the hid2hci utility.
+       Add the hidd utility.
+       Add the pand utility.
+       Add the dund utility.
+       More endian bug fixes.
+       Give udev some time to create the RFCOMM device nodes.
+       Release the TTY if no device node is found.
+       New startup script for the Bluetooth subsystem.
+       Update to the autoconf stuff.
+
+ver 2.6:
+       Change default prefix to /usr.
+       Add manpages for hcid and hcid.conf.
+       Add the sdpd server daemon.
+       Add the sdptool utility.
+       Add the ciptool utility.
+       Add new company identifiers.
+       Add BNEP and CMTP header files.
+       Add the SDP library.
+       Use R2 for default value of pscan_rep_mode.
+
+ver 2.5:
+       Add decoding of Bluetooth 1.2 features.
+       Add link manager version parameter for Bluetooth 1.2.
+       Add new company identifiers.
+       Add D-Bus support for PIN request.
+       Support for transmit power level.
+       Support for park, sniff and hold mode.
+       Support for role switch.
+       Support for reading the clock offset.
+       Support for requesting authentication.
+       Support for setting connection encryption.
+       Show revision information for Broadcom devices.
+       Replace unprintable characters in device name.
+       Use R1 for default value of pscan_rep_mode.
+       Fix some 64-bit problems.
+       Fix some endian problems.
+       Report an error on PIN helper failure.
+       Update bluepin script for GTK2.
+
+ver 2.4:
+       Increase number of inquiry responses.
+       Support for transmit power level.
+       Display all 8 bytes of the features.
+       Add support for reading and writing of IAC.
+       Correct decoding class of device.
+       Use Ericsson revision command for ST Microelectronics devices.
+       Display AVM firmware version with 'revision' command.
+       New code for CSR specific revision information.
+       Support for ST Microelectronics specific initialization.
+       Support for 3Com card version 3.0.
+       Support for TDK, IBM and Socket cards.
+       Support for initial baud rate.
+       Update man pages.
+       Fixes for some memory leaks.
+
+ver 2.3:
+       Added const qualifiers to appropriate function arguments.
+       Minor fixes.
+       CSR firmware version is now displayed by 'revision' command.
+       Voice command is working properly on big endian machines.
+       Added support for Texas Bluetooth modules.
+       Added support for high UART baud rates on Ericsson modules.
+       BCSP initialization fixes.
+       Support for role switch command (hcitool).
+       RFCOMM config file parser fixes.
+       Update man pages.
+       Removed GLib dependency.
+
+ver 2.2:
+       Updated RFCOMM header file.
+       Additional HCI command and event defines.
+       Support for voice settings (hciconfig).
+       Minor hcitool fixes.
+       Improved configure script.
+       Added Headset testing tool.
+       Updated man pages.
+       RPM package.
+
+ver 2.1.1:
+       Resurrect hci_remote_name.
+
+ver 2.1:
+       Added hci_{read, write}_class_of_dev().
+       Added hci_{read, write}_current_iac_lap().
+       Added hci_write_local_name().
+       Added RFCOMM header file.
+       Minor fixes.
+       Improved BCSP initialization (hciattach).
+       Support for displaying link quality (hcitool).
+       Support for changing link supervision timeout (hcitool).
+       New RFCOMM TTY configuration tool (rfcomm).
+       Minor fixes and updates.
+
+ver 2.0:
+       Additional company IDs.
+       BCSP initialization (hciattach).
+       Minor hciconfig fixes.
+
+ver 2.0-pr13:
+       Support for multiple pairing modes.
+       Link key database handling fixes.
+
+ver 2.0-pre12:
+       Removed max link key limit. Keys never expire.
+       Link key database is always updated. Reread PIN on SIGHUP (hcid).
+       Bluetooth script starts SDPd, if installed.
+       Other minor fixes.
+
+ver 2.0-pre11:
+       Improved link key management and more verbose logging (hcid).
+       Fixed scan command (hcitool).
+
+ver 2.0-pre10:
+       Fix hci_inquiry function to return errors and accept user buffers.
+       New functions hci_devba, hci_devid, hci_for_each_dev and hci_get_route.
+       Additional company IDs.
+       Makefile and other minor fixes.
+       Support for reading RSSI, remote name and changing
+       connection type (hcitool). 
+       Device initialization fixes (hcid).
+       Other minor fixes and improvements.
+       Build environment cleanup and fixes.
+
+ver 2.0-pre9:
+       Improved bluepin. Working X authentication.
+       Improved hcitool. New flexible cmd syntax, additional commands.
+       Human readable display of the device features.
+       LMP features to string translation support.
+       Additional HCI command and event defines.
+       Extended hci_filter API.
+
+ver 2.0-pre8:
+       Additional HCI ioctls and defines.
+       All strings and buffers are allocated dynamically.
+       ba2str, str2ba automatically swap bdaddress.
+       Additional hciconfig commands. Support for ACL and SCO MTU ioctls.
+       Support for Inventel and COM1 UART based devices.
+       Minor hcitool fixes.
+       Improved l2test. New L2CAP test modes.
+       Minor fixes and cleanup.
+
+ver 2.0-pre7:
+       Bluetooth libraries and header files is now a separate package.
+       New build environment uses automake and libtool.
+       Massive header files cleanup.
+       Bluetooth utilities is now a separate package.
+       New build environment uses automake.
+       Moved all config files and security data to /etc/bluetooth.
+       Various cleanups.
+
+ver 2.0-pre6:
+       API cleanup and additions.
+       Improved hcitool.
+       l2test minor output fixes.
+       hciattach opt to display list of supported devices.
+
+ver 2.0-pre4:
+       HCI filter enhancements.
+
+ver 2.0-pre3:
+       Cleanup.
+
+ver 2.0-pre2:
+       Additional HCI library functions.
+       Improved CSR baud rate initialization.
+       PCMCIA scripts fixes and enhancements.
+       Documentation update.
+
+ver 2.0-pre1:
+       New UART initialization utility.
+       Hot plugging support for UART based PCMCIA devices.
+       SCO testing utility.
+       New authentication utility (bluepin).
+       Minor fixes and improvements.
diff --git a/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..1c214c6
--- /dev/null
@@ -0,0 +1,490 @@
+
+AM_MAKEFLAGS = --no-print-directory
+
+lib_LTLIBRARIES =
+
+noinst_LIBRARIES =
+
+noinst_LTLIBRARIES =
+
+bin_PROGRAMS =
+
+sbin_PROGRAMS =
+
+noinst_PROGRAMS =
+
+dist_man_MANS =
+
+dist_noinst_MANS =
+
+CLEANFILES =
+
+EXTRA_DIST =
+
+includedir = @includedir@/bluetooth
+
+include_HEADERS =
+
+AM_CFLAGS = $(WARNING_CFLAGS) $(MISC_CFLAGS)
+AM_LDFLAGS = $(MISC_LDFLAGS)
+
+if DATAFILES
+dbusdir = $(sysconfdir)/dbus-1/system.d
+dbusservicedir = $(datadir)/dbus-1/system-services
+
+dbus_DATA = src/bluetooth.conf
+dbusservice_DATA = src/org.bluez.service
+
+confdir = $(sysconfdir)/bluetooth
+
+conf_DATA =
+
+statedir = $(localstatedir)/lib/bluetooth
+
+state_DATA =
+
+if SYSTEMD
+systemdunitdir = @SYSTEMD_UNITDIR@
+
+systemdunit_DATA = src/bluetooth.service
+endif
+endif
+
+plugindir = $(libdir)/bluetooth/plugins
+
+if MAINTAINER_MODE
+build_plugindir = $(abs_top_srcdir)/plugins/.libs
+else
+build_plugindir = $(plugindir)
+endif
+
+
+plugin_LTLIBRARIES =
+
+
+lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h lib/mgmt.h \
+               lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h lib/uuid.h \
+               lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h lib/a2mp.h
+local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file)))
+
+BUILT_SOURCES = $(local_headers) src/builtin.h
+
+include_HEADERS += $(lib_headers)
+
+lib_LTLIBRARIES += lib/libbluetooth.la
+
+lib_libbluetooth_la_SOURCES = $(lib_headers) \
+                               lib/bluetooth.c lib/hci.c lib/sdp.c lib/uuid.c
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 16:0:13
+lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
+
+noinst_LTLIBRARIES += lib/libbluetooth-private.la
+
+lib_libbluetooth_private_la_SOURCES = $(lib_libbluetooth_la_SOURCES)
+
+if SBC
+noinst_LTLIBRARIES += sbc/libsbc.la
+
+sbc_libsbc_la_SOURCES = sbc/sbc.h sbc/sbc.c sbc/sbc_math.h sbc/sbc_tables.h \
+                       sbc/sbc_primitives.h sbc/sbc_primitives.c \
+                       sbc/sbc_primitives_mmx.h sbc/sbc_primitives_mmx.c \
+                       sbc/sbc_primitives_iwmmxt.h sbc/sbc_primitives_iwmmxt.c \
+                       sbc/sbc_primitives_neon.h sbc/sbc_primitives_neon.c \
+                       sbc/sbc_primitives_armv6.h sbc/sbc_primitives_armv6.c
+
+sbc_libsbc_la_CFLAGS = $(AM_CFLAGS) -finline-functions -fgcse-after-reload \
+                                       -funswitch-loops -funroll-loops
+
+noinst_PROGRAMS += sbc/sbcinfo sbc/sbcdec sbc/sbcenc
+
+sbc_sbcdec_SOURCES = sbc/sbcdec.c sbc/formats.h
+sbc_sbcdec_LDADD = sbc/libsbc.la
+
+sbc_sbcenc_SOURCES = sbc/sbcenc.c sbc/formats.h
+sbc_sbcenc_LDADD = sbc/libsbc.la
+
+if SNDFILE
+noinst_PROGRAMS += sbc/sbctester
+
+sbc_sbctester_LDADD = @SNDFILE_LIBS@ -lm
+sbc_sbctest_CFLAGS = $(AM_CFLAGS) @SNDFILE_CFLAGS@
+endif
+endif
+
+attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
+               attrib/gatt.h attrib/gatt.c \
+               attrib/gattrib.h attrib/gattrib.c attrib/client.h \
+               attrib/client.c attrib/gatt-service.h attrib/gatt-service.c
+
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+                                       gdbus/object.c gdbus/polkit.c
+
+btio_sources = btio/btio.h btio/btio.c
+
+builtin_modules =
+builtin_sources =
+builtin_nodist =
+mcap_sources =
+
+if MCAP
+mcap_sources += health/mcap_lib.h health/mcap_internal.h \
+               health/mcap.h health/mcap.c \
+               health/mcap_sync.c
+endif
+
+if PNATPLUGIN
+builtin_modules += pnat
+builtin_sources += plugins/pnat.c
+endif
+
+if AUDIOPLUGIN
+builtin_modules += audio
+builtin_sources += audio/main.c \
+                       audio/manager.h audio/manager.c \
+                       audio/gateway.h audio/gateway.c \
+                       audio/headset.h audio/headset.c \
+                       audio/control.h audio/control.c \
+                       audio/avctp.h audio/avctp.c \
+                       audio/avrcp.h audio/avrcp.c \
+                       audio/device.h audio/device.c \
+                       audio/source.h audio/source.c \
+                       audio/sink.h audio/sink.c \
+                       audio/a2dp.h audio/a2dp.c \
+                       audio/avdtp.h audio/avdtp.c \
+                       audio/ipc.h audio/ipc.c \
+                       audio/unix.h audio/unix.c \
+                       audio/media.h audio/media.c \
+                       audio/transport.h audio/transport.c \
+                       audio/telephony.h audio/a2dp-codecs.h
+builtin_nodist += audio/telephony.c
+
+noinst_LIBRARIES += audio/libtelephony.a
+
+audio_libtelephony_a_SOURCES = audio/telephony.h audio/telephony-dummy.c \
+                               audio/telephony-maemo5.c audio/telephony-ofono.c \
+                               audio/telephony-maemo6.c
+endif
+
+if SAPPLUGIN
+builtin_modules += sap
+builtin_sources += sap/main.c \
+                       sap/manager.h sap/manager.c \
+                       sap/server.h sap/server.c \
+                       sap/sap.h
+
+builtin_nodist += sap/sap.c
+
+noinst_LIBRARIES += sap/libsap.a
+
+sap_libsap_a_SOURCES = sap/sap.h sap/sap-dummy.c sap/sap-u8500.c
+endif
+
+if INPUTPLUGIN
+builtin_modules += input
+builtin_sources += input/main.c \
+                       input/manager.h input/manager.c \
+                       input/server.h input/server.c \
+                       input/device.h input/device.c \
+                       input/fakehid.c input/fakehid.h
+endif
+
+if SERIALPLUGIN
+builtin_modules += serial
+builtin_sources += serial/main.c \
+                       serial/manager.h serial/manager.c \
+                       serial/proxy.h serial/proxy.c \
+                       serial/port.h serial/port.c
+endif
+
+if NETWORKPLUGIN
+builtin_modules += network
+builtin_sources += network/main.c \
+                       network/manager.h network/manager.c \
+                       network/common.h network/common.c \
+                       network/server.h network/server.c \
+                       network/connection.h network/connection.c
+endif
+
+if SERVICEPLUGIN
+builtin_modules += service
+builtin_sources += plugins/service.c
+endif
+
+if HEALTHPLUGIN
+builtin_modules += health
+builtin_sources += health/hdp_main.c health/hdp_types.h \
+                       health/hdp_manager.h health/hdp_manager.c \
+                       health/hdp.h health/hdp.c \
+                       health/hdp_util.h health/hdp_util.c
+endif
+
+if GATTMODULES
+builtin_modules += thermometer alert time gatt_example proximity \
+                       deviceinfo
+builtin_sources += thermometer/main.c \
+                       thermometer/manager.h thermometer/manager.c \
+                       thermometer/thermometer.h thermometer/thermometer.c \
+                       alert/main.c alert/server.h alert/server.c \
+                       time/main.c time/server.h time/server.c \
+                       plugins/gatt-example.c \
+                       proximity/main.c proximity/manager.h proximity/manager.c \
+                       proximity/monitor.h proximity/monitor.c \
+                       proximity/reporter.h proximity/reporter.c \
+                       proximity/linkloss.h proximity/linkloss.c \
+                       proximity/immalert.h proximity/immalert.c \
+                       deviceinfo/main.c \
+                       deviceinfo/manager.h deviceinfo/manager.c \
+                       deviceinfo/deviceinfo.h deviceinfo/deviceinfo.c
+endif
+
+
+builtin_modules += hciops mgmtops
+builtin_sources += plugins/hciops.c plugins/mgmtops.c
+
+if HAL
+builtin_modules += hal
+builtin_sources += plugins/hal.c
+else
+builtin_modules += formfactor
+builtin_sources += plugins/formfactor.c
+endif
+
+EXTRA_DIST += plugins/hal.c plugins/formfactor.c
+
+builtin_modules += storage
+builtin_sources += plugins/storage.c
+
+builtin_modules += adaptername
+builtin_sources += plugins/adaptername.c
+
+if WIIMOTEPLUGIN
+builtin_modules += wiimote
+builtin_sources += plugins/wiimote.c
+endif
+
+if MAEMO6PLUGIN
+builtin_modules += maemo6
+builtin_sources += plugins/maemo6.c
+endif
+
+if DBUSOOBPLUGIN
+builtin_modules += dbusoob
+builtin_sources += plugins/dbusoob.c
+endif
+
+if MAINTAINER_MODE
+plugin_LTLIBRARIES += plugins/external-dummy.la
+plugins_external_dummy_la_SOURCES = plugins/external-dummy.c
+plugins_external_dummy_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+                                   -no-undefined
+plugins_external_dummy_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
+endif
+
+sbin_PROGRAMS += src/bluetoothd
+
+src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
+                       $(attrib_sources) $(btio_sources) \
+                       $(mcap_sources) src/bluetooth.ver \
+                       src/main.c src/log.h src/log.c \
+                       src/rfkill.c src/hcid.h src/sdpd.h \
+                       src/sdpd-server.c src/sdpd-request.c \
+                       src/sdpd-service.c src/sdpd-database.c \
+                       src/attrib-server.h src/attrib-server.c \
+                       src/sdp-xml.h src/sdp-xml.c \
+                       src/sdp-client.h src/sdp-client.c \
+                       src/textfile.h src/textfile.c \
+                       src/glib-helper.h src/glib-helper.c \
+                       src/oui.h src/oui.c src/uinput.h src/ppoll.h \
+                       src/plugin.h src/plugin.c \
+                       src/storage.h src/storage.c \
+                       src/agent.h src/agent.c \
+                       src/error.h src/error.c \
+                       src/manager.h src/manager.c \
+                       src/adapter.h src/adapter.c \
+                       src/device.h src/device.c src/attio.h \
+                       src/dbus-common.c src/dbus-common.h \
+                       src/event.h src/event.c \
+                       src/oob.h src/oob.c src/eir.h src/eir.c
+src_bluetoothd_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @DBUS_LIBS@ \
+                                                               -ldl -lrt
+src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
+                               -Wl,--version-script=$(srcdir)/src/bluetooth.ver
+
+src_bluetoothd_DEPENDENCIES = lib/libbluetooth-private.la
+
+src_bluetoothd_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_PLUGIN_BUILTIN \
+                                       -DPLUGINDIR=\""$(build_plugindir)"\"
+src_bluetoothd_SHORTNAME = bluetoothd
+
+builtin_files = src/builtin.h $(builtin_nodist)
+
+nodist_src_bluetoothd_SOURCES = $(builtin_files)
+
+CLEANFILES += $(builtin_files)
+
+man_MANS = src/bluetoothd.8
+
+if DATAFILES
+conf_DATA += src/main.conf
+endif
+
+EXTRA_DIST += src/genbuiltin src/bluetooth.conf src/org.bluez.service \
+                       src/main.conf network/network.conf \
+                       input/input.conf serial/serial.conf \
+                       audio/audio.conf audio/telephony-dummy.c \
+                       audio/telephony-maemo5.c audio/telephony-ofono.c \
+                       audio/telephony-maemo6.c sap/sap-dummy.c sap/sap-u8500.c \
+                       proximity/proximity.conf
+
+if ALSA
+alsadir = $(libdir)/alsa-lib
+
+alsa_LTLIBRARIES = audio/libasound_module_pcm_bluetooth.la \
+                               audio/libasound_module_ctl_bluetooth.la
+
+audio_libasound_module_pcm_bluetooth_la_SOURCES = audio/pcm_bluetooth.c \
+                                       audio/rtp.h audio/ipc.h audio/ipc.c
+audio_libasound_module_pcm_bluetooth_la_LDFLAGS = $(AM_LDFLAGS) -module \
+                                                 -avoid-version
+audio_libasound_module_pcm_bluetooth_la_LIBADD = sbc/libsbc.la \
+                                       lib/libbluetooth-private.la @ALSA_LIBS@
+audio_libasound_module_pcm_bluetooth_la_CFLAGS = $(AM_CFLAGS) @ALSA_CFLAGS@
+
+audio_libasound_module_ctl_bluetooth_la_SOURCES = audio/ctl_bluetooth.c \
+                                       audio/rtp.h audio/ipc.h audio/ipc.c
+audio_libasound_module_ctl_bluetooth_la_LDFLAGS = $(AM_LDFLAGS) -module \
+                                                 -avoid-version
+audio_libasound_module_ctl_bluetooth_la_LIBADD = \
+                                       lib/libbluetooth-private.la @ALSA_LIBS@
+audio_libasound_module_ctl_bluetooth_la_CFLAGS = $(AM_CFLAGS) @ALSA_CFLAGS@
+
+if DATAFILES
+alsaconfdir = $(datadir)/alsa
+
+alsaconf_DATA = audio/bluetooth.conf
+endif
+endif
+
+if AUDIOPLUGIN
+if GSTREAMER
+gstreamerdir = $(libdir)/gstreamer-0.10
+
+gstreamer_LTLIBRARIES = audio/libgstbluetooth.la
+
+audio_libgstbluetooth_la_SOURCES = audio/gstbluetooth.c audio/gstpragma.h \
+                               audio/gstsbcenc.h audio/gstsbcenc.c \
+                               audio/gstsbcdec.h audio/gstsbcdec.c \
+                               audio/gstsbcparse.h audio/gstsbcparse.c \
+                               audio/gstavdtpsink.h audio/gstavdtpsink.c \
+                               audio/gsta2dpsink.h audio/gsta2dpsink.c \
+                               audio/gstsbcutil.h audio/gstsbcutil.c \
+                               audio/gstrtpsbcpay.h audio/gstrtpsbcpay.c \
+                               audio/rtp.h audio/ipc.h audio/ipc.c
+audio_libgstbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version
+audio_libgstbluetooth_la_LIBADD = sbc/libsbc.la lib/libbluetooth-private.la \
+                                               @DBUS_LIBS@ @GSTREAMER_LIBS@ \
+                                               -lgstaudio-0.10 -lgstrtp-0.10
+audio_libgstbluetooth_la_CFLAGS = -fvisibility=hidden -fno-strict-aliasing \
+                               $(AM_CFLAGS) @DBUS_CFLAGS@ @GSTREAMER_CFLAGS@
+endif
+endif
+
+EXTRA_DIST += audio/bluetooth.conf
+
+
+include Makefile.tools
+
+if DATAFILES
+rulesdir = @UDEV_DIR@/rules.d
+
+udev_files =
+
+if HID2HCI
+udev_files += scripts/bluetooth-hid2hci.rules
+endif
+
+if PCMCIA
+udevdir = @UDEV_DIR@
+
+udev_files += scripts/bluetooth-serial.rules
+
+dist_udev_SCRIPTS = scripts/bluetooth_serial
+endif
+
+rules_DATA = $(foreach file,$(udev_files), scripts/97-$(notdir $(file)))
+endif
+
+CLEANFILES += $(rules_DATA)
+
+EXTRA_DIST += scripts/bluetooth-hid2hci.rules scripts/bluetooth-serial.rules
+
+EXTRA_DIST += doc/manager-api.txt \
+               doc/adapter-api.txt doc/device-api.txt \
+               doc/service-api.txt doc/agent-api.txt doc/attribute-api.txt \
+               doc/serial-api.txt doc/network-api.txt \
+               doc/input-api.txt doc/audio-api.txt doc/control-api.txt \
+               doc/hfp-api.txt doc/health-api.txt doc/sap-api.txt \
+               doc/media-api.txt doc/assigned-numbers.txt
+
+AM_YFLAGS = -d
+
+AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
+
+INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
+                       -I$(srcdir)/audio -I$(srcdir)/sbc -I$(srcdir)/gdbus \
+                       -I$(srcdir)/attrib -I$(srcdir)/btio -I$(srcdir)/tools \
+                       -I$(builddir)/tools -I$(srcdir)/monitor
+
+if MCAP
+INCLUDES += -I$(builddir)/health
+endif
+
+unit_objects =
+
+if TEST
+unit_tests = unit/test-eir
+
+noinst_PROGRAMS += $(unit_tests)
+
+unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/glib-helper.c
+unit_test_eir_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @CHECK_LIBS@
+unit_test_eir_CFLAGS = $(AM_CFLAGS) @CHECK_CFLAGS@
+unit_objects += $(unit_test_eir_OBJECTS)
+else
+unit_tests =
+endif
+
+TESTS = $(unit_tests)
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+pkgconfig_DATA = bluez.pc
+
+DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles
+
+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
+
+src/builtin.h: src/genbuiltin $(builtin_sources)
+       $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+audio/telephony.c: audio/@TELEPHONY_DRIVER@
+       $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
+
+sap/sap.c: sap/@SAP_DRIVER@
+       $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+
+scripts/%.rules:
+       $(AM_V_GEN)cp $(subst 97-,,$@) $@
+
+$(lib_libbluetooth_la_OBJECTS): $(local_headers)
+
+lib/bluetooth/%.h: lib/%.h
+       $(AM_V_at)$(MKDIR_P) lib/bluetooth
+       $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
+
+clean-local:
+       $(RM) -r lib/bluetooth
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..358af59
--- /dev/null
@@ -0,0 +1,5657 @@
+# Makefile.in generated by automake 1.11.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+# Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
+       $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \
+       $(am__EXEEXT_7)
+sbin_PROGRAMS = src/bluetoothd$(EXEEXT) $(am__EXEEXT_14) \
+       $(am__EXEEXT_15) $(am__EXEEXT_16)
+noinst_PROGRAMS = $(am__EXEEXT_8) $(am__EXEEXT_9) $(am__EXEEXT_10) \
+       $(am__EXEEXT_11) $(am__EXEEXT_13)
+@SBC_TRUE@am__append_1 = sbc/libsbc.la
+@SBC_TRUE@am__append_2 = sbc/sbcinfo sbc/sbcdec sbc/sbcenc
+@SBC_TRUE@@SNDFILE_TRUE@am__append_3 = sbc/sbctester
+@MCAP_TRUE@am__append_4 = health/mcap_lib.h health/mcap_internal.h \
+@MCAP_TRUE@            health/mcap.h health/mcap.c \
+@MCAP_TRUE@            health/mcap_sync.c
+
+@PNATPLUGIN_TRUE@am__append_5 = pnat
+@PNATPLUGIN_TRUE@am__append_6 = plugins/pnat.c
+@AUDIOPLUGIN_TRUE@am__append_7 = audio
+@AUDIOPLUGIN_TRUE@am__append_8 = audio/main.c \
+@AUDIOPLUGIN_TRUE@                     audio/manager.h audio/manager.c \
+@AUDIOPLUGIN_TRUE@                     audio/gateway.h audio/gateway.c \
+@AUDIOPLUGIN_TRUE@                     audio/headset.h audio/headset.c \
+@AUDIOPLUGIN_TRUE@                     audio/control.h audio/control.c \
+@AUDIOPLUGIN_TRUE@                     audio/avctp.h audio/avctp.c \
+@AUDIOPLUGIN_TRUE@                     audio/avrcp.h audio/avrcp.c \
+@AUDIOPLUGIN_TRUE@                     audio/device.h audio/device.c \
+@AUDIOPLUGIN_TRUE@                     audio/source.h audio/source.c \
+@AUDIOPLUGIN_TRUE@                     audio/sink.h audio/sink.c \
+@AUDIOPLUGIN_TRUE@                     audio/a2dp.h audio/a2dp.c \
+@AUDIOPLUGIN_TRUE@                     audio/avdtp.h audio/avdtp.c \
+@AUDIOPLUGIN_TRUE@                     audio/ipc.h audio/ipc.c \
+@AUDIOPLUGIN_TRUE@                     audio/unix.h audio/unix.c \
+@AUDIOPLUGIN_TRUE@                     audio/media.h audio/media.c \
+@AUDIOPLUGIN_TRUE@                     audio/transport.h audio/transport.c \
+@AUDIOPLUGIN_TRUE@                     audio/telephony.h audio/a2dp-codecs.h
+
+@AUDIOPLUGIN_TRUE@am__append_9 = audio/telephony.c
+@AUDIOPLUGIN_TRUE@am__append_10 = audio/libtelephony.a
+@SAPPLUGIN_TRUE@am__append_11 = sap
+@SAPPLUGIN_TRUE@am__append_12 = sap/main.c \
+@SAPPLUGIN_TRUE@                       sap/manager.h sap/manager.c \
+@SAPPLUGIN_TRUE@                       sap/server.h sap/server.c \
+@SAPPLUGIN_TRUE@                       sap/sap.h
+
+@SAPPLUGIN_TRUE@am__append_13 = sap/sap.c
+@SAPPLUGIN_TRUE@am__append_14 = sap/libsap.a
+@INPUTPLUGIN_TRUE@am__append_15 = input
+@INPUTPLUGIN_TRUE@am__append_16 = input/main.c \
+@INPUTPLUGIN_TRUE@                     input/manager.h input/manager.c \
+@INPUTPLUGIN_TRUE@                     input/server.h input/server.c \
+@INPUTPLUGIN_TRUE@                     input/device.h input/device.c \
+@INPUTPLUGIN_TRUE@                     input/fakehid.c input/fakehid.h
+
+@SERIALPLUGIN_TRUE@am__append_17 = serial
+@SERIALPLUGIN_TRUE@am__append_18 = serial/main.c \
+@SERIALPLUGIN_TRUE@                    serial/manager.h serial/manager.c \
+@SERIALPLUGIN_TRUE@                    serial/proxy.h serial/proxy.c \
+@SERIALPLUGIN_TRUE@                    serial/port.h serial/port.c
+
+@NETWORKPLUGIN_TRUE@am__append_19 = network
+@NETWORKPLUGIN_TRUE@am__append_20 = network/main.c \
+@NETWORKPLUGIN_TRUE@                   network/manager.h network/manager.c \
+@NETWORKPLUGIN_TRUE@                   network/common.h network/common.c \
+@NETWORKPLUGIN_TRUE@                   network/server.h network/server.c \
+@NETWORKPLUGIN_TRUE@                   network/connection.h network/connection.c
+
+@SERVICEPLUGIN_TRUE@am__append_21 = service
+@SERVICEPLUGIN_TRUE@am__append_22 = plugins/service.c
+@HEALTHPLUGIN_TRUE@am__append_23 = health
+@HEALTHPLUGIN_TRUE@am__append_24 = health/hdp_main.c health/hdp_types.h \
+@HEALTHPLUGIN_TRUE@                    health/hdp_manager.h health/hdp_manager.c \
+@HEALTHPLUGIN_TRUE@                    health/hdp.h health/hdp.c \
+@HEALTHPLUGIN_TRUE@                    health/hdp_util.h health/hdp_util.c
+
+@GATTMODULES_TRUE@am__append_25 = thermometer alert time gatt_example proximity \
+@GATTMODULES_TRUE@                     deviceinfo
+
+@GATTMODULES_TRUE@am__append_26 = thermometer/main.c \
+@GATTMODULES_TRUE@                     thermometer/manager.h thermometer/manager.c \
+@GATTMODULES_TRUE@                     thermometer/thermometer.h thermometer/thermometer.c \
+@GATTMODULES_TRUE@                     alert/main.c alert/server.h alert/server.c \
+@GATTMODULES_TRUE@                     time/main.c time/server.h time/server.c \
+@GATTMODULES_TRUE@                     plugins/gatt-example.c \
+@GATTMODULES_TRUE@                     proximity/main.c proximity/manager.h proximity/manager.c \
+@GATTMODULES_TRUE@                     proximity/monitor.h proximity/monitor.c \
+@GATTMODULES_TRUE@                     proximity/reporter.h proximity/reporter.c \
+@GATTMODULES_TRUE@                     proximity/linkloss.h proximity/linkloss.c \
+@GATTMODULES_TRUE@                     proximity/immalert.h proximity/immalert.c \
+@GATTMODULES_TRUE@                     deviceinfo/main.c \
+@GATTMODULES_TRUE@                     deviceinfo/manager.h deviceinfo/manager.c \
+@GATTMODULES_TRUE@                     deviceinfo/deviceinfo.h deviceinfo/deviceinfo.c
+
+@HAL_TRUE@am__append_27 = hal
+@HAL_TRUE@am__append_28 = plugins/hal.c
+@HAL_FALSE@am__append_29 = formfactor
+@HAL_FALSE@am__append_30 = plugins/formfactor.c
+@WIIMOTEPLUGIN_TRUE@am__append_31 = wiimote
+@WIIMOTEPLUGIN_TRUE@am__append_32 = plugins/wiimote.c
+@MAEMO6PLUGIN_TRUE@am__append_33 = maemo6
+@MAEMO6PLUGIN_TRUE@am__append_34 = plugins/maemo6.c
+@DBUSOOBPLUGIN_TRUE@am__append_35 = dbusoob
+@DBUSOOBPLUGIN_TRUE@am__append_36 = plugins/dbusoob.c
+@MAINTAINER_MODE_TRUE@am__append_37 = plugins/external-dummy.la
+DIST_COMMON = README $(am__configure_deps) \
+       $(am__dist_udev_SCRIPTS_DIST) $(dist_man_MANS) \
+       $(include_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+       $(srcdir)/Makefile.tools $(srcdir)/bluez.pc.in \
+       $(srcdir)/config.h.in $(top_srcdir)/configure \
+       $(top_srcdir)/doc/version.xml.in \
+       $(top_srcdir)/src/bluetooth.service.in \
+       $(top_srcdir)/src/bluetoothd.8.in AUTHORS COPYING COPYING.LIB \
+       ChangeLog INSTALL NEWS TODO compile config.guess config.sub \
+       depcomp install-sh ltmain.sh missing tools/lexer.c \
+       tools/parser.c tools/parser.h ylwrap
+@DATAFILES_TRUE@@TOOLS_TRUE@am__append_38 = tools/rfcomm.conf
+@TOOLS_TRUE@am__append_39 = tools/rfcomm tools/l2ping \
+@TOOLS_TRUE@                           tools/hcitool tools/sdptool tools/ciptool
+
+@TOOLS_TRUE@am__append_40 = tools/hciattach tools/hciconfig
+@TOOLS_TRUE@am__append_41 = tools/avinfo tools/ppporc \
+@TOOLS_TRUE@   tools/hcieventmask tools/hcisecfilter mgmt/btmgmt \
+@TOOLS_TRUE@   monitor/btmon emulator/btvirt
+@READLINE_TRUE@@TOOLS_TRUE@am__append_42 = attrib/gatttool
+@TOOLS_TRUE@am__append_43 = tools/rfcomm.1 tools/l2ping.8 \
+@TOOLS_TRUE@                   tools/hciattach.8 tools/hciconfig.8 \
+@TOOLS_TRUE@                   tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
+
+@TOOLS_FALSE@am__append_44 = tools/rfcomm.1 tools/l2ping.8 \
+@TOOLS_FALSE@                  tools/hciattach.8 tools/hciconfig.8 \
+@TOOLS_FALSE@                  tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
+
+@BCCMD_TRUE@am__append_45 = tools/bccmd
+@BCCMD_TRUE@@USB_TRUE@am__append_46 = tools/csr_usb.c
+@BCCMD_TRUE@@USB_TRUE@am__append_47 = @USB_LIBS@
+@BCCMD_TRUE@am__append_48 = tools/bccmd.8
+@BCCMD_FALSE@am__append_49 = tools/bccmd.8
+@HID2HCI_TRUE@udev_PROGRAMS = tools/hid2hci$(EXEEXT)
+@HID2HCI_TRUE@am__append_50 = tools/hid2hci.8
+@HID2HCI_FALSE@am__append_51 = tools/hid2hci.8
+@DFUTOOL_TRUE@am__append_52 = tools/dfutool
+@DFUTOOL_TRUE@am__append_53 = tools/dfutool.1
+@DFUTOOL_FALSE@am__append_54 = tools/dfutool.1
+@USB_TRUE@am__append_55 = tools/dfubabel tools/avctrl
+@CUPS_TRUE@cups_PROGRAMS = cups/bluetooth$(EXEEXT)
+@TEST_TRUE@am__append_56 = test/hciemu
+@TEST_TRUE@am__append_57 = test/l2test test/rctest
+@TEST_TRUE@am__append_58 = test/gaptest test/sdptest test/scotest \
+@TEST_TRUE@    test/attest test/hstest test/avtest test/ipctest \
+@TEST_TRUE@    test/lmptest test/bdaddr test/agent test/btiotest \
+@TEST_TRUE@    test/test-textfile test/uuidtest test/mpris-player \
+@TEST_TRUE@    $(unit_tests)
+@TEST_TRUE@am__append_59 = test/rctest.1 test/hciemu.1
+@TEST_TRUE@am__append_60 = test/bdaddr.8
+@TEST_FALSE@am__append_61 = test/rctest.1 test/hciemu.1 test/bdaddr.8
+@HIDD_TRUE@am__append_62 = compat/hidd
+@HIDD_TRUE@am__append_63 = compat/hidd.1
+@HIDD_FALSE@am__append_64 = compat/hidd.1
+@PAND_TRUE@am__append_65 = compat/pand
+@PAND_TRUE@am__append_66 = compat/pand.1
+@PAND_FALSE@am__append_67 = compat/pand.1
+@DUND_TRUE@am__append_68 = compat/dund
+@DUND_TRUE@am__append_69 = compat/dund.1
+@DUND_FALSE@am__append_70 = compat/dund.1
+@DATAFILES_TRUE@@HID2HCI_TRUE@am__append_71 = scripts/bluetooth-hid2hci.rules
+@DATAFILES_TRUE@@PCMCIA_TRUE@am__append_72 = scripts/bluetooth-serial.rules
+@MCAP_TRUE@am__append_73 = -I$(builddir)/health
+@TEST_TRUE@am__append_74 = $(unit_test_eir_OBJECTS)
+TESTS = $(am__EXEEXT_12)
+subdir = .
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+       $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES = doc/version.xml src/bluetoothd.8 \
+       src/bluetooth.service bluez.pc
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo "  AR    " $@;
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+audio_libtelephony_a_AR = $(AR) $(ARFLAGS)
+audio_libtelephony_a_LIBADD =
+am__audio_libtelephony_a_SOURCES_DIST = audio/telephony.h \
+       audio/telephony-dummy.c audio/telephony-maemo5.c \
+       audio/telephony-ofono.c audio/telephony-maemo6.c
+am__dirstamp = $(am__leading_dot)dirstamp
+@AUDIOPLUGIN_TRUE@am_audio_libtelephony_a_OBJECTS =  \
+@AUDIOPLUGIN_TRUE@     audio/telephony-dummy.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/telephony-maemo5.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/telephony-ofono.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/telephony-maemo6.$(OBJEXT)
+audio_libtelephony_a_OBJECTS = $(am_audio_libtelephony_a_OBJECTS)
+sap_libsap_a_AR = $(AR) $(ARFLAGS)
+sap_libsap_a_LIBADD =
+am__sap_libsap_a_SOURCES_DIST = sap/sap.h sap/sap-dummy.c \
+       sap/sap-u8500.c
+@SAPPLUGIN_TRUE@am_sap_libsap_a_OBJECTS = sap/sap-dummy.$(OBJEXT) \
+@SAPPLUGIN_TRUE@       sap/sap-u8500.$(OBJEXT)
+sap_libsap_a_OBJECTS = $(am_sap_libsap_a_OBJECTS)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(alsadir)" "$(DESTDIR)$(gstreamerdir)" \
+       "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" \
+       "$(DESTDIR)$(bindir)" "$(DESTDIR)$(cupsdir)" \
+       "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(udevdir)" \
+       "$(DESTDIR)$(udevdir)" "$(DESTDIR)$(man1dir)" \
+       "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(alsaconfdir)" \
+       "$(DESTDIR)$(confdir)" "$(DESTDIR)$(dbusdir)" \
+       "$(DESTDIR)$(dbusservicedir)" "$(DESTDIR)$(pkgconfigdir)" \
+       "$(DESTDIR)$(rulesdir)" "$(DESTDIR)$(statedir)" \
+       "$(DESTDIR)$(systemdunitdir)" "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(alsa_LTLIBRARIES) $(gstreamer_LTLIBRARIES) \
+       $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) $(plugin_LTLIBRARIES)
+@ALSA_TRUE@audio_libasound_module_ctl_bluetooth_la_DEPENDENCIES =  \
+@ALSA_TRUE@    lib/libbluetooth-private.la
+am__audio_libasound_module_ctl_bluetooth_la_SOURCES_DIST =  \
+       audio/ctl_bluetooth.c audio/rtp.h audio/ipc.h audio/ipc.c
+@ALSA_TRUE@am_audio_libasound_module_ctl_bluetooth_la_OBJECTS = audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo \
+@ALSA_TRUE@    audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo
+audio_libasound_module_ctl_bluetooth_la_OBJECTS =  \
+       $(am_audio_libasound_module_ctl_bluetooth_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+audio_libasound_module_ctl_bluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+       --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+       $(CCLD) $(audio_libasound_module_ctl_bluetooth_la_CFLAGS) \
+       $(CFLAGS) $(audio_libasound_module_ctl_bluetooth_la_LDFLAGS) \
+       $(LDFLAGS) -o $@
+@ALSA_TRUE@am_audio_libasound_module_ctl_bluetooth_la_rpath = -rpath \
+@ALSA_TRUE@    $(alsadir)
+@ALSA_TRUE@audio_libasound_module_pcm_bluetooth_la_DEPENDENCIES =  \
+@ALSA_TRUE@    sbc/libsbc.la lib/libbluetooth-private.la
+am__audio_libasound_module_pcm_bluetooth_la_SOURCES_DIST =  \
+       audio/pcm_bluetooth.c audio/rtp.h audio/ipc.h audio/ipc.c
+@ALSA_TRUE@am_audio_libasound_module_pcm_bluetooth_la_OBJECTS = audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo \
+@ALSA_TRUE@    audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo
+audio_libasound_module_pcm_bluetooth_la_OBJECTS =  \
+       $(am_audio_libasound_module_pcm_bluetooth_la_OBJECTS)
+audio_libasound_module_pcm_bluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) \
+       --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
+       $(CCLD) $(audio_libasound_module_pcm_bluetooth_la_CFLAGS) \
+       $(CFLAGS) $(audio_libasound_module_pcm_bluetooth_la_LDFLAGS) \
+       $(LDFLAGS) -o $@
+@ALSA_TRUE@am_audio_libasound_module_pcm_bluetooth_la_rpath = -rpath \
+@ALSA_TRUE@    $(alsadir)
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@audio_libgstbluetooth_la_DEPENDENCIES =  \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     sbc/libsbc.la \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     lib/libbluetooth-private.la
+am__audio_libgstbluetooth_la_SOURCES_DIST = audio/gstbluetooth.c \
+       audio/gstpragma.h audio/gstsbcenc.h audio/gstsbcenc.c \
+       audio/gstsbcdec.h audio/gstsbcdec.c audio/gstsbcparse.h \
+       audio/gstsbcparse.c audio/gstavdtpsink.h audio/gstavdtpsink.c \
+       audio/gsta2dpsink.h audio/gsta2dpsink.c audio/gstsbcutil.h \
+       audio/gstsbcutil.c audio/gstrtpsbcpay.h audio/gstrtpsbcpay.c \
+       audio/rtp.h audio/ipc.h audio/ipc.c
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@am_audio_libgstbluetooth_la_OBJECTS = audio/audio_libgstbluetooth_la-gstbluetooth.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     audio/audio_libgstbluetooth_la-gstsbcenc.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     audio/audio_libgstbluetooth_la-gstsbcdec.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     audio/audio_libgstbluetooth_la-gstsbcparse.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     audio/audio_libgstbluetooth_la-gstavdtpsink.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     audio/audio_libgstbluetooth_la-gsta2dpsink.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     audio/audio_libgstbluetooth_la-gstsbcutil.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     audio/audio_libgstbluetooth_la-ipc.lo
+audio_libgstbluetooth_la_OBJECTS =  \
+       $(am_audio_libgstbluetooth_la_OBJECTS)
+audio_libgstbluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) \
+       $(audio_libgstbluetooth_la_LDFLAGS) $(LDFLAGS) -o $@
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@am_audio_libgstbluetooth_la_rpath =  \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@     -rpath $(gstreamerdir)
+lib_libbluetooth_private_la_LIBADD =
+am__objects_1 =
+am__objects_2 = $(am__objects_1) lib/bluetooth.lo lib/hci.lo \
+       lib/sdp.lo lib/uuid.lo
+am_lib_libbluetooth_private_la_OBJECTS = $(am__objects_2)
+lib_libbluetooth_private_la_OBJECTS =  \
+       $(am_lib_libbluetooth_private_la_OBJECTS)
+lib_libbluetooth_la_LIBADD =
+am_lib_libbluetooth_la_OBJECTS = $(am__objects_1) lib/bluetooth.lo \
+       lib/hci.lo lib/sdp.lo lib/uuid.lo
+lib_libbluetooth_la_OBJECTS = $(am_lib_libbluetooth_la_OBJECTS)
+lib_libbluetooth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(AM_CFLAGS) $(CFLAGS) $(lib_libbluetooth_la_LDFLAGS) \
+       $(LDFLAGS) -o $@
+plugins_external_dummy_la_LIBADD =
+am__plugins_external_dummy_la_SOURCES_DIST = plugins/external-dummy.c
+@MAINTAINER_MODE_TRUE@am_plugins_external_dummy_la_OBJECTS = plugins/plugins_external_dummy_la-external-dummy.lo
+plugins_external_dummy_la_OBJECTS =  \
+       $(am_plugins_external_dummy_la_OBJECTS)
+plugins_external_dummy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(plugins_external_dummy_la_CFLAGS) $(CFLAGS) \
+       $(plugins_external_dummy_la_LDFLAGS) $(LDFLAGS) -o $@
+@MAINTAINER_MODE_TRUE@am_plugins_external_dummy_la_rpath = -rpath \
+@MAINTAINER_MODE_TRUE@ $(plugindir)
+sbc_libsbc_la_LIBADD =
+am__sbc_libsbc_la_SOURCES_DIST = sbc/sbc.h sbc/sbc.c sbc/sbc_math.h \
+       sbc/sbc_tables.h sbc/sbc_primitives.h sbc/sbc_primitives.c \
+       sbc/sbc_primitives_mmx.h sbc/sbc_primitives_mmx.c \
+       sbc/sbc_primitives_iwmmxt.h sbc/sbc_primitives_iwmmxt.c \
+       sbc/sbc_primitives_neon.h sbc/sbc_primitives_neon.c \
+       sbc/sbc_primitives_armv6.h sbc/sbc_primitives_armv6.c
+@SBC_TRUE@am_sbc_libsbc_la_OBJECTS = sbc/sbc_libsbc_la-sbc.lo \
+@SBC_TRUE@     sbc/sbc_libsbc_la-sbc_primitives.lo \
+@SBC_TRUE@     sbc/sbc_libsbc_la-sbc_primitives_mmx.lo \
+@SBC_TRUE@     sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo \
+@SBC_TRUE@     sbc/sbc_libsbc_la-sbc_primitives_neon.lo \
+@SBC_TRUE@     sbc/sbc_libsbc_la-sbc_primitives_armv6.lo
+sbc_libsbc_la_OBJECTS = $(am_sbc_libsbc_la_OBJECTS)
+sbc_libsbc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(sbc_libsbc_la_CFLAGS) \
+       $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+@SBC_TRUE@am_sbc_libsbc_la_rpath =
+@TOOLS_TRUE@am__EXEEXT_1 = tools/rfcomm$(EXEEXT) tools/l2ping$(EXEEXT) \
+@TOOLS_TRUE@   tools/hcitool$(EXEEXT) tools/sdptool$(EXEEXT) \
+@TOOLS_TRUE@   tools/ciptool$(EXEEXT)
+@READLINE_TRUE@@TOOLS_TRUE@am__EXEEXT_2 = attrib/gatttool$(EXEEXT)
+@DFUTOOL_TRUE@am__EXEEXT_3 = tools/dfutool$(EXEEXT)
+@TEST_TRUE@am__EXEEXT_4 = test/l2test$(EXEEXT) test/rctest$(EXEEXT)
+@HIDD_TRUE@am__EXEEXT_5 = compat/hidd$(EXEEXT)
+@PAND_TRUE@am__EXEEXT_6 = compat/pand$(EXEEXT)
+@DUND_TRUE@am__EXEEXT_7 = compat/dund$(EXEEXT)
+@SBC_TRUE@am__EXEEXT_8 = sbc/sbcinfo$(EXEEXT) sbc/sbcdec$(EXEEXT) \
+@SBC_TRUE@     sbc/sbcenc$(EXEEXT)
+@SBC_TRUE@@SNDFILE_TRUE@am__EXEEXT_9 = sbc/sbctester$(EXEEXT)
+@TOOLS_TRUE@am__EXEEXT_10 = tools/avinfo$(EXEEXT) \
+@TOOLS_TRUE@   tools/ppporc$(EXEEXT) tools/hcieventmask$(EXEEXT) \
+@TOOLS_TRUE@   tools/hcisecfilter$(EXEEXT) mgmt/btmgmt$(EXEEXT) \
+@TOOLS_TRUE@   monitor/btmon$(EXEEXT) emulator/btvirt$(EXEEXT)
+@USB_TRUE@am__EXEEXT_11 = tools/dfubabel$(EXEEXT) \
+@USB_TRUE@     tools/avctrl$(EXEEXT)
+@TEST_TRUE@am__EXEEXT_12 = unit/test-eir$(EXEEXT)
+@TEST_TRUE@am__EXEEXT_13 = test/gaptest$(EXEEXT) test/sdptest$(EXEEXT) \
+@TEST_TRUE@    test/scotest$(EXEEXT) test/attest$(EXEEXT) \
+@TEST_TRUE@    test/hstest$(EXEEXT) test/avtest$(EXEEXT) \
+@TEST_TRUE@    test/ipctest$(EXEEXT) test/lmptest$(EXEEXT) \
+@TEST_TRUE@    test/bdaddr$(EXEEXT) test/agent$(EXEEXT) \
+@TEST_TRUE@    test/btiotest$(EXEEXT) test/test-textfile$(EXEEXT) \
+@TEST_TRUE@    test/uuidtest$(EXEEXT) test/mpris-player$(EXEEXT) \
+@TEST_TRUE@    $(am__EXEEXT_12)
+@TOOLS_TRUE@am__EXEEXT_14 = tools/hciattach$(EXEEXT) \
+@TOOLS_TRUE@   tools/hciconfig$(EXEEXT)
+@BCCMD_TRUE@am__EXEEXT_15 = tools/bccmd$(EXEEXT)
+@TEST_TRUE@am__EXEEXT_16 = test/hciemu$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS) $(cups_PROGRAMS) $(noinst_PROGRAMS) \
+       $(sbin_PROGRAMS) $(udev_PROGRAMS)
+am__attrib_gatttool_SOURCES_DIST = attrib/gatttool.c attrib/att.c \
+       attrib/gatt.c attrib/gattrib.c btio/btio.c attrib/gatttool.h \
+       attrib/interactive.c attrib/utils.c src/log.c
+@READLINE_TRUE@@TOOLS_TRUE@am_attrib_gatttool_OBJECTS =  \
+@READLINE_TRUE@@TOOLS_TRUE@    attrib/gatttool.$(OBJEXT) \
+@READLINE_TRUE@@TOOLS_TRUE@    attrib/att.$(OBJEXT) \
+@READLINE_TRUE@@TOOLS_TRUE@    attrib/gatt.$(OBJEXT) \
+@READLINE_TRUE@@TOOLS_TRUE@    attrib/gattrib.$(OBJEXT) \
+@READLINE_TRUE@@TOOLS_TRUE@    btio/btio.$(OBJEXT) \
+@READLINE_TRUE@@TOOLS_TRUE@    attrib/interactive.$(OBJEXT) \
+@READLINE_TRUE@@TOOLS_TRUE@    attrib/utils.$(OBJEXT) \
+@READLINE_TRUE@@TOOLS_TRUE@    src/log.$(OBJEXT)
+attrib_gatttool_OBJECTS = $(am_attrib_gatttool_OBJECTS)
+@READLINE_TRUE@@TOOLS_TRUE@attrib_gatttool_DEPENDENCIES =  \
+@READLINE_TRUE@@TOOLS_TRUE@    lib/libbluetooth-private.la
+am__compat_dund_SOURCES_DIST = compat/dund.c compat/dund.h \
+       compat/lib.h compat/sdp.h compat/sdp.c compat/dun.c \
+       compat/msdun.c src/textfile.h src/textfile.c
+@DUND_TRUE@am_compat_dund_OBJECTS = compat/dund.$(OBJEXT) \
+@DUND_TRUE@    compat/sdp.$(OBJEXT) compat/dun.$(OBJEXT) \
+@DUND_TRUE@    compat/msdun.$(OBJEXT) src/textfile.$(OBJEXT)
+compat_dund_OBJECTS = $(am_compat_dund_OBJECTS)
+@DUND_TRUE@compat_dund_DEPENDENCIES = lib/libbluetooth-private.la
+am__compat_hidd_SOURCES_DIST = compat/hidd.c compat/hidd.h \
+       src/uinput.h compat/sdp.h compat/sdp.c compat/fakehid.c \
+       src/textfile.h src/textfile.c
+@HIDD_TRUE@am_compat_hidd_OBJECTS = compat/hidd.$(OBJEXT) \
+@HIDD_TRUE@    compat/sdp.$(OBJEXT) compat/fakehid.$(OBJEXT) \
+@HIDD_TRUE@    src/textfile.$(OBJEXT)
+compat_hidd_OBJECTS = $(am_compat_hidd_OBJECTS)
+@HIDD_TRUE@compat_hidd_DEPENDENCIES = lib/libbluetooth-private.la
+am__compat_pand_SOURCES_DIST = compat/pand.c compat/pand.h \
+       compat/bnep.c compat/sdp.h compat/sdp.c src/textfile.h \
+       src/textfile.c
+@PAND_TRUE@am_compat_pand_OBJECTS = compat/pand.$(OBJEXT) \
+@PAND_TRUE@    compat/bnep.$(OBJEXT) compat/sdp.$(OBJEXT) \
+@PAND_TRUE@    src/textfile.$(OBJEXT)
+compat_pand_OBJECTS = $(am_compat_pand_OBJECTS)
+@PAND_TRUE@compat_pand_DEPENDENCIES = lib/libbluetooth-private.la
+am__cups_bluetooth_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \
+       gdbus/watch.c gdbus/object.c gdbus/polkit.c cups/main.c \
+       cups/cups.h cups/sdp.c cups/spp.c cups/hcrp.c
+am__objects_3 = gdbus/mainloop.$(OBJEXT) gdbus/watch.$(OBJEXT) \
+       gdbus/object.$(OBJEXT) gdbus/polkit.$(OBJEXT)
+@CUPS_TRUE@am_cups_bluetooth_OBJECTS = $(am__objects_3) \
+@CUPS_TRUE@    cups/main.$(OBJEXT) cups/sdp.$(OBJEXT) \
+@CUPS_TRUE@    cups/spp.$(OBJEXT) cups/hcrp.$(OBJEXT)
+cups_bluetooth_OBJECTS = $(am_cups_bluetooth_OBJECTS)
+@CUPS_TRUE@cups_bluetooth_DEPENDENCIES = lib/libbluetooth-private.la
+am__emulator_btvirt_SOURCES_DIST = emulator/main.c monitor/bt.h \
+       monitor/mainloop.h monitor/mainloop.c emulator/server.h \
+       emulator/server.c emulator/vhci.h emulator/vhci.c \
+       emulator/btdev.h emulator/btdev.c
+@TOOLS_TRUE@am_emulator_btvirt_OBJECTS = emulator/main.$(OBJEXT) \
+@TOOLS_TRUE@   monitor/mainloop.$(OBJEXT) \
+@TOOLS_TRUE@   emulator/server.$(OBJEXT) emulator/vhci.$(OBJEXT) \
+@TOOLS_TRUE@   emulator/btdev.$(OBJEXT)
+emulator_btvirt_OBJECTS = $(am_emulator_btvirt_OBJECTS)
+emulator_btvirt_LDADD = $(LDADD)
+am__mgmt_btmgmt_SOURCES_DIST = mgmt/main.c src/glib-helper.c
+@TOOLS_TRUE@am_mgmt_btmgmt_OBJECTS = mgmt/main.$(OBJEXT) \
+@TOOLS_TRUE@   src/glib-helper.$(OBJEXT)
+mgmt_btmgmt_OBJECTS = $(am_mgmt_btmgmt_OBJECTS)
+@TOOLS_TRUE@mgmt_btmgmt_DEPENDENCIES = lib/libbluetooth-private.la
+am__monitor_btmon_SOURCES_DIST = monitor/main.c monitor/bt.h \
+       monitor/mainloop.h monitor/mainloop.c monitor/hcidump.h \
+       monitor/hcidump.c monitor/btsnoop.h monitor/btsnoop.c \
+       monitor/control.h monitor/control.c monitor/packet.h \
+       monitor/packet.c
+@TOOLS_TRUE@am_monitor_btmon_OBJECTS = monitor/main.$(OBJEXT) \
+@TOOLS_TRUE@   monitor/mainloop.$(OBJEXT) \
+@TOOLS_TRUE@   monitor/hcidump.$(OBJEXT) \
+@TOOLS_TRUE@   monitor/btsnoop.$(OBJEXT) \
+@TOOLS_TRUE@   monitor/control.$(OBJEXT) monitor/packet.$(OBJEXT)
+monitor_btmon_OBJECTS = $(am_monitor_btmon_OBJECTS)
+@TOOLS_TRUE@monitor_btmon_DEPENDENCIES = lib/libbluetooth-private.la
+am__sbc_sbcdec_SOURCES_DIST = sbc/sbcdec.c sbc/formats.h
+@SBC_TRUE@am_sbc_sbcdec_OBJECTS = sbc/sbcdec.$(OBJEXT)
+sbc_sbcdec_OBJECTS = $(am_sbc_sbcdec_OBJECTS)
+@SBC_TRUE@sbc_sbcdec_DEPENDENCIES = sbc/libsbc.la
+am__sbc_sbcenc_SOURCES_DIST = sbc/sbcenc.c sbc/formats.h
+@SBC_TRUE@am_sbc_sbcenc_OBJECTS = sbc/sbcenc.$(OBJEXT)
+sbc_sbcenc_OBJECTS = $(am_sbc_sbcenc_OBJECTS)
+@SBC_TRUE@sbc_sbcenc_DEPENDENCIES = sbc/libsbc.la
+sbc_sbcinfo_SOURCES = sbc/sbcinfo.c
+sbc_sbcinfo_OBJECTS = sbc/sbcinfo.$(OBJEXT)
+sbc_sbcinfo_LDADD = $(LDADD)
+sbc_sbctester_SOURCES = sbc/sbctester.c
+sbc_sbctester_OBJECTS = sbc/sbctester.$(OBJEXT)
+sbc_sbctester_DEPENDENCIES =
+am__src_bluetoothd_SOURCES_DIST = gdbus/gdbus.h gdbus/mainloop.c \
+       gdbus/watch.c gdbus/object.c gdbus/polkit.c plugins/pnat.c \
+       audio/main.c audio/manager.h audio/manager.c audio/gateway.h \
+       audio/gateway.c audio/headset.h audio/headset.c \
+       audio/control.h audio/control.c audio/avctp.h audio/avctp.c \
+       audio/avrcp.h audio/avrcp.c audio/device.h audio/device.c \
+       audio/source.h audio/source.c audio/sink.h audio/sink.c \
+       audio/a2dp.h audio/a2dp.c audio/avdtp.h audio/avdtp.c \
+       audio/ipc.h audio/ipc.c audio/unix.h audio/unix.c \
+       audio/media.h audio/media.c audio/transport.h \
+       audio/transport.c audio/telephony.h audio/a2dp-codecs.h \
+       sap/main.c sap/manager.h sap/manager.c sap/server.h \
+       sap/server.c sap/sap.h input/main.c input/manager.h \
+       input/manager.c input/server.h input/server.c input/device.h \
+       input/device.c input/fakehid.c input/fakehid.h serial/main.c \
+       serial/manager.h serial/manager.c serial/proxy.h \
+       serial/proxy.c serial/port.h serial/port.c network/main.c \
+       network/manager.h network/manager.c network/common.h \
+       network/common.c network/server.h network/server.c \
+       network/connection.h network/connection.c plugins/service.c \
+       health/hdp_main.c health/hdp_types.h health/hdp_manager.h \
+       health/hdp_manager.c health/hdp.h health/hdp.c \
+       health/hdp_util.h health/hdp_util.c thermometer/main.c \
+       thermometer/manager.h thermometer/manager.c \
+       thermometer/thermometer.h thermometer/thermometer.c \
+       alert/main.c alert/server.h alert/server.c time/main.c \
+       time/server.h time/server.c plugins/gatt-example.c \
+       proximity/main.c proximity/manager.h proximity/manager.c \
+       proximity/monitor.h proximity/monitor.c proximity/reporter.h \
+       proximity/reporter.c proximity/linkloss.h proximity/linkloss.c \
+       proximity/immalert.h proximity/immalert.c deviceinfo/main.c \
+       deviceinfo/manager.h deviceinfo/manager.c \
+       deviceinfo/deviceinfo.h deviceinfo/deviceinfo.c \
+       plugins/hciops.c plugins/mgmtops.c plugins/hal.c \
+       plugins/formfactor.c plugins/storage.c plugins/adaptername.c \
+       plugins/wiimote.c plugins/maemo6.c plugins/dbusoob.c \
+       attrib/att.h attrib/att-database.h attrib/att.c attrib/gatt.h \
+       attrib/gatt.c attrib/gattrib.h attrib/gattrib.c \
+       attrib/client.h attrib/client.c attrib/gatt-service.h \
+       attrib/gatt-service.c btio/btio.h btio/btio.c \
+       health/mcap_lib.h health/mcap_internal.h health/mcap.h \
+       health/mcap.c health/mcap_sync.c src/bluetooth.ver src/main.c \
+       src/log.h src/log.c src/rfkill.c src/hcid.h src/sdpd.h \
+       src/sdpd-server.c src/sdpd-request.c src/sdpd-service.c \
+       src/sdpd-database.c src/attrib-server.h src/attrib-server.c \
+       src/sdp-xml.h src/sdp-xml.c src/sdp-client.h src/sdp-client.c \
+       src/textfile.h src/textfile.c src/glib-helper.h \
+       src/glib-helper.c src/oui.h src/oui.c src/uinput.h src/ppoll.h \
+       src/plugin.h src/plugin.c src/storage.h src/storage.c \
+       src/agent.h src/agent.c src/error.h src/error.c src/manager.h \
+       src/manager.c src/adapter.h src/adapter.c src/device.h \
+       src/device.c src/attio.h src/dbus-common.c src/dbus-common.h \
+       src/event.h src/event.c src/oob.h src/oob.c src/eir.h \
+       src/eir.c
+am__objects_4 = gdbus/bluetoothd-mainloop.$(OBJEXT) \
+       gdbus/bluetoothd-watch.$(OBJEXT) \
+       gdbus/bluetoothd-object.$(OBJEXT) \
+       gdbus/bluetoothd-polkit.$(OBJEXT)
+@PNATPLUGIN_TRUE@am__objects_5 = plugins/bluetoothd-pnat.$(OBJEXT)
+@AUDIOPLUGIN_TRUE@am__objects_6 = audio/bluetoothd-main.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-manager.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-gateway.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-headset.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-control.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-avctp.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-avrcp.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-device.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-source.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-sink.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-a2dp.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-avdtp.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-ipc.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-unix.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-media.$(OBJEXT) \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-transport.$(OBJEXT)
+@SAPPLUGIN_TRUE@am__objects_7 = sap/bluetoothd-main.$(OBJEXT) \
+@SAPPLUGIN_TRUE@       sap/bluetoothd-manager.$(OBJEXT) \
+@SAPPLUGIN_TRUE@       sap/bluetoothd-server.$(OBJEXT)
+@INPUTPLUGIN_TRUE@am__objects_8 = input/bluetoothd-main.$(OBJEXT) \
+@INPUTPLUGIN_TRUE@     input/bluetoothd-manager.$(OBJEXT) \
+@INPUTPLUGIN_TRUE@     input/bluetoothd-server.$(OBJEXT) \
+@INPUTPLUGIN_TRUE@     input/bluetoothd-device.$(OBJEXT) \
+@INPUTPLUGIN_TRUE@     input/bluetoothd-fakehid.$(OBJEXT)
+@SERIALPLUGIN_TRUE@am__objects_9 = serial/bluetoothd-main.$(OBJEXT) \
+@SERIALPLUGIN_TRUE@    serial/bluetoothd-manager.$(OBJEXT) \
+@SERIALPLUGIN_TRUE@    serial/bluetoothd-proxy.$(OBJEXT) \
+@SERIALPLUGIN_TRUE@    serial/bluetoothd-port.$(OBJEXT)
+@NETWORKPLUGIN_TRUE@am__objects_10 =  \
+@NETWORKPLUGIN_TRUE@   network/bluetoothd-main.$(OBJEXT) \
+@NETWORKPLUGIN_TRUE@   network/bluetoothd-manager.$(OBJEXT) \
+@NETWORKPLUGIN_TRUE@   network/bluetoothd-common.$(OBJEXT) \
+@NETWORKPLUGIN_TRUE@   network/bluetoothd-server.$(OBJEXT) \
+@NETWORKPLUGIN_TRUE@   network/bluetoothd-connection.$(OBJEXT)
+@SERVICEPLUGIN_TRUE@am__objects_11 =  \
+@SERVICEPLUGIN_TRUE@   plugins/bluetoothd-service.$(OBJEXT)
+@HEALTHPLUGIN_TRUE@am__objects_12 =  \
+@HEALTHPLUGIN_TRUE@    health/bluetoothd-hdp_main.$(OBJEXT) \
+@HEALTHPLUGIN_TRUE@    health/bluetoothd-hdp_manager.$(OBJEXT) \
+@HEALTHPLUGIN_TRUE@    health/bluetoothd-hdp.$(OBJEXT) \
+@HEALTHPLUGIN_TRUE@    health/bluetoothd-hdp_util.$(OBJEXT)
+@GATTMODULES_TRUE@am__objects_13 =  \
+@GATTMODULES_TRUE@     thermometer/bluetoothd-main.$(OBJEXT) \
+@GATTMODULES_TRUE@     thermometer/bluetoothd-manager.$(OBJEXT) \
+@GATTMODULES_TRUE@     thermometer/bluetoothd-thermometer.$(OBJEXT) \
+@GATTMODULES_TRUE@     alert/bluetoothd-main.$(OBJEXT) \
+@GATTMODULES_TRUE@     alert/bluetoothd-server.$(OBJEXT) \
+@GATTMODULES_TRUE@     time/bluetoothd-main.$(OBJEXT) \
+@GATTMODULES_TRUE@     time/bluetoothd-server.$(OBJEXT) \
+@GATTMODULES_TRUE@     plugins/bluetoothd-gatt-example.$(OBJEXT) \
+@GATTMODULES_TRUE@     proximity/bluetoothd-main.$(OBJEXT) \
+@GATTMODULES_TRUE@     proximity/bluetoothd-manager.$(OBJEXT) \
+@GATTMODULES_TRUE@     proximity/bluetoothd-monitor.$(OBJEXT) \
+@GATTMODULES_TRUE@     proximity/bluetoothd-reporter.$(OBJEXT) \
+@GATTMODULES_TRUE@     proximity/bluetoothd-linkloss.$(OBJEXT) \
+@GATTMODULES_TRUE@     proximity/bluetoothd-immalert.$(OBJEXT) \
+@GATTMODULES_TRUE@     deviceinfo/bluetoothd-main.$(OBJEXT) \
+@GATTMODULES_TRUE@     deviceinfo/bluetoothd-manager.$(OBJEXT) \
+@GATTMODULES_TRUE@     deviceinfo/bluetoothd-deviceinfo.$(OBJEXT)
+@HAL_TRUE@am__objects_14 = plugins/bluetoothd-hal.$(OBJEXT)
+@HAL_FALSE@am__objects_15 = plugins/bluetoothd-formfactor.$(OBJEXT)
+@WIIMOTEPLUGIN_TRUE@am__objects_16 =  \
+@WIIMOTEPLUGIN_TRUE@   plugins/bluetoothd-wiimote.$(OBJEXT)
+@MAEMO6PLUGIN_TRUE@am__objects_17 =  \
+@MAEMO6PLUGIN_TRUE@    plugins/bluetoothd-maemo6.$(OBJEXT)
+@DBUSOOBPLUGIN_TRUE@am__objects_18 =  \
+@DBUSOOBPLUGIN_TRUE@   plugins/bluetoothd-dbusoob.$(OBJEXT)
+am__objects_19 = $(am__objects_5) $(am__objects_6) $(am__objects_7) \
+       $(am__objects_8) $(am__objects_9) $(am__objects_10) \
+       $(am__objects_11) $(am__objects_12) $(am__objects_13) \
+       plugins/bluetoothd-hciops.$(OBJEXT) \
+       plugins/bluetoothd-mgmtops.$(OBJEXT) $(am__objects_14) \
+       $(am__objects_15) plugins/bluetoothd-storage.$(OBJEXT) \
+       plugins/bluetoothd-adaptername.$(OBJEXT) $(am__objects_16) \
+       $(am__objects_17) $(am__objects_18)
+am__objects_20 = attrib/bluetoothd-att.$(OBJEXT) \
+       attrib/bluetoothd-gatt.$(OBJEXT) \
+       attrib/bluetoothd-gattrib.$(OBJEXT) \
+       attrib/bluetoothd-client.$(OBJEXT) \
+       attrib/bluetoothd-gatt-service.$(OBJEXT)
+am__objects_21 = btio/bluetoothd-btio.$(OBJEXT)
+@MCAP_TRUE@am__objects_22 = health/bluetoothd-mcap.$(OBJEXT) \
+@MCAP_TRUE@    health/bluetoothd-mcap_sync.$(OBJEXT)
+am__objects_23 = $(am__objects_22)
+am_src_bluetoothd_OBJECTS = $(am__objects_4) $(am__objects_19) \
+       $(am__objects_20) $(am__objects_21) $(am__objects_23) \
+       src/bluetoothd-main.$(OBJEXT) src/bluetoothd-log.$(OBJEXT) \
+       src/bluetoothd-rfkill.$(OBJEXT) \
+       src/bluetoothd-sdpd-server.$(OBJEXT) \
+       src/bluetoothd-sdpd-request.$(OBJEXT) \
+       src/bluetoothd-sdpd-service.$(OBJEXT) \
+       src/bluetoothd-sdpd-database.$(OBJEXT) \
+       src/bluetoothd-attrib-server.$(OBJEXT) \
+       src/bluetoothd-sdp-xml.$(OBJEXT) \
+       src/bluetoothd-sdp-client.$(OBJEXT) \
+       src/bluetoothd-textfile.$(OBJEXT) \
+       src/bluetoothd-glib-helper.$(OBJEXT) \
+       src/bluetoothd-oui.$(OBJEXT) src/bluetoothd-plugin.$(OBJEXT) \
+       src/bluetoothd-storage.$(OBJEXT) \
+       src/bluetoothd-agent.$(OBJEXT) src/bluetoothd-error.$(OBJEXT) \
+       src/bluetoothd-manager.$(OBJEXT) \
+       src/bluetoothd-adapter.$(OBJEXT) \
+       src/bluetoothd-device.$(OBJEXT) \
+       src/bluetoothd-dbus-common.$(OBJEXT) \
+       src/bluetoothd-event.$(OBJEXT) src/bluetoothd-oob.$(OBJEXT) \
+       src/bluetoothd-eir.$(OBJEXT)
+@AUDIOPLUGIN_TRUE@am__objects_24 =  \
+@AUDIOPLUGIN_TRUE@     audio/bluetoothd-telephony.$(OBJEXT)
+@SAPPLUGIN_TRUE@am__objects_25 = sap/bluetoothd-sap.$(OBJEXT)
+am__objects_26 = $(am__objects_24) $(am__objects_25)
+am__objects_27 = $(am__objects_26)
+nodist_src_bluetoothd_OBJECTS = $(am__objects_27)
+src_bluetoothd_OBJECTS = $(am_src_bluetoothd_OBJECTS) \
+       $(nodist_src_bluetoothd_OBJECTS)
+src_bluetoothd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(src_bluetoothd_CFLAGS) $(CFLAGS) $(src_bluetoothd_LDFLAGS) \
+       $(LDFLAGS) -o $@
+test_agent_SOURCES = test/agent.c
+test_agent_OBJECTS = test/agent.$(OBJEXT)
+test_agent_DEPENDENCIES =
+test_attest_SOURCES = test/attest.c
+test_attest_OBJECTS = test/attest.$(OBJEXT)
+@TEST_TRUE@test_attest_DEPENDENCIES = lib/libbluetooth-private.la
+test_avtest_SOURCES = test/avtest.c
+test_avtest_OBJECTS = test/avtest.$(OBJEXT)
+@TEST_TRUE@test_avtest_DEPENDENCIES = lib/libbluetooth-private.la
+am__test_bdaddr_SOURCES_DIST = test/bdaddr.c src/oui.h src/oui.c
+@TEST_TRUE@am_test_bdaddr_OBJECTS = test/bdaddr.$(OBJEXT) \
+@TEST_TRUE@    src/oui.$(OBJEXT)
+test_bdaddr_OBJECTS = $(am_test_bdaddr_OBJECTS)
+@TEST_TRUE@test_bdaddr_DEPENDENCIES = lib/libbluetooth-private.la
+am__test_btiotest_SOURCES_DIST = test/btiotest.c btio/btio.h \
+       btio/btio.c
+@TEST_TRUE@am_test_btiotest_OBJECTS = test/btiotest.$(OBJEXT) \
+@TEST_TRUE@    btio/btio.$(OBJEXT)
+test_btiotest_OBJECTS = $(am_test_btiotest_OBJECTS)
+@TEST_TRUE@test_btiotest_DEPENDENCIES = lib/libbluetooth-private.la
+test_gaptest_SOURCES = test/gaptest.c
+test_gaptest_OBJECTS = test/gaptest.$(OBJEXT)
+test_gaptest_DEPENDENCIES =
+test_hciemu_SOURCES = test/hciemu.c
+test_hciemu_OBJECTS = test/hciemu.$(OBJEXT)
+@TEST_TRUE@test_hciemu_DEPENDENCIES = lib/libbluetooth-private.la
+test_hstest_SOURCES = test/hstest.c
+test_hstest_OBJECTS = test/hstest.$(OBJEXT)
+@TEST_TRUE@test_hstest_DEPENDENCIES = lib/libbluetooth-private.la
+am__test_ipctest_SOURCES_DIST = test/ipctest.c audio/ipc.h audio/ipc.c
+@TEST_TRUE@am_test_ipctest_OBJECTS = test/ipctest.$(OBJEXT) \
+@TEST_TRUE@    audio/ipc.$(OBJEXT)
+test_ipctest_OBJECTS = $(am_test_ipctest_OBJECTS)
+@TEST_TRUE@test_ipctest_DEPENDENCIES = sbc/libsbc.la
+test_l2test_SOURCES = test/l2test.c
+test_l2test_OBJECTS = test/l2test.$(OBJEXT)
+@TEST_TRUE@test_l2test_DEPENDENCIES = lib/libbluetooth-private.la
+test_lmptest_SOURCES = test/lmptest.c
+test_lmptest_OBJECTS = test/lmptest.$(OBJEXT)
+@TEST_TRUE@test_lmptest_DEPENDENCIES = lib/libbluetooth-private.la
+am__test_mpris_player_SOURCES_DIST = test/mpris-player.c
+@TEST_TRUE@am_test_mpris_player_OBJECTS = test/mpris-player.$(OBJEXT)
+test_mpris_player_OBJECTS = $(am_test_mpris_player_OBJECTS)
+test_mpris_player_DEPENDENCIES =
+test_rctest_SOURCES = test/rctest.c
+test_rctest_OBJECTS = test/rctest.$(OBJEXT)
+@TEST_TRUE@test_rctest_DEPENDENCIES = lib/libbluetooth-private.la
+test_scotest_SOURCES = test/scotest.c
+test_scotest_OBJECTS = test/scotest.$(OBJEXT)
+@TEST_TRUE@test_scotest_DEPENDENCIES = lib/libbluetooth-private.la
+test_sdptest_SOURCES = test/sdptest.c
+test_sdptest_OBJECTS = test/sdptest.$(OBJEXT)
+@TEST_TRUE@test_sdptest_DEPENDENCIES = lib/libbluetooth-private.la
+am__test_test_textfile_SOURCES_DIST = test/test-textfile.c \
+       src/textfile.h src/textfile.c
+@TEST_TRUE@am_test_test_textfile_OBJECTS =  \
+@TEST_TRUE@    test/test-textfile.$(OBJEXT) src/textfile.$(OBJEXT)
+test_test_textfile_OBJECTS = $(am_test_test_textfile_OBJECTS)
+test_test_textfile_LDADD = $(LDADD)
+am__test_uuidtest_SOURCES_DIST = test/uuidtest.c
+@TEST_TRUE@am_test_uuidtest_OBJECTS = test/uuidtest.$(OBJEXT)
+test_uuidtest_OBJECTS = $(am_test_uuidtest_OBJECTS)
+@TEST_TRUE@test_uuidtest_DEPENDENCIES = lib/libbluetooth-private.la
+tools_avctrl_SOURCES = tools/avctrl.c
+tools_avctrl_OBJECTS = tools/avctrl.$(OBJEXT)
+tools_avctrl_DEPENDENCIES =
+tools_avinfo_SOURCES = tools/avinfo.c
+tools_avinfo_OBJECTS = tools/avinfo.$(OBJEXT)
+@TOOLS_TRUE@tools_avinfo_DEPENDENCIES = lib/libbluetooth-private.la
+am__tools_bccmd_SOURCES_DIST = tools/bccmd.c tools/csr.h tools/csr.c \
+       tools/csr_hci.c tools/csr_h4.c tools/csr_3wire.c \
+       tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c tools/csr_usb.c
+@BCCMD_TRUE@@USB_TRUE@am__objects_28 = tools/csr_usb.$(OBJEXT)
+@BCCMD_TRUE@am_tools_bccmd_OBJECTS = tools/bccmd.$(OBJEXT) \
+@BCCMD_TRUE@   tools/csr.$(OBJEXT) tools/csr_hci.$(OBJEXT) \
+@BCCMD_TRUE@   tools/csr_h4.$(OBJEXT) tools/csr_3wire.$(OBJEXT) \
+@BCCMD_TRUE@   tools/csr_bcsp.$(OBJEXT) tools/ubcsp.$(OBJEXT) \
+@BCCMD_TRUE@   $(am__objects_28)
+tools_bccmd_OBJECTS = $(am_tools_bccmd_OBJECTS)
+am__DEPENDENCIES_1 =
+@BCCMD_TRUE@tools_bccmd_DEPENDENCIES = lib/libbluetooth-private.la \
+@BCCMD_TRUE@   $(am__DEPENDENCIES_1)
+tools_ciptool_SOURCES = tools/ciptool.c
+tools_ciptool_OBJECTS = tools/ciptool.$(OBJEXT)
+@TOOLS_TRUE@tools_ciptool_DEPENDENCIES = lib/libbluetooth-private.la
+tools_dfubabel_SOURCES = tools/dfubabel.c
+tools_dfubabel_OBJECTS = tools/dfubabel.$(OBJEXT)
+tools_dfubabel_DEPENDENCIES =
+am__tools_dfutool_SOURCES_DIST = tools/dfutool.c tools/dfu.h \
+       tools/dfu.c
+@DFUTOOL_TRUE@am_tools_dfutool_OBJECTS = tools/dfutool.$(OBJEXT) \
+@DFUTOOL_TRUE@ tools/dfu.$(OBJEXT)
+tools_dfutool_OBJECTS = $(am_tools_dfutool_OBJECTS)
+tools_dfutool_DEPENDENCIES =
+am__tools_hciattach_SOURCES_DIST = tools/hciattach.c tools/hciattach.h \
+       tools/hciattach_st.c tools/hciattach_ti.c \
+       tools/hciattach_tialt.c tools/hciattach_ath3k.c \
+       tools/hciattach_qualcomm.c tools/hciattach_intel.c
+@TOOLS_TRUE@am_tools_hciattach_OBJECTS = tools/hciattach.$(OBJEXT) \
+@TOOLS_TRUE@   tools/hciattach_st.$(OBJEXT) \
+@TOOLS_TRUE@   tools/hciattach_ti.$(OBJEXT) \
+@TOOLS_TRUE@   tools/hciattach_tialt.$(OBJEXT) \
+@TOOLS_TRUE@   tools/hciattach_ath3k.$(OBJEXT) \
+@TOOLS_TRUE@   tools/hciattach_qualcomm.$(OBJEXT) \
+@TOOLS_TRUE@   tools/hciattach_intel.$(OBJEXT)
+tools_hciattach_OBJECTS = $(am_tools_hciattach_OBJECTS)
+@TOOLS_TRUE@tools_hciattach_DEPENDENCIES =  \
+@TOOLS_TRUE@   lib/libbluetooth-private.la
+am__tools_hciconfig_SOURCES_DIST = tools/hciconfig.c tools/csr.h \
+       tools/csr.c src/textfile.h src/textfile.c
+@TOOLS_TRUE@am_tools_hciconfig_OBJECTS = tools/hciconfig.$(OBJEXT) \
+@TOOLS_TRUE@   tools/csr.$(OBJEXT) src/textfile.$(OBJEXT)
+tools_hciconfig_OBJECTS = $(am_tools_hciconfig_OBJECTS)
+@TOOLS_TRUE@tools_hciconfig_DEPENDENCIES =  \
+@TOOLS_TRUE@   lib/libbluetooth-private.la
+tools_hcieventmask_SOURCES = tools/hcieventmask.c
+tools_hcieventmask_OBJECTS = tools/hcieventmask.$(OBJEXT)
+@TOOLS_TRUE@tools_hcieventmask_DEPENDENCIES =  \
+@TOOLS_TRUE@   lib/libbluetooth-private.la
+tools_hcisecfilter_SOURCES = tools/hcisecfilter.c
+tools_hcisecfilter_OBJECTS = tools/hcisecfilter.$(OBJEXT)
+tools_hcisecfilter_LDADD = $(LDADD)
+am__tools_hcitool_SOURCES_DIST = tools/hcitool.c src/oui.h src/oui.c \
+       src/textfile.h src/textfile.c
+@TOOLS_TRUE@am_tools_hcitool_OBJECTS = tools/hcitool.$(OBJEXT) \
+@TOOLS_TRUE@   src/oui.$(OBJEXT) src/textfile.$(OBJEXT)
+tools_hcitool_OBJECTS = $(am_tools_hcitool_OBJECTS)
+@TOOLS_TRUE@tools_hcitool_DEPENDENCIES = lib/libbluetooth-private.la
+tools_hid2hci_SOURCES = tools/hid2hci.c
+tools_hid2hci_OBJECTS = tools/hid2hci.$(OBJEXT)
+tools_hid2hci_DEPENDENCIES =
+tools_l2ping_SOURCES = tools/l2ping.c
+tools_l2ping_OBJECTS = tools/l2ping.$(OBJEXT)
+@TOOLS_TRUE@tools_l2ping_DEPENDENCIES = lib/libbluetooth-private.la
+tools_ppporc_SOURCES = tools/ppporc.c
+tools_ppporc_OBJECTS = tools/ppporc.$(OBJEXT)
+@TOOLS_TRUE@tools_ppporc_DEPENDENCIES = lib/libbluetooth-private.la
+am__tools_rfcomm_SOURCES_DIST = tools/rfcomm.c tools/parser.y \
+       tools/lexer.l tools/kword.h tools/kword.c
+@TOOLS_TRUE@am_tools_rfcomm_OBJECTS = tools/rfcomm.$(OBJEXT) \
+@TOOLS_TRUE@   tools/parser.$(OBJEXT) tools/lexer.$(OBJEXT) \
+@TOOLS_TRUE@   tools/kword.$(OBJEXT)
+am__EXTRA_tools_rfcomm_SOURCES_DIST = tools/parser.h tools/parser.c \
+       tools/lexer.c
+tools_rfcomm_OBJECTS = $(am_tools_rfcomm_OBJECTS)
+@TOOLS_TRUE@tools_rfcomm_DEPENDENCIES = lib/libbluetooth-private.la
+am__tools_sdptool_SOURCES_DIST = tools/sdptool.c src/sdp-xml.h \
+       src/sdp-xml.c
+@TOOLS_TRUE@am_tools_sdptool_OBJECTS = tools/sdptool.$(OBJEXT) \
+@TOOLS_TRUE@   src/sdp-xml.$(OBJEXT)
+tools_sdptool_OBJECTS = $(am_tools_sdptool_OBJECTS)
+@TOOLS_TRUE@tools_sdptool_DEPENDENCIES = lib/libbluetooth-private.la
+am__unit_test_eir_SOURCES_DIST = unit/test-eir.c src/eir.c \
+       src/glib-helper.c
+@TEST_TRUE@am_unit_test_eir_OBJECTS =  \
+@TEST_TRUE@    unit/unit_test_eir-test-eir.$(OBJEXT) \
+@TEST_TRUE@    src/unit_test_eir-eir.$(OBJEXT) \
+@TEST_TRUE@    src/unit_test_eir-glib-helper.$(OBJEXT)
+unit_test_eir_OBJECTS = $(am_unit_test_eir_OBJECTS)
+@TEST_TRUE@unit_test_eir_DEPENDENCIES = lib/libbluetooth-private.la
+unit_test_eir_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(unit_test_eir_CFLAGS) \
+       $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+am__dist_udev_SCRIPTS_DIST = scripts/bluetooth_serial
+SCRIPTS = $(dist_udev_SCRIPTS)
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+       $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+       $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC    " $@;
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD  " $@;
+@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ ||
+LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS)
+LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS)
+AM_V_LEX = $(am__v_LEX_@AM_V@)
+am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@)
+am__v_LEX_0 = @echo "  LEX   " $@;
+YLWRAP = $(top_srcdir)/ylwrap
+@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ ||
+YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS)
+AM_V_YACC = $(am__v_YACC_@AM_V@)
+am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@)
+am__v_YACC_0 = @echo "  YACC  " $@;
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN   " $@;
+SOURCES = $(audio_libtelephony_a_SOURCES) $(sap_libsap_a_SOURCES) \
+       $(audio_libasound_module_ctl_bluetooth_la_SOURCES) \
+       $(audio_libasound_module_pcm_bluetooth_la_SOURCES) \
+       $(audio_libgstbluetooth_la_SOURCES) \
+       $(lib_libbluetooth_private_la_SOURCES) \
+       $(lib_libbluetooth_la_SOURCES) \
+       $(plugins_external_dummy_la_SOURCES) $(sbc_libsbc_la_SOURCES) \
+       $(attrib_gatttool_SOURCES) $(compat_dund_SOURCES) \
+       $(compat_hidd_SOURCES) $(compat_pand_SOURCES) \
+       $(cups_bluetooth_SOURCES) $(emulator_btvirt_SOURCES) \
+       $(mgmt_btmgmt_SOURCES) $(monitor_btmon_SOURCES) \
+       $(sbc_sbcdec_SOURCES) $(sbc_sbcenc_SOURCES) sbc/sbcinfo.c \
+       sbc/sbctester.c $(src_bluetoothd_SOURCES) \
+       $(nodist_src_bluetoothd_SOURCES) test/agent.c test/attest.c \
+       test/avtest.c $(test_bdaddr_SOURCES) $(test_btiotest_SOURCES) \
+       test/gaptest.c test/hciemu.c test/hstest.c \
+       $(test_ipctest_SOURCES) test/l2test.c test/lmptest.c \
+       $(test_mpris_player_SOURCES) test/rctest.c test/scotest.c \
+       test/sdptest.c $(test_test_textfile_SOURCES) \
+       $(test_uuidtest_SOURCES) tools/avctrl.c tools/avinfo.c \
+       $(tools_bccmd_SOURCES) tools/ciptool.c tools/dfubabel.c \
+       $(tools_dfutool_SOURCES) $(tools_hciattach_SOURCES) \
+       $(tools_hciconfig_SOURCES) tools/hcieventmask.c \
+       tools/hcisecfilter.c $(tools_hcitool_SOURCES) tools/hid2hci.c \
+       tools/l2ping.c tools/ppporc.c $(tools_rfcomm_SOURCES) \
+       $(EXTRA_tools_rfcomm_SOURCES) $(tools_sdptool_SOURCES) \
+       $(unit_test_eir_SOURCES)
+DIST_SOURCES = $(am__audio_libtelephony_a_SOURCES_DIST) \
+       $(am__sap_libsap_a_SOURCES_DIST) \
+       $(am__audio_libasound_module_ctl_bluetooth_la_SOURCES_DIST) \
+       $(am__audio_libasound_module_pcm_bluetooth_la_SOURCES_DIST) \
+       $(am__audio_libgstbluetooth_la_SOURCES_DIST) \
+       $(lib_libbluetooth_private_la_SOURCES) \
+       $(lib_libbluetooth_la_SOURCES) \
+       $(am__plugins_external_dummy_la_SOURCES_DIST) \
+       $(am__sbc_libsbc_la_SOURCES_DIST) \
+       $(am__attrib_gatttool_SOURCES_DIST) \
+       $(am__compat_dund_SOURCES_DIST) \
+       $(am__compat_hidd_SOURCES_DIST) \
+       $(am__compat_pand_SOURCES_DIST) \
+       $(am__cups_bluetooth_SOURCES_DIST) \
+       $(am__emulator_btvirt_SOURCES_DIST) \
+       $(am__mgmt_btmgmt_SOURCES_DIST) \
+       $(am__monitor_btmon_SOURCES_DIST) \
+       $(am__sbc_sbcdec_SOURCES_DIST) $(am__sbc_sbcenc_SOURCES_DIST) \
+       sbc/sbcinfo.c sbc/sbctester.c \
+       $(am__src_bluetoothd_SOURCES_DIST) test/agent.c test/attest.c \
+       test/avtest.c $(am__test_bdaddr_SOURCES_DIST) \
+       $(am__test_btiotest_SOURCES_DIST) test/gaptest.c test/hciemu.c \
+       test/hstest.c $(am__test_ipctest_SOURCES_DIST) test/l2test.c \
+       test/lmptest.c $(am__test_mpris_player_SOURCES_DIST) \
+       test/rctest.c test/scotest.c test/sdptest.c \
+       $(am__test_test_textfile_SOURCES_DIST) \
+       $(am__test_uuidtest_SOURCES_DIST) tools/avctrl.c \
+       tools/avinfo.c $(am__tools_bccmd_SOURCES_DIST) tools/ciptool.c \
+       tools/dfubabel.c $(am__tools_dfutool_SOURCES_DIST) \
+       $(am__tools_hciattach_SOURCES_DIST) \
+       $(am__tools_hciconfig_SOURCES_DIST) tools/hcieventmask.c \
+       tools/hcisecfilter.c $(am__tools_hcitool_SOURCES_DIST) \
+       tools/hid2hci.c tools/l2ping.c tools/ppporc.c \
+       $(am__tools_rfcomm_SOURCES_DIST) \
+       $(am__EXTRA_tools_rfcomm_SOURCES_DIST) \
+       $(am__tools_sdptool_SOURCES_DIST) \
+       $(am__unit_test_eir_SOURCES_DIST)
+man1dir = $(mandir)/man1
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(dist_man_MANS) $(man_MANS)
+DATA = $(alsaconf_DATA) $(conf_DATA) $(dbus_DATA) $(dbusservice_DATA) \
+       $(pkgconfig_DATA) $(rules_DATA) $(state_DATA) \
+       $(systemdunit_DATA)
+HEADERS = $(include_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+# If stdout is a non-dumb tty, use colors.  If test -t is not supported,
+# then this fails; a conservative approach.  Of course do not redirect
+# stdout here, just stderr.
+am__tty_colors = \
+red=; grn=; lgn=; blu=; std=; \
+test "X$(AM_COLOR_TESTS)" != Xno \
+&& test "X$$TERM" != Xdumb \
+&& { test "X$(AM_COLOR_TESTS)" = Xalways || test -t 1 2>/dev/null; } \
+&& { \
+  red='\e[0;31m'; \
+  grn='\e[0;32m'; \
+  lgn='\e[1;32m'; \
+  blu='\e[1;34m'; \
+  std='\e[m'; \
+}
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+  if test -d "$(distdir)"; then \
+    find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+      && rm -rf "$(distdir)" \
+      || { sleep 5 && rm -rf "$(distdir)"; }; \
+  else :; fi
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
+  | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_CFLAGS = @CHECK_CFLAGS@
+CHECK_LIBS = @CHECK_LIBS@
+CONFIGDIR = @CONFIGDIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+GREP = @GREP@
+GSTREAMER_CFLAGS = @GSTREAMER_CFLAGS@
+GSTREAMER_LIBS = @GSTREAMER_LIBS@
+GSTREAMER_PLUGINSDIR = @GSTREAMER_PLUGINSDIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MISC_CFLAGS = @MISC_CFLAGS@
+MISC_LDFLAGS = @MISC_LDFLAGS@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SAP_DRIVER = @SAP_DRIVER@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SNDFILE_CFLAGS = @SNDFILE_CFLAGS@
+SNDFILE_LIBS = @SNDFILE_LIBS@
+STORAGEDIR = @STORAGEDIR@
+STRIP = @STRIP@
+SYSTEMD_UNITDIR = @SYSTEMD_UNITDIR@
+TELEPHONY_DRIVER = @TELEPHONY_DRIVER@
+UDEV_CFLAGS = @UDEV_CFLAGS@
+UDEV_DIR = @UDEV_DIR@
+UDEV_LIBS = @UDEV_LIBS@
+USB_CFLAGS = @USB_CFLAGS@
+USB_LIBS = @USB_LIBS@
+VERSION = @VERSION@
+WARNING_CFLAGS = @WARNING_CFLAGS@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@/bluetooth
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_MAKEFLAGS = --no-print-directory
+lib_LTLIBRARIES = lib/libbluetooth.la
+noinst_LIBRARIES = $(am__append_10) $(am__append_14)
+noinst_LTLIBRARIES = lib/libbluetooth-private.la $(am__append_1)
+dist_man_MANS = $(am__append_43) $(am__append_48) $(am__append_50) \
+       $(am__append_53) $(am__append_59) $(am__append_63) \
+       $(am__append_66) $(am__append_69)
+dist_noinst_MANS = 
+CLEANFILES = $(builtin_files) tools/lexer.c tools/parser.c \
+       tools/parser.h $(rules_DATA)
+EXTRA_DIST = plugins/hal.c plugins/formfactor.c src/genbuiltin \
+       src/bluetooth.conf src/org.bluez.service src/main.conf \
+       network/network.conf input/input.conf serial/serial.conf \
+       audio/audio.conf audio/telephony-dummy.c \
+       audio/telephony-maemo5.c audio/telephony-ofono.c \
+       audio/telephony-maemo6.c sap/sap-dummy.c sap/sap-u8500.c \
+       proximity/proximity.conf audio/bluetooth.conf $(am__append_44) \
+       tools/rfcomm.conf $(am__append_49) $(am__append_51) \
+       $(am__append_54) tools/dfubabel.1 tools/avctrl.8 \
+       $(am__append_60) $(am__append_61) test/sap-client test/hsplay \
+       test/hsmicro test/dbusdef.py test/monitor-bluetooth \
+       test/list-devices test/test-discovery test/test-manager \
+       test/test-adapter test/test-device test/test-service \
+       test/test-serial test/test-telephony test/test-network \
+       test/simple-agent test/simple-service test/simple-endpoint \
+       test/test-audio test/test-input test/test-sap-server \
+       test/test-oob test/test-attrib test/test-proximity \
+       test/test-thermometer test/test-serial-proxy test/test-health \
+       test/test-health-sink test/service-record.dtd \
+       test/service-did.xml test/service-spp.xml test/service-opp.xml \
+       test/service-ftp.xml test/simple-player test/test-nap \
+       $(am__append_64) $(am__append_67) $(am__append_70) \
+       scripts/bluetooth-hid2hci.rules scripts/bluetooth-serial.rules \
+       doc/manager-api.txt doc/adapter-api.txt doc/device-api.txt \
+       doc/service-api.txt doc/agent-api.txt doc/attribute-api.txt \
+       doc/serial-api.txt doc/network-api.txt doc/input-api.txt \
+       doc/audio-api.txt doc/control-api.txt doc/hfp-api.txt \
+       doc/health-api.txt doc/sap-api.txt doc/media-api.txt \
+       doc/assigned-numbers.txt
+include_HEADERS = $(lib_headers)
+AM_CFLAGS = $(WARNING_CFLAGS) $(MISC_CFLAGS) @DBUS_CFLAGS@ \
+       @GLIB_CFLAGS@ $(am__empty)
+AM_LDFLAGS = $(MISC_LDFLAGS)
+@DATAFILES_TRUE@dbusdir = $(sysconfdir)/dbus-1/system.d
+@DATAFILES_TRUE@dbusservicedir = $(datadir)/dbus-1/system-services
+@DATAFILES_TRUE@dbus_DATA = src/bluetooth.conf
+@DATAFILES_TRUE@dbusservice_DATA = src/org.bluez.service
+@DATAFILES_TRUE@confdir = $(sysconfdir)/bluetooth
+@DATAFILES_TRUE@conf_DATA = src/main.conf $(am__append_38)
+@DATAFILES_TRUE@statedir = $(localstatedir)/lib/bluetooth
+@DATAFILES_TRUE@state_DATA = 
+@DATAFILES_TRUE@@SYSTEMD_TRUE@systemdunitdir = @SYSTEMD_UNITDIR@
+@DATAFILES_TRUE@@SYSTEMD_TRUE@systemdunit_DATA = src/bluetooth.service
+plugindir = $(libdir)/bluetooth/plugins
+@MAINTAINER_MODE_FALSE@build_plugindir = $(plugindir)
+@MAINTAINER_MODE_TRUE@build_plugindir = $(abs_top_srcdir)/plugins/.libs
+plugin_LTLIBRARIES = $(am__append_37)
+lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h lib/mgmt.h \
+               lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h lib/uuid.h \
+               lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h lib/a2mp.h
+
+local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file)))
+BUILT_SOURCES = $(local_headers) src/builtin.h
+lib_libbluetooth_la_SOURCES = $(lib_headers) \
+                               lib/bluetooth.c lib/hci.c lib/sdp.c lib/uuid.c
+
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 16:0:13
+lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
+lib_libbluetooth_private_la_SOURCES = $(lib_libbluetooth_la_SOURCES)
+@SBC_TRUE@sbc_libsbc_la_SOURCES = sbc/sbc.h sbc/sbc.c sbc/sbc_math.h sbc/sbc_tables.h \
+@SBC_TRUE@                     sbc/sbc_primitives.h sbc/sbc_primitives.c \
+@SBC_TRUE@                     sbc/sbc_primitives_mmx.h sbc/sbc_primitives_mmx.c \
+@SBC_TRUE@                     sbc/sbc_primitives_iwmmxt.h sbc/sbc_primitives_iwmmxt.c \
+@SBC_TRUE@                     sbc/sbc_primitives_neon.h sbc/sbc_primitives_neon.c \
+@SBC_TRUE@                     sbc/sbc_primitives_armv6.h sbc/sbc_primitives_armv6.c
+
+@SBC_TRUE@sbc_libsbc_la_CFLAGS = $(AM_CFLAGS) -finline-functions -fgcse-after-reload \
+@SBC_TRUE@                                     -funswitch-loops -funroll-loops
+
+@SBC_TRUE@sbc_sbcdec_SOURCES = sbc/sbcdec.c sbc/formats.h
+@SBC_TRUE@sbc_sbcdec_LDADD = sbc/libsbc.la
+@SBC_TRUE@sbc_sbcenc_SOURCES = sbc/sbcenc.c sbc/formats.h
+@SBC_TRUE@sbc_sbcenc_LDADD = sbc/libsbc.la
+@SBC_TRUE@@SNDFILE_TRUE@sbc_sbctester_LDADD = @SNDFILE_LIBS@ -lm
+@SBC_TRUE@@SNDFILE_TRUE@sbc_sbctest_CFLAGS = $(AM_CFLAGS) @SNDFILE_CFLAGS@
+attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
+               attrib/gatt.h attrib/gatt.c \
+               attrib/gattrib.h attrib/gattrib.c attrib/client.h \
+               attrib/client.c attrib/gatt-service.h attrib/gatt-service.c
+
+gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
+                                       gdbus/object.c gdbus/polkit.c
+
+btio_sources = btio/btio.h btio/btio.c
+builtin_modules = $(am__append_5) $(am__append_7) $(am__append_11) \
+       $(am__append_15) $(am__append_17) $(am__append_19) \
+       $(am__append_21) $(am__append_23) $(am__append_25) hciops \
+       mgmtops $(am__append_27) $(am__append_29) storage adaptername \
+       $(am__append_31) $(am__append_33) $(am__append_35)
+builtin_sources = $(am__append_6) $(am__append_8) $(am__append_12) \
+       $(am__append_16) $(am__append_18) $(am__append_20) \
+       $(am__append_22) $(am__append_24) $(am__append_26) \
+       plugins/hciops.c plugins/mgmtops.c $(am__append_28) \
+       $(am__append_30) plugins/storage.c plugins/adaptername.c \
+       $(am__append_32) $(am__append_34) $(am__append_36)
+builtin_nodist = $(am__append_9) $(am__append_13)
+mcap_sources = $(am__append_4)
+@AUDIOPLUGIN_TRUE@audio_libtelephony_a_SOURCES = audio/telephony.h audio/telephony-dummy.c \
+@AUDIOPLUGIN_TRUE@                             audio/telephony-maemo5.c audio/telephony-ofono.c \
+@AUDIOPLUGIN_TRUE@                             audio/telephony-maemo6.c
+
+@SAPPLUGIN_TRUE@sap_libsap_a_SOURCES = sap/sap.h sap/sap-dummy.c sap/sap-u8500.c
+@MAINTAINER_MODE_TRUE@plugins_external_dummy_la_SOURCES = plugins/external-dummy.c
+@MAINTAINER_MODE_TRUE@plugins_external_dummy_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+@MAINTAINER_MODE_TRUE@                             -no-undefined
+
+@MAINTAINER_MODE_TRUE@plugins_external_dummy_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
+src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
+                       $(attrib_sources) $(btio_sources) \
+                       $(mcap_sources) src/bluetooth.ver \
+                       src/main.c src/log.h src/log.c \
+                       src/rfkill.c src/hcid.h src/sdpd.h \
+                       src/sdpd-server.c src/sdpd-request.c \
+                       src/sdpd-service.c src/sdpd-database.c \
+                       src/attrib-server.h src/attrib-server.c \
+                       src/sdp-xml.h src/sdp-xml.c \
+                       src/sdp-client.h src/sdp-client.c \
+                       src/textfile.h src/textfile.c \
+                       src/glib-helper.h src/glib-helper.c \
+                       src/oui.h src/oui.c src/uinput.h src/ppoll.h \
+                       src/plugin.h src/plugin.c \
+                       src/storage.h src/storage.c \
+                       src/agent.h src/agent.c \
+                       src/error.h src/error.c \
+                       src/manager.h src/manager.c \
+                       src/adapter.h src/adapter.c \
+                       src/device.h src/device.c src/attio.h \
+                       src/dbus-common.c src/dbus-common.h \
+                       src/event.h src/event.c \
+                       src/oob.h src/oob.c src/eir.h src/eir.c
+
+src_bluetoothd_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @DBUS_LIBS@ \
+                                                               -ldl -lrt
+
+src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
+                               -Wl,--version-script=$(srcdir)/src/bluetooth.ver
+
+src_bluetoothd_DEPENDENCIES = lib/libbluetooth-private.la
+src_bluetoothd_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_PLUGIN_BUILTIN \
+                                       -DPLUGINDIR=\""$(build_plugindir)"\"
+
+src_bluetoothd_SHORTNAME = bluetoothd
+builtin_files = src/builtin.h $(builtin_nodist)
+nodist_src_bluetoothd_SOURCES = $(builtin_files)
+man_MANS = src/bluetoothd.8
+@ALSA_TRUE@alsadir = $(libdir)/alsa-lib
+@ALSA_TRUE@alsa_LTLIBRARIES = audio/libasound_module_pcm_bluetooth.la \
+@ALSA_TRUE@                            audio/libasound_module_ctl_bluetooth.la
+
+@ALSA_TRUE@audio_libasound_module_pcm_bluetooth_la_SOURCES = audio/pcm_bluetooth.c \
+@ALSA_TRUE@                                    audio/rtp.h audio/ipc.h audio/ipc.c
+
+@ALSA_TRUE@audio_libasound_module_pcm_bluetooth_la_LDFLAGS = $(AM_LDFLAGS) -module \
+@ALSA_TRUE@                                              -avoid-version
+
+@ALSA_TRUE@audio_libasound_module_pcm_bluetooth_la_LIBADD = sbc/libsbc.la \
+@ALSA_TRUE@                                    lib/libbluetooth-private.la @ALSA_LIBS@
+
+@ALSA_TRUE@audio_libasound_module_pcm_bluetooth_la_CFLAGS = $(AM_CFLAGS) @ALSA_CFLAGS@
+@ALSA_TRUE@audio_libasound_module_ctl_bluetooth_la_SOURCES = audio/ctl_bluetooth.c \
+@ALSA_TRUE@                                    audio/rtp.h audio/ipc.h audio/ipc.c
+
+@ALSA_TRUE@audio_libasound_module_ctl_bluetooth_la_LDFLAGS = $(AM_LDFLAGS) -module \
+@ALSA_TRUE@                                              -avoid-version
+
+@ALSA_TRUE@audio_libasound_module_ctl_bluetooth_la_LIBADD = \
+@ALSA_TRUE@                                    lib/libbluetooth-private.la @ALSA_LIBS@
+
+@ALSA_TRUE@audio_libasound_module_ctl_bluetooth_la_CFLAGS = $(AM_CFLAGS) @ALSA_CFLAGS@
+@ALSA_TRUE@@DATAFILES_TRUE@alsaconfdir = $(datadir)/alsa
+@ALSA_TRUE@@DATAFILES_TRUE@alsaconf_DATA = audio/bluetooth.conf
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@gstreamerdir = $(libdir)/gstreamer-0.10
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@gstreamer_LTLIBRARIES = audio/libgstbluetooth.la
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@audio_libgstbluetooth_la_SOURCES = audio/gstbluetooth.c audio/gstpragma.h \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                             audio/gstsbcenc.h audio/gstsbcenc.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                             audio/gstsbcdec.h audio/gstsbcdec.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                             audio/gstsbcparse.h audio/gstsbcparse.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                             audio/gstavdtpsink.h audio/gstavdtpsink.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                             audio/gsta2dpsink.h audio/gsta2dpsink.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                             audio/gstsbcutil.h audio/gstsbcutil.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                             audio/gstrtpsbcpay.h audio/gstrtpsbcpay.c \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                             audio/rtp.h audio/ipc.h audio/ipc.c
+
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@audio_libgstbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@audio_libgstbluetooth_la_LIBADD = sbc/libsbc.la lib/libbluetooth-private.la \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                                             @DBUS_LIBS@ @GSTREAMER_LIBS@ \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                                             -lgstaudio-0.10 -lgstrtp-0.10
+
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@audio_libgstbluetooth_la_CFLAGS = -fvisibility=hidden -fno-strict-aliasing \
+@AUDIOPLUGIN_TRUE@@GSTREAMER_TRUE@                               $(AM_CFLAGS) @DBUS_CFLAGS@ @GSTREAMER_CFLAGS@
+
+@TOOLS_TRUE@tools_rfcomm_SOURCES = tools/rfcomm.c tools/parser.y tools/lexer.l \
+@TOOLS_TRUE@                                   tools/kword.h tools/kword.c
+
+@TOOLS_TRUE@EXTRA_tools_rfcomm_SOURCES = tools/parser.h tools/parser.c \
+@TOOLS_TRUE@                                                   tools/lexer.c
+
+@TOOLS_TRUE@tools_rfcomm_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@tools_l2ping_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
+@TOOLS_TRUE@                                           tools/hciattach_st.c \
+@TOOLS_TRUE@                                           tools/hciattach_ti.c \
+@TOOLS_TRUE@                                           tools/hciattach_tialt.c \
+@TOOLS_TRUE@                                           tools/hciattach_ath3k.c \
+@TOOLS_TRUE@                                           tools/hciattach_qualcomm.c \
+@TOOLS_TRUE@                                           tools/hciattach_intel.c
+
+@TOOLS_TRUE@tools_hciattach_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c \
+@TOOLS_TRUE@                                           src/textfile.h src/textfile.c
+
+@TOOLS_TRUE@tools_hciconfig_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c \
+@TOOLS_TRUE@                                           src/textfile.h src/textfile.c
+
+@TOOLS_TRUE@tools_hcitool_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
+@TOOLS_TRUE@tools_sdptool_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@tools_ciptool_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@tools_avinfo_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@tools_ppporc_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@tools_hcieventmask_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@mgmt_btmgmt_SOURCES = mgmt/main.c src/glib-helper.c
+@TOOLS_TRUE@mgmt_btmgmt_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@
+@TOOLS_TRUE@monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
+@TOOLS_TRUE@                                   monitor/mainloop.h monitor/mainloop.c \
+@TOOLS_TRUE@                                   monitor/hcidump.h monitor/hcidump.c \
+@TOOLS_TRUE@                                   monitor/btsnoop.h monitor/btsnoop.c \
+@TOOLS_TRUE@                                   monitor/control.h monitor/control.c \
+@TOOLS_TRUE@                                   monitor/packet.h monitor/packet.c
+
+@TOOLS_TRUE@monitor_btmon_LDADD = lib/libbluetooth-private.la
+@TOOLS_TRUE@emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
+@TOOLS_TRUE@                                   monitor/mainloop.h monitor/mainloop.c \
+@TOOLS_TRUE@                                   emulator/server.h emulator/server.c \
+@TOOLS_TRUE@                                   emulator/vhci.h emulator/vhci.c \
+@TOOLS_TRUE@                                   emulator/btdev.h emulator/btdev.c
+
+@READLINE_TRUE@@TOOLS_TRUE@attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
+@READLINE_TRUE@@TOOLS_TRUE@                            attrib/gattrib.c btio/btio.c \
+@READLINE_TRUE@@TOOLS_TRUE@                            attrib/gatttool.h attrib/interactive.c \
+@READLINE_TRUE@@TOOLS_TRUE@                            attrib/utils.c src/log.c
+
+@READLINE_TRUE@@TOOLS_TRUE@attrib_gatttool_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @READLINE_LIBS@
+@BCCMD_TRUE@tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h \
+@BCCMD_TRUE@   tools/csr.c tools/csr_hci.c tools/csr_h4.c \
+@BCCMD_TRUE@   tools/csr_3wire.c tools/csr_bcsp.c tools/ubcsp.h \
+@BCCMD_TRUE@   tools/ubcsp.c $(am__append_46)
+@BCCMD_TRUE@tools_bccmd_LDADD = lib/libbluetooth-private.la \
+@BCCMD_TRUE@   $(am__append_47)
+@DATAFILES_TRUE@@PCMCIA_TRUE@udevdir = @UDEV_DIR@
+@HID2HCI_TRUE@udevdir = @UDEV_DIR@
+@HID2HCI_TRUE@tools_hid2hci_LDADD = @USB_LIBS@ @UDEV_LIBS@
+@DFUTOOL_TRUE@tools_dfutool_SOURCES = tools/dfutool.c tools/dfu.h tools/dfu.c
+@DFUTOOL_TRUE@tools_dfutool_LDADD = @USB_LIBS@
+@USB_TRUE@tools_dfubabel_LDADD = @USB_LIBS@
+@USB_TRUE@tools_avctrl_LDADD = @USB_LIBS@
+@CUPS_TRUE@cupsdir = $(libdir)/cups/backend
+@CUPS_TRUE@cups_bluetooth_SOURCES = $(gdbus_sources) cups/main.c cups/cups.h \
+@CUPS_TRUE@                                    cups/sdp.c cups/spp.c cups/hcrp.c
+
+@CUPS_TRUE@cups_bluetooth_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ lib/libbluetooth-private.la
+@TEST_TRUE@test_hciemu_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_l2test_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_rctest_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_gaptest_LDADD = @DBUS_LIBS@
+@TEST_TRUE@test_sdptest_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_scotest_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_attest_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_hstest_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_avtest_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_lmptest_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_ipctest_SOURCES = test/ipctest.c audio/ipc.h audio/ipc.c
+@TEST_TRUE@test_ipctest_LDADD = @GLIB_LIBS@ sbc/libsbc.la
+@TEST_TRUE@test_bdaddr_SOURCES = test/bdaddr.c src/oui.h src/oui.c
+@TEST_TRUE@test_bdaddr_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_agent_LDADD = @DBUS_LIBS@
+@TEST_TRUE@test_btiotest_SOURCES = test/btiotest.c btio/btio.h btio/btio.c
+@TEST_TRUE@test_btiotest_LDADD = @GLIB_LIBS@ lib/libbluetooth-private.la
+@TEST_TRUE@test_uuidtest_SOURCES = test/uuidtest.c
+@TEST_TRUE@test_uuidtest_LDADD = lib/libbluetooth-private.la
+@TEST_TRUE@test_mpris_player_SOURCES = test/mpris-player.c
+@TEST_TRUE@test_mpris_player_LDADD = @DBUS_LIBS@ @GLIB_LIBS@
+@TEST_TRUE@test_test_textfile_SOURCES = test/test-textfile.c src/textfile.h src/textfile.c
+@HIDD_TRUE@compat_hidd_SOURCES = compat/hidd.c compat/hidd.h src/uinput.h \
+@HIDD_TRUE@                            compat/sdp.h compat/sdp.c compat/fakehid.c \
+@HIDD_TRUE@                                            src/textfile.h src/textfile.c
+
+@HIDD_TRUE@compat_hidd_LDADD = -lm lib/libbluetooth-private.la
+@PAND_TRUE@compat_pand_SOURCES = compat/pand.c compat/pand.h \
+@PAND_TRUE@                            compat/bnep.c compat/sdp.h compat/sdp.c \
+@PAND_TRUE@                                            src/textfile.h src/textfile.c
+
+@PAND_TRUE@compat_pand_LDADD = lib/libbluetooth-private.la
+@DUND_TRUE@compat_dund_SOURCES = compat/dund.c compat/dund.h compat/lib.h \
+@DUND_TRUE@                    compat/sdp.h compat/sdp.c compat/dun.c compat/msdun.c \
+@DUND_TRUE@                                            src/textfile.h src/textfile.c
+
+@DUND_TRUE@compat_dund_LDADD = lib/libbluetooth-private.la
+@DATAFILES_TRUE@rulesdir = @UDEV_DIR@/rules.d
+@DATAFILES_TRUE@udev_files = $(am__append_71) $(am__append_72)
+@DATAFILES_TRUE@@PCMCIA_TRUE@dist_udev_SCRIPTS = scripts/bluetooth_serial
+@DATAFILES_TRUE@rules_DATA = $(foreach file,$(udev_files), scripts/97-$(notdir $(file)))
+AM_YFLAGS = -d
+INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
+       -I$(srcdir)/audio -I$(srcdir)/sbc -I$(srcdir)/gdbus \
+       -I$(srcdir)/attrib -I$(srcdir)/btio -I$(srcdir)/tools \
+       -I$(builddir)/tools -I$(srcdir)/monitor $(am__append_73)
+unit_objects = $(am__append_74)
+@TEST_FALSE@unit_tests = 
+@TEST_TRUE@unit_tests = unit/test-eir
+@TEST_TRUE@unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/glib-helper.c
+@TEST_TRUE@unit_test_eir_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @CHECK_LIBS@
+@TEST_TRUE@unit_test_eir_CFLAGS = $(AM_CFLAGS) @CHECK_CFLAGS@
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = bluez.pc
+DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles
+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
+
+all: $(BUILT_SOURCES) config.h
+       $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .l .lo .o .obj .y
+am--refresh: Makefile
+       @:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.tools $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
+             $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
+               && exit 0; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+       $(am__cd) $(top_srcdir) && \
+         $(AUTOMAKE) --foreign Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           echo ' $(SHELL) ./config.status'; \
+           $(SHELL) ./config.status;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+       esac;
+$(srcdir)/Makefile.tools:
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+       $(am__cd) $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+       $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+       @if test ! -f $@; then rm -f stamp-h1; else :; fi
+       @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) stamp-h1; else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+       @rm -f stamp-h1
+       cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) 
+       ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+       rm -f stamp-h1
+       touch $@
+
+distclean-hdr:
+       -rm -f config.h stamp-h1
+doc/version.xml: $(top_builddir)/config.status $(top_srcdir)/doc/version.xml.in
+       cd $(top_builddir) && $(SHELL) ./config.status $@
+src/bluetoothd.8: $(top_builddir)/config.status $(top_srcdir)/src/bluetoothd.8.in
+       cd $(top_builddir) && $(SHELL) ./config.status $@
+src/bluetooth.service: $(top_builddir)/config.status $(top_srcdir)/src/bluetooth.service.in
+       cd $(top_builddir) && $(SHELL) ./config.status $@
+bluez.pc: $(top_builddir)/config.status $(srcdir)/bluez.pc.in
+       cd $(top_builddir) && $(SHELL) ./config.status $@
+
+clean-noinstLIBRARIES:
+       -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+audio/$(am__dirstamp):
+       @$(MKDIR_P) audio
+       @: > audio/$(am__dirstamp)
+audio/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) audio/$(DEPDIR)
+       @: > audio/$(DEPDIR)/$(am__dirstamp)
+audio/telephony-dummy.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/telephony-maemo5.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/telephony-ofono.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/telephony-maemo6.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/libtelephony.a: $(audio_libtelephony_a_OBJECTS) $(audio_libtelephony_a_DEPENDENCIES) $(EXTRA_audio_libtelephony_a_DEPENDENCIES) audio/$(am__dirstamp)
+       $(AM_V_at)-rm -f audio/libtelephony.a
+       $(AM_V_AR)$(audio_libtelephony_a_AR) audio/libtelephony.a $(audio_libtelephony_a_OBJECTS) $(audio_libtelephony_a_LIBADD)
+       $(AM_V_at)$(RANLIB) audio/libtelephony.a
+sap/$(am__dirstamp):
+       @$(MKDIR_P) sap
+       @: > sap/$(am__dirstamp)
+sap/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) sap/$(DEPDIR)
+       @: > sap/$(DEPDIR)/$(am__dirstamp)
+sap/sap-dummy.$(OBJEXT): sap/$(am__dirstamp) \
+       sap/$(DEPDIR)/$(am__dirstamp)
+sap/sap-u8500.$(OBJEXT): sap/$(am__dirstamp) \
+       sap/$(DEPDIR)/$(am__dirstamp)
+sap/libsap.a: $(sap_libsap_a_OBJECTS) $(sap_libsap_a_DEPENDENCIES) $(EXTRA_sap_libsap_a_DEPENDENCIES) sap/$(am__dirstamp)
+       $(AM_V_at)-rm -f sap/libsap.a
+       $(AM_V_AR)$(sap_libsap_a_AR) sap/libsap.a $(sap_libsap_a_OBJECTS) $(sap_libsap_a_LIBADD)
+       $(AM_V_at)$(RANLIB) sap/libsap.a
+install-alsaLTLIBRARIES: $(alsa_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(alsadir)" || $(MKDIR_P) "$(DESTDIR)$(alsadir)"
+       @list='$(alsa_LTLIBRARIES)'; test -n "$(alsadir)" || list=; \
+       list2=; for p in $$list; do \
+         if test -f $$p; then \
+           list2="$$list2 $$p"; \
+         else :; fi; \
+       done; \
+       test -z "$$list2" || { \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(alsadir)'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(alsadir)"; \
+       }
+
+uninstall-alsaLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(alsa_LTLIBRARIES)'; test -n "$(alsadir)" || list=; \
+       for p in $$list; do \
+         $(am__strip_dir) \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(alsadir)/$$f'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(alsadir)/$$f"; \
+       done
+
+clean-alsaLTLIBRARIES:
+       -test -z "$(alsa_LTLIBRARIES)" || rm -f $(alsa_LTLIBRARIES)
+       @list='$(alsa_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+install-gstreamerLTLIBRARIES: $(gstreamer_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(gstreamerdir)" || $(MKDIR_P) "$(DESTDIR)$(gstreamerdir)"
+       @list='$(gstreamer_LTLIBRARIES)'; test -n "$(gstreamerdir)" || list=; \
+       list2=; for p in $$list; do \
+         if test -f $$p; then \
+           list2="$$list2 $$p"; \
+         else :; fi; \
+       done; \
+       test -z "$$list2" || { \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(gstreamerdir)'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(gstreamerdir)"; \
+       }
+
+uninstall-gstreamerLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(gstreamer_LTLIBRARIES)'; test -n "$(gstreamerdir)" || list=; \
+       for p in $$list; do \
+         $(am__strip_dir) \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(gstreamerdir)/$$f'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(gstreamerdir)/$$f"; \
+       done
+
+clean-gstreamerLTLIBRARIES:
+       -test -z "$(gstreamer_LTLIBRARIES)" || rm -f $(gstreamer_LTLIBRARIES)
+       @list='$(gstreamer_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+       @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+       list2=; for p in $$list; do \
+         if test -f $$p; then \
+           list2="$$list2 $$p"; \
+         else :; fi; \
+       done; \
+       test -z "$$list2" || { \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+       }
+
+uninstall-libLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+       for p in $$list; do \
+         $(am__strip_dir) \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+       done
+
+clean-libLTLIBRARIES:
+       -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+       @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+
+clean-noinstLTLIBRARIES:
+       -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+       @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)"
+       @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+       list2=; for p in $$list; do \
+         if test -f $$p; then \
+           list2="$$list2 $$p"; \
+         else :; fi; \
+       done; \
+       test -z "$$list2" || { \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \
+       }
+
+uninstall-pluginLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+       for p in $$list; do \
+         $(am__strip_dir) \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \
+       done
+
+clean-pluginLTLIBRARIES:
+       -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES)
+       @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo:  \
+       audio/$(am__dirstamp) audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo:  \
+       audio/$(am__dirstamp) audio/$(DEPDIR)/$(am__dirstamp)
+audio/libasound_module_ctl_bluetooth.la: $(audio_libasound_module_ctl_bluetooth_la_OBJECTS) $(audio_libasound_module_ctl_bluetooth_la_DEPENDENCIES) $(EXTRA_audio_libasound_module_ctl_bluetooth_la_DEPENDENCIES) audio/$(am__dirstamp)
+       $(AM_V_CCLD)$(audio_libasound_module_ctl_bluetooth_la_LINK) $(am_audio_libasound_module_ctl_bluetooth_la_rpath) $(audio_libasound_module_ctl_bluetooth_la_OBJECTS) $(audio_libasound_module_ctl_bluetooth_la_LIBADD) $(LIBS)
+audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo:  \
+       audio/$(am__dirstamp) audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo:  \
+       audio/$(am__dirstamp) audio/$(DEPDIR)/$(am__dirstamp)
+audio/libasound_module_pcm_bluetooth.la: $(audio_libasound_module_pcm_bluetooth_la_OBJECTS) $(audio_libasound_module_pcm_bluetooth_la_DEPENDENCIES) $(EXTRA_audio_libasound_module_pcm_bluetooth_la_DEPENDENCIES) audio/$(am__dirstamp)
+       $(AM_V_CCLD)$(audio_libasound_module_pcm_bluetooth_la_LINK) $(am_audio_libasound_module_pcm_bluetooth_la_rpath) $(audio_libasound_module_pcm_bluetooth_la_OBJECTS) $(audio_libasound_module_pcm_bluetooth_la_LIBADD) $(LIBS)
+audio/audio_libgstbluetooth_la-gstbluetooth.lo: audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstsbcenc.lo: audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstsbcdec.lo: audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstsbcparse.lo: audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstavdtpsink.lo: audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gsta2dpsink.lo: audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstsbcutil.lo: audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo: audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/audio_libgstbluetooth_la-ipc.lo: audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/libgstbluetooth.la: $(audio_libgstbluetooth_la_OBJECTS) $(audio_libgstbluetooth_la_DEPENDENCIES) $(EXTRA_audio_libgstbluetooth_la_DEPENDENCIES) audio/$(am__dirstamp)
+       $(AM_V_CCLD)$(audio_libgstbluetooth_la_LINK) $(am_audio_libgstbluetooth_la_rpath) $(audio_libgstbluetooth_la_OBJECTS) $(audio_libgstbluetooth_la_LIBADD) $(LIBS)
+lib/$(am__dirstamp):
+       @$(MKDIR_P) lib
+       @: > lib/$(am__dirstamp)
+lib/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) lib/$(DEPDIR)
+       @: > lib/$(DEPDIR)/$(am__dirstamp)
+lib/bluetooth.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/hci.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/sdp.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/uuid.lo: lib/$(am__dirstamp) lib/$(DEPDIR)/$(am__dirstamp)
+lib/libbluetooth-private.la: $(lib_libbluetooth_private_la_OBJECTS) $(lib_libbluetooth_private_la_DEPENDENCIES) $(EXTRA_lib_libbluetooth_private_la_DEPENDENCIES) lib/$(am__dirstamp)
+       $(AM_V_CCLD)$(LINK)  $(lib_libbluetooth_private_la_OBJECTS) $(lib_libbluetooth_private_la_LIBADD) $(LIBS)
+lib/libbluetooth.la: $(lib_libbluetooth_la_OBJECTS) $(lib_libbluetooth_la_DEPENDENCIES) $(EXTRA_lib_libbluetooth_la_DEPENDENCIES) lib/$(am__dirstamp)
+       $(AM_V_CCLD)$(lib_libbluetooth_la_LINK) -rpath $(libdir) $(lib_libbluetooth_la_OBJECTS) $(lib_libbluetooth_la_LIBADD) $(LIBS)
+plugins/$(am__dirstamp):
+       @$(MKDIR_P) plugins
+       @: > plugins/$(am__dirstamp)
+plugins/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) plugins/$(DEPDIR)
+       @: > plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/plugins_external_dummy_la-external-dummy.lo:  \
+       plugins/$(am__dirstamp) plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/external-dummy.la: $(plugins_external_dummy_la_OBJECTS) $(plugins_external_dummy_la_DEPENDENCIES) $(EXTRA_plugins_external_dummy_la_DEPENDENCIES) plugins/$(am__dirstamp)
+       $(AM_V_CCLD)$(plugins_external_dummy_la_LINK) $(am_plugins_external_dummy_la_rpath) $(plugins_external_dummy_la_OBJECTS) $(plugins_external_dummy_la_LIBADD) $(LIBS)
+sbc/$(am__dirstamp):
+       @$(MKDIR_P) sbc
+       @: > sbc/$(am__dirstamp)
+sbc/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) sbc/$(DEPDIR)
+       @: > sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc.lo: sbc/$(am__dirstamp) \
+       sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc_primitives.lo: sbc/$(am__dirstamp) \
+       sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc_primitives_mmx.lo: sbc/$(am__dirstamp) \
+       sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo: sbc/$(am__dirstamp) \
+       sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc_primitives_neon.lo: sbc/$(am__dirstamp) \
+       sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbc_libsbc_la-sbc_primitives_armv6.lo: sbc/$(am__dirstamp) \
+       sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/libsbc.la: $(sbc_libsbc_la_OBJECTS) $(sbc_libsbc_la_DEPENDENCIES) $(EXTRA_sbc_libsbc_la_DEPENDENCIES) sbc/$(am__dirstamp)
+       $(AM_V_CCLD)$(sbc_libsbc_la_LINK) $(am_sbc_libsbc_la_rpath) $(sbc_libsbc_la_OBJECTS) $(sbc_libsbc_la_LIBADD) $(LIBS)
+install-binPROGRAMS: $(bin_PROGRAMS)
+       @$(NORMAL_INSTALL)
+       test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+       @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+       for p in $$list; do echo "$$p $$p"; done | \
+       sed 's/$(EXEEXT)$$//' | \
+       while read p p1; do if test -f $$p || test -f $$p1; \
+         then echo "$$p"; echo "$$p"; else :; fi; \
+       done | \
+       sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+           -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+       sed 'N;N;N;s,\n, ,g' | \
+       $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+         { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+           if ($$2 == $$4) files[d] = files[d] " " $$1; \
+           else { print "f", $$3 "/" $$4, $$1; } } \
+         END { for (d in files) print "f", d, files[d] }' | \
+       while read type dir files; do \
+           if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+           test -z "$$files" || { \
+           echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+           $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+           } \
+       ; done
+
+uninstall-binPROGRAMS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+       files=`for p in $$list; do echo "$$p"; done | \
+         sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+             -e 's/$$/$(EXEEXT)/' `; \
+       test -n "$$list" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+       @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+       echo " rm -f" $$list; \
+       rm -f $$list || exit $$?; \
+       test -n "$(EXEEXT)" || exit 0; \
+       list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+       echo " rm -f" $$list; \
+       rm -f $$list
+install-cupsPROGRAMS: $(cups_PROGRAMS)
+       @$(NORMAL_INSTALL)
+       test -z "$(cupsdir)" || $(MKDIR_P) "$(DESTDIR)$(cupsdir)"
+       @list='$(cups_PROGRAMS)'; test -n "$(cupsdir)" || list=; \
+       for p in $$list; do echo "$$p $$p"; done | \
+       sed 's/$(EXEEXT)$$//' | \
+       while read p p1; do if test -f $$p || test -f $$p1; \
+         then echo "$$p"; echo "$$p"; else :; fi; \
+       done | \
+       sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+           -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+       sed 'N;N;N;s,\n, ,g' | \
+       $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+         { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+           if ($$2 == $$4) files[d] = files[d] " " $$1; \
+           else { print "f", $$3 "/" $$4, $$1; } } \
+         END { for (d in files) print "f", d, files[d] }' | \
+       while read type dir files; do \
+           if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+           test -z "$$files" || { \
+           echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(cupsdir)$$dir'"; \
+           $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(cupsdir)$$dir" || exit $$?; \
+           } \
+       ; done
+
+uninstall-cupsPROGRAMS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(cups_PROGRAMS)'; test -n "$(cupsdir)" || list=; \
+       files=`for p in $$list; do echo "$$p"; done | \
+         sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+             -e 's/$$/$(EXEEXT)/' `; \
+       test -n "$$list" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(cupsdir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(cupsdir)" && rm -f $$files
+
+clean-cupsPROGRAMS:
+       @list='$(cups_PROGRAMS)'; test -n "$$list" || exit 0; \
+       echo " rm -f" $$list; \
+       rm -f $$list || exit $$?; \
+       test -n "$(EXEEXT)" || exit 0; \
+       list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+       echo " rm -f" $$list; \
+       rm -f $$list
+
+clean-noinstPROGRAMS:
+       @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+       echo " rm -f" $$list; \
+       rm -f $$list || exit $$?; \
+       test -n "$(EXEEXT)" || exit 0; \
+       list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+       echo " rm -f" $$list; \
+       rm -f $$list
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+       @$(NORMAL_INSTALL)
+       test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+       @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+       for p in $$list; do echo "$$p $$p"; done | \
+       sed 's/$(EXEEXT)$$//' | \
+       while read p p1; do if test -f $$p || test -f $$p1; \
+         then echo "$$p"; echo "$$p"; else :; fi; \
+       done | \
+       sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+           -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+       sed 'N;N;N;s,\n, ,g' | \
+       $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+         { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+           if ($$2 == $$4) files[d] = files[d] " " $$1; \
+           else { print "f", $$3 "/" $$4, $$1; } } \
+         END { for (d in files) print "f", d, files[d] }' | \
+       while read type dir files; do \
+           if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+           test -z "$$files" || { \
+           echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+           $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+           } \
+       ; done
+
+uninstall-sbinPROGRAMS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+       files=`for p in $$list; do echo "$$p"; done | \
+         sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+             -e 's/$$/$(EXEEXT)/' `; \
+       test -n "$$list" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+       @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+       echo " rm -f" $$list; \
+       rm -f $$list || exit $$?; \
+       test -n "$(EXEEXT)" || exit 0; \
+       list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+       echo " rm -f" $$list; \
+       rm -f $$list
+install-udevPROGRAMS: $(udev_PROGRAMS)
+       @$(NORMAL_INSTALL)
+       test -z "$(udevdir)" || $(MKDIR_P) "$(DESTDIR)$(udevdir)"
+       @list='$(udev_PROGRAMS)'; test -n "$(udevdir)" || list=; \
+       for p in $$list; do echo "$$p $$p"; done | \
+       sed 's/$(EXEEXT)$$//' | \
+       while read p p1; do if test -f $$p || test -f $$p1; \
+         then echo "$$p"; echo "$$p"; else :; fi; \
+       done | \
+       sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+           -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+       sed 'N;N;N;s,\n, ,g' | \
+       $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+         { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+           if ($$2 == $$4) files[d] = files[d] " " $$1; \
+           else { print "f", $$3 "/" $$4, $$1; } } \
+         END { for (d in files) print "f", d, files[d] }' | \
+       while read type dir files; do \
+           if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+           test -z "$$files" || { \
+           echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(udevdir)$$dir'"; \
+           $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(udevdir)$$dir" || exit $$?; \
+           } \
+       ; done
+
+uninstall-udevPROGRAMS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(udev_PROGRAMS)'; test -n "$(udevdir)" || list=; \
+       files=`for p in $$list; do echo "$$p"; done | \
+         sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+             -e 's/$$/$(EXEEXT)/' `; \
+       test -n "$$list" || exit 0; \
+       echo " ( cd '$(DESTDIR)$(udevdir)' && rm -f" $$files ")"; \
+       cd "$(DESTDIR)$(udevdir)" && rm -f $$files
+
+clean-udevPROGRAMS:
+       @list='$(udev_PROGRAMS)'; test -n "$$list" || exit 0; \
+       echo " rm -f" $$list; \
+       rm -f $$list || exit $$?; \
+       test -n "$(EXEEXT)" || exit 0; \
+       list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+       echo " rm -f" $$list; \
+       rm -f $$list
+attrib/$(am__dirstamp):
+       @$(MKDIR_P) attrib
+       @: > attrib/$(am__dirstamp)
+attrib/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) attrib/$(DEPDIR)
+       @: > attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/gatttool.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/att.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/gatt.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/gattrib.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+btio/$(am__dirstamp):
+       @$(MKDIR_P) btio
+       @: > btio/$(am__dirstamp)
+btio/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) btio/$(DEPDIR)
+       @: > btio/$(DEPDIR)/$(am__dirstamp)
+btio/btio.$(OBJEXT): btio/$(am__dirstamp) \
+       btio/$(DEPDIR)/$(am__dirstamp)
+attrib/interactive.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/utils.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+src/$(am__dirstamp):
+       @$(MKDIR_P) src
+       @: > src/$(am__dirstamp)
+src/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) src/$(DEPDIR)
+       @: > src/$(DEPDIR)/$(am__dirstamp)
+src/log.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+attrib/gatttool$(EXEEXT): $(attrib_gatttool_OBJECTS) $(attrib_gatttool_DEPENDENCIES) $(EXTRA_attrib_gatttool_DEPENDENCIES) attrib/$(am__dirstamp)
+       @rm -f attrib/gatttool$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(attrib_gatttool_OBJECTS) $(attrib_gatttool_LDADD) $(LIBS)
+compat/$(am__dirstamp):
+       @$(MKDIR_P) compat
+       @: > compat/$(am__dirstamp)
+compat/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) compat/$(DEPDIR)
+       @: > compat/$(DEPDIR)/$(am__dirstamp)
+compat/dund.$(OBJEXT): compat/$(am__dirstamp) \
+       compat/$(DEPDIR)/$(am__dirstamp)
+compat/sdp.$(OBJEXT): compat/$(am__dirstamp) \
+       compat/$(DEPDIR)/$(am__dirstamp)
+compat/dun.$(OBJEXT): compat/$(am__dirstamp) \
+       compat/$(DEPDIR)/$(am__dirstamp)
+compat/msdun.$(OBJEXT): compat/$(am__dirstamp) \
+       compat/$(DEPDIR)/$(am__dirstamp)
+src/textfile.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+compat/dund$(EXEEXT): $(compat_dund_OBJECTS) $(compat_dund_DEPENDENCIES) $(EXTRA_compat_dund_DEPENDENCIES) compat/$(am__dirstamp)
+       @rm -f compat/dund$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(compat_dund_OBJECTS) $(compat_dund_LDADD) $(LIBS)
+compat/hidd.$(OBJEXT): compat/$(am__dirstamp) \
+       compat/$(DEPDIR)/$(am__dirstamp)
+compat/fakehid.$(OBJEXT): compat/$(am__dirstamp) \
+       compat/$(DEPDIR)/$(am__dirstamp)
+compat/hidd$(EXEEXT): $(compat_hidd_OBJECTS) $(compat_hidd_DEPENDENCIES) $(EXTRA_compat_hidd_DEPENDENCIES) compat/$(am__dirstamp)
+       @rm -f compat/hidd$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(compat_hidd_OBJECTS) $(compat_hidd_LDADD) $(LIBS)
+compat/pand.$(OBJEXT): compat/$(am__dirstamp) \
+       compat/$(DEPDIR)/$(am__dirstamp)
+compat/bnep.$(OBJEXT): compat/$(am__dirstamp) \
+       compat/$(DEPDIR)/$(am__dirstamp)
+compat/pand$(EXEEXT): $(compat_pand_OBJECTS) $(compat_pand_DEPENDENCIES) $(EXTRA_compat_pand_DEPENDENCIES) compat/$(am__dirstamp)
+       @rm -f compat/pand$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(compat_pand_OBJECTS) $(compat_pand_LDADD) $(LIBS)
+gdbus/$(am__dirstamp):
+       @$(MKDIR_P) gdbus
+       @: > gdbus/$(am__dirstamp)
+gdbus/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) gdbus/$(DEPDIR)
+       @: > gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/mainloop.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/watch.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/object.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/polkit.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+cups/$(am__dirstamp):
+       @$(MKDIR_P) cups
+       @: > cups/$(am__dirstamp)
+cups/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) cups/$(DEPDIR)
+       @: > cups/$(DEPDIR)/$(am__dirstamp)
+cups/main.$(OBJEXT): cups/$(am__dirstamp) \
+       cups/$(DEPDIR)/$(am__dirstamp)
+cups/sdp.$(OBJEXT): cups/$(am__dirstamp) \
+       cups/$(DEPDIR)/$(am__dirstamp)
+cups/spp.$(OBJEXT): cups/$(am__dirstamp) \
+       cups/$(DEPDIR)/$(am__dirstamp)
+cups/hcrp.$(OBJEXT): cups/$(am__dirstamp) \
+       cups/$(DEPDIR)/$(am__dirstamp)
+cups/bluetooth$(EXEEXT): $(cups_bluetooth_OBJECTS) $(cups_bluetooth_DEPENDENCIES) $(EXTRA_cups_bluetooth_DEPENDENCIES) cups/$(am__dirstamp)
+       @rm -f cups/bluetooth$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(cups_bluetooth_OBJECTS) $(cups_bluetooth_LDADD) $(LIBS)
+emulator/$(am__dirstamp):
+       @$(MKDIR_P) emulator
+       @: > emulator/$(am__dirstamp)
+emulator/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) emulator/$(DEPDIR)
+       @: > emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/main.$(OBJEXT): emulator/$(am__dirstamp) \
+       emulator/$(DEPDIR)/$(am__dirstamp)
+monitor/$(am__dirstamp):
+       @$(MKDIR_P) monitor
+       @: > monitor/$(am__dirstamp)
+monitor/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) monitor/$(DEPDIR)
+       @: > monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/mainloop.$(OBJEXT): monitor/$(am__dirstamp) \
+       monitor/$(DEPDIR)/$(am__dirstamp)
+emulator/server.$(OBJEXT): emulator/$(am__dirstamp) \
+       emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/vhci.$(OBJEXT): emulator/$(am__dirstamp) \
+       emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/btdev.$(OBJEXT): emulator/$(am__dirstamp) \
+       emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/btvirt$(EXEEXT): $(emulator_btvirt_OBJECTS) $(emulator_btvirt_DEPENDENCIES) $(EXTRA_emulator_btvirt_DEPENDENCIES) emulator/$(am__dirstamp)
+       @rm -f emulator/btvirt$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(emulator_btvirt_OBJECTS) $(emulator_btvirt_LDADD) $(LIBS)
+mgmt/$(am__dirstamp):
+       @$(MKDIR_P) mgmt
+       @: > mgmt/$(am__dirstamp)
+mgmt/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) mgmt/$(DEPDIR)
+       @: > mgmt/$(DEPDIR)/$(am__dirstamp)
+mgmt/main.$(OBJEXT): mgmt/$(am__dirstamp) \
+       mgmt/$(DEPDIR)/$(am__dirstamp)
+src/glib-helper.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+mgmt/btmgmt$(EXEEXT): $(mgmt_btmgmt_OBJECTS) $(mgmt_btmgmt_DEPENDENCIES) $(EXTRA_mgmt_btmgmt_DEPENDENCIES) mgmt/$(am__dirstamp)
+       @rm -f mgmt/btmgmt$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(mgmt_btmgmt_OBJECTS) $(mgmt_btmgmt_LDADD) $(LIBS)
+monitor/main.$(OBJEXT): monitor/$(am__dirstamp) \
+       monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/hcidump.$(OBJEXT): monitor/$(am__dirstamp) \
+       monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/btsnoop.$(OBJEXT): monitor/$(am__dirstamp) \
+       monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/control.$(OBJEXT): monitor/$(am__dirstamp) \
+       monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/packet.$(OBJEXT): monitor/$(am__dirstamp) \
+       monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/btmon$(EXEEXT): $(monitor_btmon_OBJECTS) $(monitor_btmon_DEPENDENCIES) $(EXTRA_monitor_btmon_DEPENDENCIES) monitor/$(am__dirstamp)
+       @rm -f monitor/btmon$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(monitor_btmon_OBJECTS) $(monitor_btmon_LDADD) $(LIBS)
+sbc/sbcdec.$(OBJEXT): sbc/$(am__dirstamp) \
+       sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbcdec$(EXEEXT): $(sbc_sbcdec_OBJECTS) $(sbc_sbcdec_DEPENDENCIES) $(EXTRA_sbc_sbcdec_DEPENDENCIES) sbc/$(am__dirstamp)
+       @rm -f sbc/sbcdec$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(sbc_sbcdec_OBJECTS) $(sbc_sbcdec_LDADD) $(LIBS)
+sbc/sbcenc.$(OBJEXT): sbc/$(am__dirstamp) \
+       sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbcenc$(EXEEXT): $(sbc_sbcenc_OBJECTS) $(sbc_sbcenc_DEPENDENCIES) $(EXTRA_sbc_sbcenc_DEPENDENCIES) sbc/$(am__dirstamp)
+       @rm -f sbc/sbcenc$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(sbc_sbcenc_OBJECTS) $(sbc_sbcenc_LDADD) $(LIBS)
+sbc/sbcinfo.$(OBJEXT): sbc/$(am__dirstamp) \
+       sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbcinfo$(EXEEXT): $(sbc_sbcinfo_OBJECTS) $(sbc_sbcinfo_DEPENDENCIES) $(EXTRA_sbc_sbcinfo_DEPENDENCIES) sbc/$(am__dirstamp)
+       @rm -f sbc/sbcinfo$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(sbc_sbcinfo_OBJECTS) $(sbc_sbcinfo_LDADD) $(LIBS)
+sbc/sbctester.$(OBJEXT): sbc/$(am__dirstamp) \
+       sbc/$(DEPDIR)/$(am__dirstamp)
+sbc/sbctester$(EXEEXT): $(sbc_sbctester_OBJECTS) $(sbc_sbctester_DEPENDENCIES) $(EXTRA_sbc_sbctester_DEPENDENCIES) sbc/$(am__dirstamp)
+       @rm -f sbc/sbctester$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(sbc_sbctester_OBJECTS) $(sbc_sbctester_LDADD) $(LIBS)
+gdbus/bluetoothd-mainloop.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/bluetoothd-watch.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/bluetoothd-object.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+gdbus/bluetoothd-polkit.$(OBJEXT): gdbus/$(am__dirstamp) \
+       gdbus/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-pnat.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-main.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-manager.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-gateway.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-headset.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-control.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-avctp.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-avrcp.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-device.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-source.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-sink.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-a2dp.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-avdtp.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-ipc.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-unix.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-media.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-transport.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+sap/bluetoothd-main.$(OBJEXT): sap/$(am__dirstamp) \
+       sap/$(DEPDIR)/$(am__dirstamp)
+sap/bluetoothd-manager.$(OBJEXT): sap/$(am__dirstamp) \
+       sap/$(DEPDIR)/$(am__dirstamp)
+sap/bluetoothd-server.$(OBJEXT): sap/$(am__dirstamp) \
+       sap/$(DEPDIR)/$(am__dirstamp)
+input/$(am__dirstamp):
+       @$(MKDIR_P) input
+       @: > input/$(am__dirstamp)
+input/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) input/$(DEPDIR)
+       @: > input/$(DEPDIR)/$(am__dirstamp)
+input/bluetoothd-main.$(OBJEXT): input/$(am__dirstamp) \
+       input/$(DEPDIR)/$(am__dirstamp)
+input/bluetoothd-manager.$(OBJEXT): input/$(am__dirstamp) \
+       input/$(DEPDIR)/$(am__dirstamp)
+input/bluetoothd-server.$(OBJEXT): input/$(am__dirstamp) \
+       input/$(DEPDIR)/$(am__dirstamp)
+input/bluetoothd-device.$(OBJEXT): input/$(am__dirstamp) \
+       input/$(DEPDIR)/$(am__dirstamp)
+input/bluetoothd-fakehid.$(OBJEXT): input/$(am__dirstamp) \
+       input/$(DEPDIR)/$(am__dirstamp)
+serial/$(am__dirstamp):
+       @$(MKDIR_P) serial
+       @: > serial/$(am__dirstamp)
+serial/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) serial/$(DEPDIR)
+       @: > serial/$(DEPDIR)/$(am__dirstamp)
+serial/bluetoothd-main.$(OBJEXT): serial/$(am__dirstamp) \
+       serial/$(DEPDIR)/$(am__dirstamp)
+serial/bluetoothd-manager.$(OBJEXT): serial/$(am__dirstamp) \
+       serial/$(DEPDIR)/$(am__dirstamp)
+serial/bluetoothd-proxy.$(OBJEXT): serial/$(am__dirstamp) \
+       serial/$(DEPDIR)/$(am__dirstamp)
+serial/bluetoothd-port.$(OBJEXT): serial/$(am__dirstamp) \
+       serial/$(DEPDIR)/$(am__dirstamp)
+network/$(am__dirstamp):
+       @$(MKDIR_P) network
+       @: > network/$(am__dirstamp)
+network/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) network/$(DEPDIR)
+       @: > network/$(DEPDIR)/$(am__dirstamp)
+network/bluetoothd-main.$(OBJEXT): network/$(am__dirstamp) \
+       network/$(DEPDIR)/$(am__dirstamp)
+network/bluetoothd-manager.$(OBJEXT): network/$(am__dirstamp) \
+       network/$(DEPDIR)/$(am__dirstamp)
+network/bluetoothd-common.$(OBJEXT): network/$(am__dirstamp) \
+       network/$(DEPDIR)/$(am__dirstamp)
+network/bluetoothd-server.$(OBJEXT): network/$(am__dirstamp) \
+       network/$(DEPDIR)/$(am__dirstamp)
+network/bluetoothd-connection.$(OBJEXT): network/$(am__dirstamp) \
+       network/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-service.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+health/$(am__dirstamp):
+       @$(MKDIR_P) health
+       @: > health/$(am__dirstamp)
+health/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) health/$(DEPDIR)
+       @: > health/$(DEPDIR)/$(am__dirstamp)
+health/bluetoothd-hdp_main.$(OBJEXT): health/$(am__dirstamp) \
+       health/$(DEPDIR)/$(am__dirstamp)
+health/bluetoothd-hdp_manager.$(OBJEXT): health/$(am__dirstamp) \
+       health/$(DEPDIR)/$(am__dirstamp)
+health/bluetoothd-hdp.$(OBJEXT): health/$(am__dirstamp) \
+       health/$(DEPDIR)/$(am__dirstamp)
+health/bluetoothd-hdp_util.$(OBJEXT): health/$(am__dirstamp) \
+       health/$(DEPDIR)/$(am__dirstamp)
+thermometer/$(am__dirstamp):
+       @$(MKDIR_P) thermometer
+       @: > thermometer/$(am__dirstamp)
+thermometer/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) thermometer/$(DEPDIR)
+       @: > thermometer/$(DEPDIR)/$(am__dirstamp)
+thermometer/bluetoothd-main.$(OBJEXT): thermometer/$(am__dirstamp) \
+       thermometer/$(DEPDIR)/$(am__dirstamp)
+thermometer/bluetoothd-manager.$(OBJEXT): thermometer/$(am__dirstamp) \
+       thermometer/$(DEPDIR)/$(am__dirstamp)
+thermometer/bluetoothd-thermometer.$(OBJEXT):  \
+       thermometer/$(am__dirstamp) \
+       thermometer/$(DEPDIR)/$(am__dirstamp)
+alert/$(am__dirstamp):
+       @$(MKDIR_P) alert
+       @: > alert/$(am__dirstamp)
+alert/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) alert/$(DEPDIR)
+       @: > alert/$(DEPDIR)/$(am__dirstamp)
+alert/bluetoothd-main.$(OBJEXT): alert/$(am__dirstamp) \
+       alert/$(DEPDIR)/$(am__dirstamp)
+alert/bluetoothd-server.$(OBJEXT): alert/$(am__dirstamp) \
+       alert/$(DEPDIR)/$(am__dirstamp)
+time/$(am__dirstamp):
+       @$(MKDIR_P) time
+       @: > time/$(am__dirstamp)
+time/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) time/$(DEPDIR)
+       @: > time/$(DEPDIR)/$(am__dirstamp)
+time/bluetoothd-main.$(OBJEXT): time/$(am__dirstamp) \
+       time/$(DEPDIR)/$(am__dirstamp)
+time/bluetoothd-server.$(OBJEXT): time/$(am__dirstamp) \
+       time/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-gatt-example.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+proximity/$(am__dirstamp):
+       @$(MKDIR_P) proximity
+       @: > proximity/$(am__dirstamp)
+proximity/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) proximity/$(DEPDIR)
+       @: > proximity/$(DEPDIR)/$(am__dirstamp)
+proximity/bluetoothd-main.$(OBJEXT): proximity/$(am__dirstamp) \
+       proximity/$(DEPDIR)/$(am__dirstamp)
+proximity/bluetoothd-manager.$(OBJEXT): proximity/$(am__dirstamp) \
+       proximity/$(DEPDIR)/$(am__dirstamp)
+proximity/bluetoothd-monitor.$(OBJEXT): proximity/$(am__dirstamp) \
+       proximity/$(DEPDIR)/$(am__dirstamp)
+proximity/bluetoothd-reporter.$(OBJEXT): proximity/$(am__dirstamp) \
+       proximity/$(DEPDIR)/$(am__dirstamp)
+proximity/bluetoothd-linkloss.$(OBJEXT): proximity/$(am__dirstamp) \
+       proximity/$(DEPDIR)/$(am__dirstamp)
+proximity/bluetoothd-immalert.$(OBJEXT): proximity/$(am__dirstamp) \
+       proximity/$(DEPDIR)/$(am__dirstamp)
+deviceinfo/$(am__dirstamp):
+       @$(MKDIR_P) deviceinfo
+       @: > deviceinfo/$(am__dirstamp)
+deviceinfo/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) deviceinfo/$(DEPDIR)
+       @: > deviceinfo/$(DEPDIR)/$(am__dirstamp)
+deviceinfo/bluetoothd-main.$(OBJEXT): deviceinfo/$(am__dirstamp) \
+       deviceinfo/$(DEPDIR)/$(am__dirstamp)
+deviceinfo/bluetoothd-manager.$(OBJEXT): deviceinfo/$(am__dirstamp) \
+       deviceinfo/$(DEPDIR)/$(am__dirstamp)
+deviceinfo/bluetoothd-deviceinfo.$(OBJEXT):  \
+       deviceinfo/$(am__dirstamp) \
+       deviceinfo/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-hciops.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-mgmtops.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-hal.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-formfactor.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-storage.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-adaptername.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-wiimote.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-maemo6.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/bluetoothd-dbusoob.$(OBJEXT): plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+attrib/bluetoothd-att.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/bluetoothd-gatt.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/bluetoothd-gattrib.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/bluetoothd-client.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+attrib/bluetoothd-gatt-service.$(OBJEXT): attrib/$(am__dirstamp) \
+       attrib/$(DEPDIR)/$(am__dirstamp)
+btio/bluetoothd-btio.$(OBJEXT): btio/$(am__dirstamp) \
+       btio/$(DEPDIR)/$(am__dirstamp)
+health/bluetoothd-mcap.$(OBJEXT): health/$(am__dirstamp) \
+       health/$(DEPDIR)/$(am__dirstamp)
+health/bluetoothd-mcap_sync.$(OBJEXT): health/$(am__dirstamp) \
+       health/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-main.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-log.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-rfkill.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdpd-server.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdpd-request.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdpd-service.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdpd-database.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-attrib-server.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdp-xml.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-sdp-client.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-textfile.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-glib-helper.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-oui.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-plugin.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-storage.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-agent.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-error.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-manager.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-adapter.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-device.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-dbus-common.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-event.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-oob.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-eir.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+audio/bluetoothd-telephony.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+sap/bluetoothd-sap.$(OBJEXT): sap/$(am__dirstamp) \
+       sap/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd$(EXEEXT): $(src_bluetoothd_OBJECTS) $(src_bluetoothd_DEPENDENCIES) $(EXTRA_src_bluetoothd_DEPENDENCIES) src/$(am__dirstamp)
+       @rm -f src/bluetoothd$(EXEEXT)
+       $(AM_V_CCLD)$(src_bluetoothd_LINK) $(src_bluetoothd_OBJECTS) $(src_bluetoothd_LDADD) $(LIBS)
+test/$(am__dirstamp):
+       @$(MKDIR_P) test
+       @: > test/$(am__dirstamp)
+test/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) test/$(DEPDIR)
+       @: > test/$(DEPDIR)/$(am__dirstamp)
+test/agent.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/agent$(EXEEXT): $(test_agent_OBJECTS) $(test_agent_DEPENDENCIES) $(EXTRA_test_agent_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/agent$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_agent_OBJECTS) $(test_agent_LDADD) $(LIBS)
+test/attest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/attest$(EXEEXT): $(test_attest_OBJECTS) $(test_attest_DEPENDENCIES) $(EXTRA_test_attest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/attest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_attest_OBJECTS) $(test_attest_LDADD) $(LIBS)
+test/avtest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/avtest$(EXEEXT): $(test_avtest_OBJECTS) $(test_avtest_DEPENDENCIES) $(EXTRA_test_avtest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/avtest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_avtest_OBJECTS) $(test_avtest_LDADD) $(LIBS)
+test/bdaddr.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+src/oui.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
+test/bdaddr$(EXEEXT): $(test_bdaddr_OBJECTS) $(test_bdaddr_DEPENDENCIES) $(EXTRA_test_bdaddr_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/bdaddr$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_bdaddr_OBJECTS) $(test_bdaddr_LDADD) $(LIBS)
+test/btiotest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/btiotest$(EXEEXT): $(test_btiotest_OBJECTS) $(test_btiotest_DEPENDENCIES) $(EXTRA_test_btiotest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/btiotest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_btiotest_OBJECTS) $(test_btiotest_LDADD) $(LIBS)
+test/gaptest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/gaptest$(EXEEXT): $(test_gaptest_OBJECTS) $(test_gaptest_DEPENDENCIES) $(EXTRA_test_gaptest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/gaptest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_gaptest_OBJECTS) $(test_gaptest_LDADD) $(LIBS)
+test/hciemu.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/hciemu$(EXEEXT): $(test_hciemu_OBJECTS) $(test_hciemu_DEPENDENCIES) $(EXTRA_test_hciemu_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/hciemu$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_hciemu_OBJECTS) $(test_hciemu_LDADD) $(LIBS)
+test/hstest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/hstest$(EXEEXT): $(test_hstest_OBJECTS) $(test_hstest_DEPENDENCIES) $(EXTRA_test_hstest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/hstest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_hstest_OBJECTS) $(test_hstest_LDADD) $(LIBS)
+test/ipctest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+audio/ipc.$(OBJEXT): audio/$(am__dirstamp) \
+       audio/$(DEPDIR)/$(am__dirstamp)
+test/ipctest$(EXEEXT): $(test_ipctest_OBJECTS) $(test_ipctest_DEPENDENCIES) $(EXTRA_test_ipctest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/ipctest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_ipctest_OBJECTS) $(test_ipctest_LDADD) $(LIBS)
+test/l2test.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/l2test$(EXEEXT): $(test_l2test_OBJECTS) $(test_l2test_DEPENDENCIES) $(EXTRA_test_l2test_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/l2test$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_l2test_OBJECTS) $(test_l2test_LDADD) $(LIBS)
+test/lmptest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/lmptest$(EXEEXT): $(test_lmptest_OBJECTS) $(test_lmptest_DEPENDENCIES) $(EXTRA_test_lmptest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/lmptest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_lmptest_OBJECTS) $(test_lmptest_LDADD) $(LIBS)
+test/mpris-player.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/mpris-player$(EXEEXT): $(test_mpris_player_OBJECTS) $(test_mpris_player_DEPENDENCIES) $(EXTRA_test_mpris_player_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/mpris-player$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_mpris_player_OBJECTS) $(test_mpris_player_LDADD) $(LIBS)
+test/rctest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/rctest$(EXEEXT): $(test_rctest_OBJECTS) $(test_rctest_DEPENDENCIES) $(EXTRA_test_rctest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/rctest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_rctest_OBJECTS) $(test_rctest_LDADD) $(LIBS)
+test/scotest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/scotest$(EXEEXT): $(test_scotest_OBJECTS) $(test_scotest_DEPENDENCIES) $(EXTRA_test_scotest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/scotest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_scotest_OBJECTS) $(test_scotest_LDADD) $(LIBS)
+test/sdptest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/sdptest$(EXEEXT): $(test_sdptest_OBJECTS) $(test_sdptest_DEPENDENCIES) $(EXTRA_test_sdptest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/sdptest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_sdptest_OBJECTS) $(test_sdptest_LDADD) $(LIBS)
+test/test-textfile.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/test-textfile$(EXEEXT): $(test_test_textfile_OBJECTS) $(test_test_textfile_DEPENDENCIES) $(EXTRA_test_test_textfile_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/test-textfile$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_test_textfile_OBJECTS) $(test_test_textfile_LDADD) $(LIBS)
+test/uuidtest.$(OBJEXT): test/$(am__dirstamp) \
+       test/$(DEPDIR)/$(am__dirstamp)
+test/uuidtest$(EXEEXT): $(test_uuidtest_OBJECTS) $(test_uuidtest_DEPENDENCIES) $(EXTRA_test_uuidtest_DEPENDENCIES) test/$(am__dirstamp)
+       @rm -f test/uuidtest$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(test_uuidtest_OBJECTS) $(test_uuidtest_LDADD) $(LIBS)
+tools/$(am__dirstamp):
+       @$(MKDIR_P) tools
+       @: > tools/$(am__dirstamp)
+tools/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) tools/$(DEPDIR)
+       @: > tools/$(DEPDIR)/$(am__dirstamp)
+tools/avctrl.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/avctrl$(EXEEXT): $(tools_avctrl_OBJECTS) $(tools_avctrl_DEPENDENCIES) $(EXTRA_tools_avctrl_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/avctrl$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_avctrl_OBJECTS) $(tools_avctrl_LDADD) $(LIBS)
+tools/avinfo.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/avinfo$(EXEEXT): $(tools_avinfo_OBJECTS) $(tools_avinfo_DEPENDENCIES) $(EXTRA_tools_avinfo_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/avinfo$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_avinfo_OBJECTS) $(tools_avinfo_LDADD) $(LIBS)
+tools/bccmd.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_hci.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_h4.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_3wire.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_bcsp.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/ubcsp.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/csr_usb.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/bccmd$(EXEEXT): $(tools_bccmd_OBJECTS) $(tools_bccmd_DEPENDENCIES) $(EXTRA_tools_bccmd_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/bccmd$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_bccmd_OBJECTS) $(tools_bccmd_LDADD) $(LIBS)
+tools/ciptool.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/ciptool$(EXEEXT): $(tools_ciptool_OBJECTS) $(tools_ciptool_DEPENDENCIES) $(EXTRA_tools_ciptool_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/ciptool$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_ciptool_OBJECTS) $(tools_ciptool_LDADD) $(LIBS)
+tools/dfubabel.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/dfubabel$(EXEEXT): $(tools_dfubabel_OBJECTS) $(tools_dfubabel_DEPENDENCIES) $(EXTRA_tools_dfubabel_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/dfubabel$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_dfubabel_OBJECTS) $(tools_dfubabel_LDADD) $(LIBS)
+tools/dfutool.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/dfu.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/dfutool$(EXEEXT): $(tools_dfutool_OBJECTS) $(tools_dfutool_DEPENDENCIES) $(EXTRA_tools_dfutool_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/dfutool$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_dfutool_OBJECTS) $(tools_dfutool_LDADD) $(LIBS)
+tools/hciattach.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_st.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_ti.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_tialt.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_ath3k.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_qualcomm.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_intel.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach$(EXEEXT): $(tools_hciattach_OBJECTS) $(tools_hciattach_DEPENDENCIES) $(EXTRA_tools_hciattach_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/hciattach$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_hciattach_OBJECTS) $(tools_hciattach_LDADD) $(LIBS)
+tools/hciconfig.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciconfig$(EXEEXT): $(tools_hciconfig_OBJECTS) $(tools_hciconfig_DEPENDENCIES) $(EXTRA_tools_hciconfig_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/hciconfig$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_hciconfig_OBJECTS) $(tools_hciconfig_LDADD) $(LIBS)
+tools/hcieventmask.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hcieventmask$(EXEEXT): $(tools_hcieventmask_OBJECTS) $(tools_hcieventmask_DEPENDENCIES) $(EXTRA_tools_hcieventmask_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/hcieventmask$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_hcieventmask_OBJECTS) $(tools_hcieventmask_LDADD) $(LIBS)
+tools/hcisecfilter.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hcisecfilter$(EXEEXT): $(tools_hcisecfilter_OBJECTS) $(tools_hcisecfilter_DEPENDENCIES) $(EXTRA_tools_hcisecfilter_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/hcisecfilter$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_hcisecfilter_OBJECTS) $(tools_hcisecfilter_LDADD) $(LIBS)
+tools/hcitool.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hcitool$(EXEEXT): $(tools_hcitool_OBJECTS) $(tools_hcitool_DEPENDENCIES) $(EXTRA_tools_hcitool_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/hcitool$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_hcitool_OBJECTS) $(tools_hcitool_LDADD) $(LIBS)
+tools/hid2hci.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hid2hci$(EXEEXT): $(tools_hid2hci_OBJECTS) $(tools_hid2hci_DEPENDENCIES) $(EXTRA_tools_hid2hci_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/hid2hci$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_hid2hci_OBJECTS) $(tools_hid2hci_LDADD) $(LIBS)
+tools/l2ping.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/l2ping$(EXEEXT): $(tools_l2ping_OBJECTS) $(tools_l2ping_DEPENDENCIES) $(EXTRA_tools_l2ping_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/l2ping$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_l2ping_OBJECTS) $(tools_l2ping_LDADD) $(LIBS)
+tools/ppporc.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/ppporc$(EXEEXT): $(tools_ppporc_OBJECTS) $(tools_ppporc_DEPENDENCIES) $(EXTRA_tools_ppporc_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/ppporc$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_ppporc_OBJECTS) $(tools_ppporc_LDADD) $(LIBS)
+tools/rfcomm.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/parser.h: tools/parser.c
+       @if test ! -f $@; then rm -f tools/parser.c; else :; fi
+       @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) tools/parser.c; else :; fi
+tools/parser.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/lexer.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/kword.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/rfcomm$(EXEEXT): $(tools_rfcomm_OBJECTS) $(tools_rfcomm_DEPENDENCIES) $(EXTRA_tools_rfcomm_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/rfcomm$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_rfcomm_OBJECTS) $(tools_rfcomm_LDADD) $(LIBS)
+tools/sdptool.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+src/sdp-xml.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+tools/sdptool$(EXEEXT): $(tools_sdptool_OBJECTS) $(tools_sdptool_DEPENDENCIES) $(EXTRA_tools_sdptool_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/sdptool$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_sdptool_OBJECTS) $(tools_sdptool_LDADD) $(LIBS)
+unit/$(am__dirstamp):
+       @$(MKDIR_P) unit
+       @: > unit/$(am__dirstamp)
+unit/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) unit/$(DEPDIR)
+       @: > unit/$(DEPDIR)/$(am__dirstamp)
+unit/unit_test_eir-test-eir.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+src/unit_test_eir-eir.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/unit_test_eir-glib-helper.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+unit/test-eir$(EXEEXT): $(unit_test_eir_OBJECTS) $(unit_test_eir_DEPENDENCIES) $(EXTRA_unit_test_eir_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-eir$(EXEEXT)
+       $(AM_V_CCLD)$(unit_test_eir_LINK) $(unit_test_eir_OBJECTS) $(unit_test_eir_LDADD) $(LIBS)
+install-dist_udevSCRIPTS: $(dist_udev_SCRIPTS)
+       @$(NORMAL_INSTALL)
+       test -z "$(udevdir)" || $(MKDIR_P) "$(DESTDIR)$(udevdir)"
+       @list='$(dist_udev_SCRIPTS)'; test -n "$(udevdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+       done | \
+       sed -e 'p;s,.*/,,;n' \
+           -e 'h;s|.*|.|' \
+           -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+       $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+         { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+           if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+             if (++n[d] == $(am__install_max)) { \
+               print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+           else { print "f", d "/" $$4, $$1 } } \
+         END { for (d in files) print "f", d, files[d] }' | \
+       while read type dir files; do \
+            if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+            test -z "$$files" || { \
+              echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(udevdir)$$dir'"; \
+              $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(udevdir)$$dir" || exit $$?; \
+            } \
+       ; done
+
+uninstall-dist_udevSCRIPTS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(dist_udev_SCRIPTS)'; test -n "$(udevdir)" || exit 0; \
+       files=`for p in $$list; do echo "$$p"; done | \
+              sed -e 's,.*/,,;$(transform)'`; \
+       dir='$(DESTDIR)$(udevdir)'; $(am__uninstall_files_from_dir)
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+       -rm -f alert/bluetoothd-main.$(OBJEXT)
+       -rm -f alert/bluetoothd-server.$(OBJEXT)
+       -rm -f attrib/att.$(OBJEXT)
+       -rm -f attrib/bluetoothd-att.$(OBJEXT)
+       -rm -f attrib/bluetoothd-client.$(OBJEXT)
+       -rm -f attrib/bluetoothd-gatt-service.$(OBJEXT)
+       -rm -f attrib/bluetoothd-gatt.$(OBJEXT)
+       -rm -f attrib/bluetoothd-gattrib.$(OBJEXT)
+       -rm -f attrib/gatt.$(OBJEXT)
+       -rm -f attrib/gattrib.$(OBJEXT)
+       -rm -f attrib/gatttool.$(OBJEXT)
+       -rm -f attrib/interactive.$(OBJEXT)
+       -rm -f attrib/utils.$(OBJEXT)
+       -rm -f audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.$(OBJEXT)
+       -rm -f audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo
+       -rm -f audio/audio_libasound_module_ctl_bluetooth_la-ipc.$(OBJEXT)
+       -rm -f audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo
+       -rm -f audio/audio_libasound_module_pcm_bluetooth_la-ipc.$(OBJEXT)
+       -rm -f audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo
+       -rm -f audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.$(OBJEXT)
+       -rm -f audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo
+       -rm -f audio/audio_libgstbluetooth_la-gsta2dpsink.$(OBJEXT)
+       -rm -f audio/audio_libgstbluetooth_la-gsta2dpsink.lo
+       -rm -f audio/audio_libgstbluetooth_la-gstavdtpsink.$(OBJEXT)
+       -rm -f audio/audio_libgstbluetooth_la-gstavdtpsink.lo
+       -rm -f audio/audio_libgstbluetooth_la-gstbluetooth.$(OBJEXT)
+       -rm -f audio/audio_libgstbluetooth_la-gstbluetooth.lo
+       -rm -f audio/audio_libgstbluetooth_la-gstrtpsbcpay.$(OBJEXT)
+       -rm -f audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo
+       -rm -f audio/audio_libgstbluetooth_la-gstsbcdec.$(OBJEXT)
+       -rm -f audio/audio_libgstbluetooth_la-gstsbcdec.lo
+       -rm -f audio/audio_libgstbluetooth_la-gstsbcenc.$(OBJEXT)
+       -rm -f audio/audio_libgstbluetooth_la-gstsbcenc.lo
+       -rm -f audio/audio_libgstbluetooth_la-gstsbcparse.$(OBJEXT)
+       -rm -f audio/audio_libgstbluetooth_la-gstsbcparse.lo
+       -rm -f audio/audio_libgstbluetooth_la-gstsbcutil.$(OBJEXT)
+       -rm -f audio/audio_libgstbluetooth_la-gstsbcutil.lo
+       -rm -f audio/audio_libgstbluetooth_la-ipc.$(OBJEXT)
+       -rm -f audio/audio_libgstbluetooth_la-ipc.lo
+       -rm -f audio/bluetoothd-a2dp.$(OBJEXT)
+       -rm -f audio/bluetoothd-avctp.$(OBJEXT)
+       -rm -f audio/bluetoothd-avdtp.$(OBJEXT)
+       -rm -f audio/bluetoothd-avrcp.$(OBJEXT)
+       -rm -f audio/bluetoothd-control.$(OBJEXT)
+       -rm -f audio/bluetoothd-device.$(OBJEXT)
+       -rm -f audio/bluetoothd-gateway.$(OBJEXT)
+       -rm -f audio/bluetoothd-headset.$(OBJEXT)
+       -rm -f audio/bluetoothd-ipc.$(OBJEXT)
+       -rm -f audio/bluetoothd-main.$(OBJEXT)
+       -rm -f audio/bluetoothd-manager.$(OBJEXT)
+       -rm -f audio/bluetoothd-media.$(OBJEXT)
+       -rm -f audio/bluetoothd-sink.$(OBJEXT)
+       -rm -f audio/bluetoothd-source.$(OBJEXT)
+       -rm -f audio/bluetoothd-telephony.$(OBJEXT)
+       -rm -f audio/bluetoothd-transport.$(OBJEXT)
+       -rm -f audio/bluetoothd-unix.$(OBJEXT)
+       -rm -f audio/ipc.$(OBJEXT)
+       -rm -f audio/telephony-dummy.$(OBJEXT)
+       -rm -f audio/telephony-maemo5.$(OBJEXT)
+       -rm -f audio/telephony-maemo6.$(OBJEXT)
+       -rm -f audio/telephony-ofono.$(OBJEXT)
+       -rm -f btio/bluetoothd-btio.$(OBJEXT)
+       -rm -f btio/btio.$(OBJEXT)
+       -rm -f compat/bnep.$(OBJEXT)
+       -rm -f compat/dun.$(OBJEXT)
+       -rm -f compat/dund.$(OBJEXT)
+       -rm -f compat/fakehid.$(OBJEXT)
+       -rm -f compat/hidd.$(OBJEXT)
+       -rm -f compat/msdun.$(OBJEXT)
+       -rm -f compat/pand.$(OBJEXT)
+       -rm -f compat/sdp.$(OBJEXT)
+       -rm -f cups/hcrp.$(OBJEXT)
+       -rm -f cups/main.$(OBJEXT)
+       -rm -f cups/sdp.$(OBJEXT)
+       -rm -f cups/spp.$(OBJEXT)
+       -rm -f deviceinfo/bluetoothd-deviceinfo.$(OBJEXT)
+       -rm -f deviceinfo/bluetoothd-main.$(OBJEXT)
+       -rm -f deviceinfo/bluetoothd-manager.$(OBJEXT)
+       -rm -f emulator/btdev.$(OBJEXT)
+       -rm -f emulator/main.$(OBJEXT)
+       -rm -f emulator/server.$(OBJEXT)
+       -rm -f emulator/vhci.$(OBJEXT)
+       -rm -f gdbus/bluetoothd-mainloop.$(OBJEXT)
+       -rm -f gdbus/bluetoothd-object.$(OBJEXT)
+       -rm -f gdbus/bluetoothd-polkit.$(OBJEXT)
+       -rm -f gdbus/bluetoothd-watch.$(OBJEXT)
+       -rm -f gdbus/mainloop.$(OBJEXT)
+       -rm -f gdbus/object.$(OBJEXT)
+       -rm -f gdbus/polkit.$(OBJEXT)
+       -rm -f gdbus/watch.$(OBJEXT)
+       -rm -f health/bluetoothd-hdp.$(OBJEXT)
+       -rm -f health/bluetoothd-hdp_main.$(OBJEXT)
+       -rm -f health/bluetoothd-hdp_manager.$(OBJEXT)
+       -rm -f health/bluetoothd-hdp_util.$(OBJEXT)
+       -rm -f health/bluetoothd-mcap.$(OBJEXT)
+       -rm -f health/bluetoothd-mcap_sync.$(OBJEXT)
+       -rm -f input/bluetoothd-device.$(OBJEXT)
+       -rm -f input/bluetoothd-fakehid.$(OBJEXT)
+       -rm -f input/bluetoothd-main.$(OBJEXT)
+       -rm -f input/bluetoothd-manager.$(OBJEXT)
+       -rm -f input/bluetoothd-server.$(OBJEXT)
+       -rm -f lib/bluetooth.$(OBJEXT)
+       -rm -f lib/bluetooth.lo
+       -rm -f lib/hci.$(OBJEXT)
+       -rm -f lib/hci.lo
+       -rm -f lib/sdp.$(OBJEXT)
+       -rm -f lib/sdp.lo
+       -rm -f lib/uuid.$(OBJEXT)
+       -rm -f lib/uuid.lo
+       -rm -f mgmt/main.$(OBJEXT)
+       -rm -f monitor/btsnoop.$(OBJEXT)
+       -rm -f monitor/control.$(OBJEXT)
+       -rm -f monitor/hcidump.$(OBJEXT)
+       -rm -f monitor/main.$(OBJEXT)
+       -rm -f monitor/mainloop.$(OBJEXT)
+       -rm -f monitor/packet.$(OBJEXT)
+       -rm -f network/bluetoothd-common.$(OBJEXT)
+       -rm -f network/bluetoothd-connection.$(OBJEXT)
+       -rm -f network/bluetoothd-main.$(OBJEXT)
+       -rm -f network/bluetoothd-manager.$(OBJEXT)
+       -rm -f network/bluetoothd-server.$(OBJEXT)
+       -rm -f plugins/bluetoothd-adaptername.$(OBJEXT)
+       -rm -f plugins/bluetoothd-dbusoob.$(OBJEXT)
+       -rm -f plugins/bluetoothd-formfactor.$(OBJEXT)
+       -rm -f plugins/bluetoothd-gatt-example.$(OBJEXT)
+       -rm -f plugins/bluetoothd-hal.$(OBJEXT)
+       -rm -f plugins/bluetoothd-hciops.$(OBJEXT)
+       -rm -f plugins/bluetoothd-maemo6.$(OBJEXT)
+       -rm -f plugins/bluetoothd-mgmtops.$(OBJEXT)
+       -rm -f plugins/bluetoothd-pnat.$(OBJEXT)
+       -rm -f plugins/bluetoothd-service.$(OBJEXT)
+       -rm -f plugins/bluetoothd-storage.$(OBJEXT)
+       -rm -f plugins/bluetoothd-wiimote.$(OBJEXT)
+       -rm -f plugins/plugins_external_dummy_la-external-dummy.$(OBJEXT)
+       -rm -f plugins/plugins_external_dummy_la-external-dummy.lo
+       -rm -f proximity/bluetoothd-immalert.$(OBJEXT)
+       -rm -f proximity/bluetoothd-linkloss.$(OBJEXT)
+       -rm -f proximity/bluetoothd-main.$(OBJEXT)
+       -rm -f proximity/bluetoothd-manager.$(OBJEXT)
+       -rm -f proximity/bluetoothd-monitor.$(OBJEXT)
+       -rm -f proximity/bluetoothd-reporter.$(OBJEXT)
+       -rm -f sap/bluetoothd-main.$(OBJEXT)
+       -rm -f sap/bluetoothd-manager.$(OBJEXT)
+       -rm -f sap/bluetoothd-sap.$(OBJEXT)
+       -rm -f sap/bluetoothd-server.$(OBJEXT)
+       -rm -f sap/sap-dummy.$(OBJEXT)
+       -rm -f sap/sap-u8500.$(OBJEXT)
+       -rm -f sbc/sbc_libsbc_la-sbc.$(OBJEXT)
+       -rm -f sbc/sbc_libsbc_la-sbc.lo
+       -rm -f sbc/sbc_libsbc_la-sbc_primitives.$(OBJEXT)
+       -rm -f sbc/sbc_libsbc_la-sbc_primitives.lo
+       -rm -f sbc/sbc_libsbc_la-sbc_primitives_armv6.$(OBJEXT)
+       -rm -f sbc/sbc_libsbc_la-sbc_primitives_armv6.lo
+       -rm -f sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.$(OBJEXT)
+       -rm -f sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo
+       -rm -f sbc/sbc_libsbc_la-sbc_primitives_mmx.$(OBJEXT)
+       -rm -f sbc/sbc_libsbc_la-sbc_primitives_mmx.lo
+       -rm -f sbc/sbc_libsbc_la-sbc_primitives_neon.$(OBJEXT)
+       -rm -f sbc/sbc_libsbc_la-sbc_primitives_neon.lo
+       -rm -f sbc/sbcdec.$(OBJEXT)
+       -rm -f sbc/sbcenc.$(OBJEXT)
+       -rm -f sbc/sbcinfo.$(OBJEXT)
+       -rm -f sbc/sbctester.$(OBJEXT)
+       -rm -f serial/bluetoothd-main.$(OBJEXT)
+       -rm -f serial/bluetoothd-manager.$(OBJEXT)
+       -rm -f serial/bluetoothd-port.$(OBJEXT)
+       -rm -f serial/bluetoothd-proxy.$(OBJEXT)
+       -rm -f src/bluetoothd-adapter.$(OBJEXT)
+       -rm -f src/bluetoothd-agent.$(OBJEXT)
+       -rm -f src/bluetoothd-attrib-server.$(OBJEXT)
+       -rm -f src/bluetoothd-dbus-common.$(OBJEXT)
+       -rm -f src/bluetoothd-device.$(OBJEXT)
+       -rm -f src/bluetoothd-eir.$(OBJEXT)
+       -rm -f src/bluetoothd-error.$(OBJEXT)
+       -rm -f src/bluetoothd-event.$(OBJEXT)
+       -rm -f src/bluetoothd-glib-helper.$(OBJEXT)
+       -rm -f src/bluetoothd-log.$(OBJEXT)
+       -rm -f src/bluetoothd-main.$(OBJEXT)
+       -rm -f src/bluetoothd-manager.$(OBJEXT)
+       -rm -f src/bluetoothd-oob.$(OBJEXT)
+       -rm -f src/bluetoothd-oui.$(OBJEXT)
+       -rm -f src/bluetoothd-plugin.$(OBJEXT)
+       -rm -f src/bluetoothd-rfkill.$(OBJEXT)
+       -rm -f src/bluetoothd-sdp-client.$(OBJEXT)
+       -rm -f src/bluetoothd-sdp-xml.$(OBJEXT)
+       -rm -f src/bluetoothd-sdpd-database.$(OBJEXT)
+       -rm -f src/bluetoothd-sdpd-request.$(OBJEXT)
+       -rm -f src/bluetoothd-sdpd-server.$(OBJEXT)
+       -rm -f src/bluetoothd-sdpd-service.$(OBJEXT)
+       -rm -f src/bluetoothd-storage.$(OBJEXT)
+       -rm -f src/bluetoothd-textfile.$(OBJEXT)
+       -rm -f src/glib-helper.$(OBJEXT)
+       -rm -f src/log.$(OBJEXT)
+       -rm -f src/oui.$(OBJEXT)
+       -rm -f src/sdp-xml.$(OBJEXT)
+       -rm -f src/textfile.$(OBJEXT)
+       -rm -f src/unit_test_eir-eir.$(OBJEXT)
+       -rm -f src/unit_test_eir-glib-helper.$(OBJEXT)
+       -rm -f test/agent.$(OBJEXT)
+       -rm -f test/attest.$(OBJEXT)
+       -rm -f test/avtest.$(OBJEXT)
+       -rm -f test/bdaddr.$(OBJEXT)
+       -rm -f test/btiotest.$(OBJEXT)
+       -rm -f test/gaptest.$(OBJEXT)
+       -rm -f test/hciemu.$(OBJEXT)
+       -rm -f test/hstest.$(OBJEXT)
+       -rm -f test/ipctest.$(OBJEXT)
+       -rm -f test/l2test.$(OBJEXT)
+       -rm -f test/lmptest.$(OBJEXT)
+       -rm -f test/mpris-player.$(OBJEXT)
+       -rm -f test/rctest.$(OBJEXT)
+       -rm -f test/scotest.$(OBJEXT)
+       -rm -f test/sdptest.$(OBJEXT)
+       -rm -f test/test-textfile.$(OBJEXT)
+       -rm -f test/uuidtest.$(OBJEXT)
+       -rm -f thermometer/bluetoothd-main.$(OBJEXT)
+       -rm -f thermometer/bluetoothd-manager.$(OBJEXT)
+       -rm -f thermometer/bluetoothd-thermometer.$(OBJEXT)
+       -rm -f time/bluetoothd-main.$(OBJEXT)
+       -rm -f time/bluetoothd-server.$(OBJEXT)
+       -rm -f tools/avctrl.$(OBJEXT)
+       -rm -f tools/avinfo.$(OBJEXT)
+       -rm -f tools/bccmd.$(OBJEXT)
+       -rm -f tools/ciptool.$(OBJEXT)
+       -rm -f tools/csr.$(OBJEXT)
+       -rm -f tools/csr_3wire.$(OBJEXT)
+       -rm -f tools/csr_bcsp.$(OBJEXT)
+       -rm -f tools/csr_h4.$(OBJEXT)
+       -rm -f tools/csr_hci.$(OBJEXT)
+       -rm -f tools/csr_usb.$(OBJEXT)
+       -rm -f tools/dfu.$(OBJEXT)
+       -rm -f tools/dfubabel.$(OBJEXT)
+       -rm -f tools/dfutool.$(OBJEXT)
+       -rm -f tools/hciattach.$(OBJEXT)
+       -rm -f tools/hciattach_ath3k.$(OBJEXT)
+       -rm -f tools/hciattach_intel.$(OBJEXT)
+       -rm -f tools/hciattach_qualcomm.$(OBJEXT)
+       -rm -f tools/hciattach_st.$(OBJEXT)
+       -rm -f tools/hciattach_ti.$(OBJEXT)
+       -rm -f tools/hciattach_tialt.$(OBJEXT)
+       -rm -f tools/hciconfig.$(OBJEXT)
+       -rm -f tools/hcieventmask.$(OBJEXT)
+       -rm -f tools/hcisecfilter.$(OBJEXT)
+       -rm -f tools/hcitool.$(OBJEXT)
+       -rm -f tools/hid2hci.$(OBJEXT)
+       -rm -f tools/kword.$(OBJEXT)
+       -rm -f tools/l2ping.$(OBJEXT)
+       -rm -f tools/lexer.$(OBJEXT)
+       -rm -f tools/parser.$(OBJEXT)
+       -rm -f tools/ppporc.$(OBJEXT)
+       -rm -f tools/rfcomm.$(OBJEXT)
+       -rm -f tools/sdptool.$(OBJEXT)
+       -rm -f tools/ubcsp.$(OBJEXT)
+       -rm -f unit/unit_test_eir-test-eir.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@alert/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@alert/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/att.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-att.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-gatt-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-gatt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-gattrib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gatt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gattrib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/gatttool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/interactive.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ipc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-ipc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gsta2dpsink.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstavdtpsink.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstbluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstrtpsbcpay.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcdec.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcenc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcparse.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcutil.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/audio_libgstbluetooth_la-ipc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-a2dp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-avctp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-avdtp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-avrcp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-control.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-device.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-gateway.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-headset.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-ipc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-media.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-sink.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-source.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-telephony.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-transport.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/bluetoothd-unix.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/ipc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/telephony-dummy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/telephony-maemo5.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/telephony-maemo6.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@audio/$(DEPDIR)/telephony-ofono.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/bluetoothd-btio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@btio/$(DEPDIR)/btio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/bnep.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/dun.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/dund.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/fakehid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/hidd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/msdun.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/pand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@compat/$(DEPDIR)/sdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@cups/$(DEPDIR)/hcrp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@cups/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@cups/$(DEPDIR)/sdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@cups/$(DEPDIR)/spp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@deviceinfo/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@deviceinfo/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/btdev.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/vhci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/bluetoothd-mainloop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/bluetoothd-object.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/bluetoothd-polkit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/bluetoothd-watch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/mainloop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/object.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/polkit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/watch.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/bluetoothd-hdp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/bluetoothd-hdp_main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/bluetoothd-hdp_manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/bluetoothd-hdp_util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/bluetoothd-mcap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@health/$(DEPDIR)/bluetoothd-mcap_sync.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/bluetoothd-device.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/bluetoothd-fakehid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@input/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/bluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/hci.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/sdp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@lib/$(DEPDIR)/uuid.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@mgmt/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/btsnoop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/control.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/hcidump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/mainloop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/packet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/bluetoothd-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/bluetoothd-connection.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@network/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-adaptername.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-dbusoob.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-formfactor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-gatt-example.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-hal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-hciops.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-maemo6.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-mgmtops.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-pnat.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-storage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-wiimote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@proximity/$(DEPDIR)/bluetoothd-immalert.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@proximity/$(DEPDIR)/bluetoothd-linkloss.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@proximity/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@proximity/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@proximity/$(DEPDIR)/bluetoothd-monitor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@proximity/$(DEPDIR)/bluetoothd-reporter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/bluetoothd-sap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/sap-dummy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sap/$(DEPDIR)/sap-u8500.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_armv6.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_iwmmxt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_mmx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_neon.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbcdec.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbcenc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbcinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@sbc/$(DEPDIR)/sbctester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@serial/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@serial/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@serial/$(DEPDIR)/bluetoothd-port.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@serial/$(DEPDIR)/bluetoothd-proxy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-adapter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-agent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-attrib-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-dbus-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-device.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-eir.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-error.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-event.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-glib-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-oob.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-oui.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-plugin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-rfkill.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdp-client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdp-xml.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-database.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-request.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-sdpd-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-storage.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-textfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/glib-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/log.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/oui.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdp-xml.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/textfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/unit_test_eir-eir.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/unit_test_eir-glib-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/agent.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/attest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/avtest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/bdaddr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/btiotest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/gaptest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/hciemu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/hstest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/ipctest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/l2test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/lmptest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/mpris-player.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/rctest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/scotest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/sdptest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/test-textfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@test/$(DEPDIR)/uuidtest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@thermometer/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@thermometer/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@thermometer/$(DEPDIR)/bluetoothd-thermometer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@time/$(DEPDIR)/bluetoothd-main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@time/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avctrl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avinfo.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bccmd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ciptool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_3wire.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_bcsp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_h4.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_hci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/csr_usb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/dfu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/dfubabel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/dfutool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_ath3k.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_intel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_qualcomm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_st.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_ti.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciattach_tialt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hciconfig.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcieventmask.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcisecfilter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hcitool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hid2hci.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/kword.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2ping.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/lexer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/parser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ppporc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rfcomm.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sdptool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ubcsp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/unit_test_eir-test-eir.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@   $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@   $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@   $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@   $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@   $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo: audio/ctl_bluetooth.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_ctl_bluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo -MD -MP -MF audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.Tpo -c -o audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo `test -f 'audio/ctl_bluetooth.c' || echo '$(srcdir)/'`audio/ctl_bluetooth.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.Tpo audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/ctl_bluetooth.c' object='audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_ctl_bluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libasound_module_ctl_bluetooth_la-ctl_bluetooth.lo `test -f 'audio/ctl_bluetooth.c' || echo '$(srcdir)/'`audio/ctl_bluetooth.c
+
+audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo: audio/ipc.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_ctl_bluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo -MD -MP -MF audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ipc.Tpo -c -o audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ipc.Tpo audio/$(DEPDIR)/audio_libasound_module_ctl_bluetooth_la-ipc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/ipc.c' object='audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_ctl_bluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libasound_module_ctl_bluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+
+audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo: audio/pcm_bluetooth.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_pcm_bluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo -MD -MP -MF audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.Tpo -c -o audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo `test -f 'audio/pcm_bluetooth.c' || echo '$(srcdir)/'`audio/pcm_bluetooth.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.Tpo audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/pcm_bluetooth.c' object='audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_pcm_bluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libasound_module_pcm_bluetooth_la-pcm_bluetooth.lo `test -f 'audio/pcm_bluetooth.c' || echo '$(srcdir)/'`audio/pcm_bluetooth.c
+
+audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo: audio/ipc.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_pcm_bluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo -MD -MP -MF audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-ipc.Tpo -c -o audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-ipc.Tpo audio/$(DEPDIR)/audio_libasound_module_pcm_bluetooth_la-ipc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/ipc.c' object='audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libasound_module_pcm_bluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libasound_module_pcm_bluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+
+audio/audio_libgstbluetooth_la-gstbluetooth.lo: audio/gstbluetooth.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstbluetooth.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstbluetooth.Tpo -c -o audio/audio_libgstbluetooth_la-gstbluetooth.lo `test -f 'audio/gstbluetooth.c' || echo '$(srcdir)/'`audio/gstbluetooth.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstbluetooth.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstbluetooth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/gstbluetooth.c' object='audio/audio_libgstbluetooth_la-gstbluetooth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstbluetooth.lo `test -f 'audio/gstbluetooth.c' || echo '$(srcdir)/'`audio/gstbluetooth.c
+
+audio/audio_libgstbluetooth_la-gstsbcenc.lo: audio/gstsbcenc.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstsbcenc.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcenc.Tpo -c -o audio/audio_libgstbluetooth_la-gstsbcenc.lo `test -f 'audio/gstsbcenc.c' || echo '$(srcdir)/'`audio/gstsbcenc.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcenc.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcenc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/gstsbcenc.c' object='audio/audio_libgstbluetooth_la-gstsbcenc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstsbcenc.lo `test -f 'audio/gstsbcenc.c' || echo '$(srcdir)/'`audio/gstsbcenc.c
+
+audio/audio_libgstbluetooth_la-gstsbcdec.lo: audio/gstsbcdec.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstsbcdec.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcdec.Tpo -c -o audio/audio_libgstbluetooth_la-gstsbcdec.lo `test -f 'audio/gstsbcdec.c' || echo '$(srcdir)/'`audio/gstsbcdec.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcdec.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcdec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/gstsbcdec.c' object='audio/audio_libgstbluetooth_la-gstsbcdec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstsbcdec.lo `test -f 'audio/gstsbcdec.c' || echo '$(srcdir)/'`audio/gstsbcdec.c
+
+audio/audio_libgstbluetooth_la-gstsbcparse.lo: audio/gstsbcparse.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstsbcparse.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcparse.Tpo -c -o audio/audio_libgstbluetooth_la-gstsbcparse.lo `test -f 'audio/gstsbcparse.c' || echo '$(srcdir)/'`audio/gstsbcparse.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcparse.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcparse.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/gstsbcparse.c' object='audio/audio_libgstbluetooth_la-gstsbcparse.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstsbcparse.lo `test -f 'audio/gstsbcparse.c' || echo '$(srcdir)/'`audio/gstsbcparse.c
+
+audio/audio_libgstbluetooth_la-gstavdtpsink.lo: audio/gstavdtpsink.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstavdtpsink.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstavdtpsink.Tpo -c -o audio/audio_libgstbluetooth_la-gstavdtpsink.lo `test -f 'audio/gstavdtpsink.c' || echo '$(srcdir)/'`audio/gstavdtpsink.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstavdtpsink.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstavdtpsink.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/gstavdtpsink.c' object='audio/audio_libgstbluetooth_la-gstavdtpsink.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstavdtpsink.lo `test -f 'audio/gstavdtpsink.c' || echo '$(srcdir)/'`audio/gstavdtpsink.c
+
+audio/audio_libgstbluetooth_la-gsta2dpsink.lo: audio/gsta2dpsink.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gsta2dpsink.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gsta2dpsink.Tpo -c -o audio/audio_libgstbluetooth_la-gsta2dpsink.lo `test -f 'audio/gsta2dpsink.c' || echo '$(srcdir)/'`audio/gsta2dpsink.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gsta2dpsink.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gsta2dpsink.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/gsta2dpsink.c' object='audio/audio_libgstbluetooth_la-gsta2dpsink.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gsta2dpsink.lo `test -f 'audio/gsta2dpsink.c' || echo '$(srcdir)/'`audio/gsta2dpsink.c
+
+audio/audio_libgstbluetooth_la-gstsbcutil.lo: audio/gstsbcutil.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstsbcutil.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcutil.Tpo -c -o audio/audio_libgstbluetooth_la-gstsbcutil.lo `test -f 'audio/gstsbcutil.c' || echo '$(srcdir)/'`audio/gstsbcutil.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcutil.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstsbcutil.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/gstsbcutil.c' object='audio/audio_libgstbluetooth_la-gstsbcutil.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstsbcutil.lo `test -f 'audio/gstsbcutil.c' || echo '$(srcdir)/'`audio/gstsbcutil.c
+
+audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo: audio/gstrtpsbcpay.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-gstrtpsbcpay.Tpo -c -o audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo `test -f 'audio/gstrtpsbcpay.c' || echo '$(srcdir)/'`audio/gstrtpsbcpay.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-gstrtpsbcpay.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-gstrtpsbcpay.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/gstrtpsbcpay.c' object='audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-gstrtpsbcpay.lo `test -f 'audio/gstrtpsbcpay.c' || echo '$(srcdir)/'`audio/gstrtpsbcpay.c
+
+audio/audio_libgstbluetooth_la-ipc.lo: audio/ipc.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -MT audio/audio_libgstbluetooth_la-ipc.lo -MD -MP -MF audio/$(DEPDIR)/audio_libgstbluetooth_la-ipc.Tpo -c -o audio/audio_libgstbluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/audio_libgstbluetooth_la-ipc.Tpo audio/$(DEPDIR)/audio_libgstbluetooth_la-ipc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/ipc.c' object='audio/audio_libgstbluetooth_la-ipc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(audio_libgstbluetooth_la_CFLAGS) $(CFLAGS) -c -o audio/audio_libgstbluetooth_la-ipc.lo `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+
+plugins/plugins_external_dummy_la-external-dummy.lo: plugins/external-dummy.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_external_dummy_la_CFLAGS) $(CFLAGS) -MT plugins/plugins_external_dummy_la-external-dummy.lo -MD -MP -MF plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Tpo -c -o plugins/plugins_external_dummy_la-external-dummy.lo `test -f 'plugins/external-dummy.c' || echo '$(srcdir)/'`plugins/external-dummy.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Tpo plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/external-dummy.c' object='plugins/plugins_external_dummy_la-external-dummy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_external_dummy_la_CFLAGS) $(CFLAGS) -c -o plugins/plugins_external_dummy_la-external-dummy.lo `test -f 'plugins/external-dummy.c' || echo '$(srcdir)/'`plugins/external-dummy.c
+
+sbc/sbc_libsbc_la-sbc.lo: sbc/sbc.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc.Tpo -c -o sbc/sbc_libsbc_la-sbc.lo `test -f 'sbc/sbc.c' || echo '$(srcdir)/'`sbc/sbc.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sbc/sbc.c' object='sbc/sbc_libsbc_la-sbc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc.lo `test -f 'sbc/sbc.c' || echo '$(srcdir)/'`sbc/sbc.c
+
+sbc/sbc_libsbc_la-sbc_primitives.lo: sbc/sbc_primitives.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc_primitives.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives.Tpo -c -o sbc/sbc_libsbc_la-sbc_primitives.lo `test -f 'sbc/sbc_primitives.c' || echo '$(srcdir)/'`sbc/sbc_primitives.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sbc/sbc_primitives.c' object='sbc/sbc_libsbc_la-sbc_primitives.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc_primitives.lo `test -f 'sbc/sbc_primitives.c' || echo '$(srcdir)/'`sbc/sbc_primitives.c
+
+sbc/sbc_libsbc_la-sbc_primitives_mmx.lo: sbc/sbc_primitives_mmx.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc_primitives_mmx.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_mmx.Tpo -c -o sbc/sbc_libsbc_la-sbc_primitives_mmx.lo `test -f 'sbc/sbc_primitives_mmx.c' || echo '$(srcdir)/'`sbc/sbc_primitives_mmx.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_mmx.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_mmx.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sbc/sbc_primitives_mmx.c' object='sbc/sbc_libsbc_la-sbc_primitives_mmx.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc_primitives_mmx.lo `test -f 'sbc/sbc_primitives_mmx.c' || echo '$(srcdir)/'`sbc/sbc_primitives_mmx.c
+
+sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo: sbc/sbc_primitives_iwmmxt.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_iwmmxt.Tpo -c -o sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo `test -f 'sbc/sbc_primitives_iwmmxt.c' || echo '$(srcdir)/'`sbc/sbc_primitives_iwmmxt.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_iwmmxt.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_iwmmxt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sbc/sbc_primitives_iwmmxt.c' object='sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc_primitives_iwmmxt.lo `test -f 'sbc/sbc_primitives_iwmmxt.c' || echo '$(srcdir)/'`sbc/sbc_primitives_iwmmxt.c
+
+sbc/sbc_libsbc_la-sbc_primitives_neon.lo: sbc/sbc_primitives_neon.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc_primitives_neon.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_neon.Tpo -c -o sbc/sbc_libsbc_la-sbc_primitives_neon.lo `test -f 'sbc/sbc_primitives_neon.c' || echo '$(srcdir)/'`sbc/sbc_primitives_neon.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_neon.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_neon.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sbc/sbc_primitives_neon.c' object='sbc/sbc_libsbc_la-sbc_primitives_neon.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc_primitives_neon.lo `test -f 'sbc/sbc_primitives_neon.c' || echo '$(srcdir)/'`sbc/sbc_primitives_neon.c
+
+sbc/sbc_libsbc_la-sbc_primitives_armv6.lo: sbc/sbc_primitives_armv6.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -MT sbc/sbc_libsbc_la-sbc_primitives_armv6.lo -MD -MP -MF sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_armv6.Tpo -c -o sbc/sbc_libsbc_la-sbc_primitives_armv6.lo `test -f 'sbc/sbc_primitives_armv6.c' || echo '$(srcdir)/'`sbc/sbc_primitives_armv6.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_armv6.Tpo sbc/$(DEPDIR)/sbc_libsbc_la-sbc_primitives_armv6.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sbc/sbc_primitives_armv6.c' object='sbc/sbc_libsbc_la-sbc_primitives_armv6.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(sbc_libsbc_la_CFLAGS) $(CFLAGS) -c -o sbc/sbc_libsbc_la-sbc_primitives_armv6.lo `test -f 'sbc/sbc_primitives_armv6.c' || echo '$(srcdir)/'`sbc/sbc_primitives_armv6.c
+
+gdbus/bluetoothd-mainloop.o: gdbus/mainloop.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT gdbus/bluetoothd-mainloop.o -MD -MP -MF gdbus/$(DEPDIR)/bluetoothd-mainloop.Tpo -c -o gdbus/bluetoothd-mainloop.o `test -f 'gdbus/mainloop.c' || echo '$(srcdir)/'`gdbus/mainloop.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) gdbus/$(DEPDIR)/bluetoothd-mainloop.Tpo gdbus/$(DEPDIR)/bluetoothd-mainloop.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='gdbus/mainloop.c' object='gdbus/bluetoothd-mainloop.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o gdbus/bluetoothd-mainloop.o `test -f 'gdbus/mainloop.c' || echo '$(srcdir)/'`gdbus/mainloop.c
+
+gdbus/bluetoothd-mainloop.obj: gdbus/mainloop.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT gdbus/bluetoothd-mainloop.obj -MD -MP -MF gdbus/$(DEPDIR)/bluetoothd-mainloop.Tpo -c -o gdbus/bluetoothd-mainloop.obj `if test -f 'gdbus/mainloop.c'; then $(CYGPATH_W) 'gdbus/mainloop.c'; else $(CYGPATH_W) '$(srcdir)/gdbus/mainloop.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) gdbus/$(DEPDIR)/bluetoothd-mainloop.Tpo gdbus/$(DEPDIR)/bluetoothd-mainloop.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='gdbus/mainloop.c' object='gdbus/bluetoothd-mainloop.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o gdbus/bluetoothd-mainloop.obj `if test -f 'gdbus/mainloop.c'; then $(CYGPATH_W) 'gdbus/mainloop.c'; else $(CYGPATH_W) '$(srcdir)/gdbus/mainloop.c'; fi`
+
+gdbus/bluetoothd-watch.o: gdbus/watch.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT gdbus/bluetoothd-watch.o -MD -MP -MF gdbus/$(DEPDIR)/bluetoothd-watch.Tpo -c -o gdbus/bluetoothd-watch.o `test -f 'gdbus/watch.c' || echo '$(srcdir)/'`gdbus/watch.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) gdbus/$(DEPDIR)/bluetoothd-watch.Tpo gdbus/$(DEPDIR)/bluetoothd-watch.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='gdbus/watch.c' object='gdbus/bluetoothd-watch.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o gdbus/bluetoothd-watch.o `test -f 'gdbus/watch.c' || echo '$(srcdir)/'`gdbus/watch.c
+
+gdbus/bluetoothd-watch.obj: gdbus/watch.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT gdbus/bluetoothd-watch.obj -MD -MP -MF gdbus/$(DEPDIR)/bluetoothd-watch.Tpo -c -o gdbus/bluetoothd-watch.obj `if test -f 'gdbus/watch.c'; then $(CYGPATH_W) 'gdbus/watch.c'; else $(CYGPATH_W) '$(srcdir)/gdbus/watch.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) gdbus/$(DEPDIR)/bluetoothd-watch.Tpo gdbus/$(DEPDIR)/bluetoothd-watch.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='gdbus/watch.c' object='gdbus/bluetoothd-watch.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o gdbus/bluetoothd-watch.obj `if test -f 'gdbus/watch.c'; then $(CYGPATH_W) 'gdbus/watch.c'; else $(CYGPATH_W) '$(srcdir)/gdbus/watch.c'; fi`
+
+gdbus/bluetoothd-object.o: gdbus/object.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT gdbus/bluetoothd-object.o -MD -MP -MF gdbus/$(DEPDIR)/bluetoothd-object.Tpo -c -o gdbus/bluetoothd-object.o `test -f 'gdbus/object.c' || echo '$(srcdir)/'`gdbus/object.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) gdbus/$(DEPDIR)/bluetoothd-object.Tpo gdbus/$(DEPDIR)/bluetoothd-object.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='gdbus/object.c' object='gdbus/bluetoothd-object.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o gdbus/bluetoothd-object.o `test -f 'gdbus/object.c' || echo '$(srcdir)/'`gdbus/object.c
+
+gdbus/bluetoothd-object.obj: gdbus/object.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT gdbus/bluetoothd-object.obj -MD -MP -MF gdbus/$(DEPDIR)/bluetoothd-object.Tpo -c -o gdbus/bluetoothd-object.obj `if test -f 'gdbus/object.c'; then $(CYGPATH_W) 'gdbus/object.c'; else $(CYGPATH_W) '$(srcdir)/gdbus/object.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) gdbus/$(DEPDIR)/bluetoothd-object.Tpo gdbus/$(DEPDIR)/bluetoothd-object.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='gdbus/object.c' object='gdbus/bluetoothd-object.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o gdbus/bluetoothd-object.obj `if test -f 'gdbus/object.c'; then $(CYGPATH_W) 'gdbus/object.c'; else $(CYGPATH_W) '$(srcdir)/gdbus/object.c'; fi`
+
+gdbus/bluetoothd-polkit.o: gdbus/polkit.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT gdbus/bluetoothd-polkit.o -MD -MP -MF gdbus/$(DEPDIR)/bluetoothd-polkit.Tpo -c -o gdbus/bluetoothd-polkit.o `test -f 'gdbus/polkit.c' || echo '$(srcdir)/'`gdbus/polkit.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) gdbus/$(DEPDIR)/bluetoothd-polkit.Tpo gdbus/$(DEPDIR)/bluetoothd-polkit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='gdbus/polkit.c' object='gdbus/bluetoothd-polkit.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o gdbus/bluetoothd-polkit.o `test -f 'gdbus/polkit.c' || echo '$(srcdir)/'`gdbus/polkit.c
+
+gdbus/bluetoothd-polkit.obj: gdbus/polkit.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT gdbus/bluetoothd-polkit.obj -MD -MP -MF gdbus/$(DEPDIR)/bluetoothd-polkit.Tpo -c -o gdbus/bluetoothd-polkit.obj `if test -f 'gdbus/polkit.c'; then $(CYGPATH_W) 'gdbus/polkit.c'; else $(CYGPATH_W) '$(srcdir)/gdbus/polkit.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) gdbus/$(DEPDIR)/bluetoothd-polkit.Tpo gdbus/$(DEPDIR)/bluetoothd-polkit.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='gdbus/polkit.c' object='gdbus/bluetoothd-polkit.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o gdbus/bluetoothd-polkit.obj `if test -f 'gdbus/polkit.c'; then $(CYGPATH_W) 'gdbus/polkit.c'; else $(CYGPATH_W) '$(srcdir)/gdbus/polkit.c'; fi`
+
+plugins/bluetoothd-pnat.o: plugins/pnat.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-pnat.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-pnat.Tpo -c -o plugins/bluetoothd-pnat.o `test -f 'plugins/pnat.c' || echo '$(srcdir)/'`plugins/pnat.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-pnat.Tpo plugins/$(DEPDIR)/bluetoothd-pnat.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/pnat.c' object='plugins/bluetoothd-pnat.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-pnat.o `test -f 'plugins/pnat.c' || echo '$(srcdir)/'`plugins/pnat.c
+
+plugins/bluetoothd-pnat.obj: plugins/pnat.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-pnat.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-pnat.Tpo -c -o plugins/bluetoothd-pnat.obj `if test -f 'plugins/pnat.c'; then $(CYGPATH_W) 'plugins/pnat.c'; else $(CYGPATH_W) '$(srcdir)/plugins/pnat.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-pnat.Tpo plugins/$(DEPDIR)/bluetoothd-pnat.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/pnat.c' object='plugins/bluetoothd-pnat.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-pnat.obj `if test -f 'plugins/pnat.c'; then $(CYGPATH_W) 'plugins/pnat.c'; else $(CYGPATH_W) '$(srcdir)/plugins/pnat.c'; fi`
+
+audio/bluetoothd-main.o: audio/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-main.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-main.Tpo -c -o audio/bluetoothd-main.o `test -f 'audio/main.c' || echo '$(srcdir)/'`audio/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-main.Tpo audio/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/main.c' object='audio/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-main.o `test -f 'audio/main.c' || echo '$(srcdir)/'`audio/main.c
+
+audio/bluetoothd-main.obj: audio/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-main.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-main.Tpo -c -o audio/bluetoothd-main.obj `if test -f 'audio/main.c'; then $(CYGPATH_W) 'audio/main.c'; else $(CYGPATH_W) '$(srcdir)/audio/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-main.Tpo audio/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/main.c' object='audio/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-main.obj `if test -f 'audio/main.c'; then $(CYGPATH_W) 'audio/main.c'; else $(CYGPATH_W) '$(srcdir)/audio/main.c'; fi`
+
+audio/bluetoothd-manager.o: audio/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-manager.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-manager.Tpo -c -o audio/bluetoothd-manager.o `test -f 'audio/manager.c' || echo '$(srcdir)/'`audio/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-manager.Tpo audio/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/manager.c' object='audio/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-manager.o `test -f 'audio/manager.c' || echo '$(srcdir)/'`audio/manager.c
+
+audio/bluetoothd-manager.obj: audio/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-manager.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-manager.Tpo -c -o audio/bluetoothd-manager.obj `if test -f 'audio/manager.c'; then $(CYGPATH_W) 'audio/manager.c'; else $(CYGPATH_W) '$(srcdir)/audio/manager.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-manager.Tpo audio/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/manager.c' object='audio/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-manager.obj `if test -f 'audio/manager.c'; then $(CYGPATH_W) 'audio/manager.c'; else $(CYGPATH_W) '$(srcdir)/audio/manager.c'; fi`
+
+audio/bluetoothd-gateway.o: audio/gateway.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-gateway.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-gateway.Tpo -c -o audio/bluetoothd-gateway.o `test -f 'audio/gateway.c' || echo '$(srcdir)/'`audio/gateway.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-gateway.Tpo audio/$(DEPDIR)/bluetoothd-gateway.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/gateway.c' object='audio/bluetoothd-gateway.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-gateway.o `test -f 'audio/gateway.c' || echo '$(srcdir)/'`audio/gateway.c
+
+audio/bluetoothd-gateway.obj: audio/gateway.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-gateway.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-gateway.Tpo -c -o audio/bluetoothd-gateway.obj `if test -f 'audio/gateway.c'; then $(CYGPATH_W) 'audio/gateway.c'; else $(CYGPATH_W) '$(srcdir)/audio/gateway.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-gateway.Tpo audio/$(DEPDIR)/bluetoothd-gateway.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/gateway.c' object='audio/bluetoothd-gateway.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-gateway.obj `if test -f 'audio/gateway.c'; then $(CYGPATH_W) 'audio/gateway.c'; else $(CYGPATH_W) '$(srcdir)/audio/gateway.c'; fi`
+
+audio/bluetoothd-headset.o: audio/headset.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-headset.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-headset.Tpo -c -o audio/bluetoothd-headset.o `test -f 'audio/headset.c' || echo '$(srcdir)/'`audio/headset.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-headset.Tpo audio/$(DEPDIR)/bluetoothd-headset.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/headset.c' object='audio/bluetoothd-headset.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-headset.o `test -f 'audio/headset.c' || echo '$(srcdir)/'`audio/headset.c
+
+audio/bluetoothd-headset.obj: audio/headset.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-headset.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-headset.Tpo -c -o audio/bluetoothd-headset.obj `if test -f 'audio/headset.c'; then $(CYGPATH_W) 'audio/headset.c'; else $(CYGPATH_W) '$(srcdir)/audio/headset.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-headset.Tpo audio/$(DEPDIR)/bluetoothd-headset.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/headset.c' object='audio/bluetoothd-headset.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-headset.obj `if test -f 'audio/headset.c'; then $(CYGPATH_W) 'audio/headset.c'; else $(CYGPATH_W) '$(srcdir)/audio/headset.c'; fi`
+
+audio/bluetoothd-control.o: audio/control.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-control.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-control.Tpo -c -o audio/bluetoothd-control.o `test -f 'audio/control.c' || echo '$(srcdir)/'`audio/control.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-control.Tpo audio/$(DEPDIR)/bluetoothd-control.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/control.c' object='audio/bluetoothd-control.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-control.o `test -f 'audio/control.c' || echo '$(srcdir)/'`audio/control.c
+
+audio/bluetoothd-control.obj: audio/control.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-control.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-control.Tpo -c -o audio/bluetoothd-control.obj `if test -f 'audio/control.c'; then $(CYGPATH_W) 'audio/control.c'; else $(CYGPATH_W) '$(srcdir)/audio/control.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-control.Tpo audio/$(DEPDIR)/bluetoothd-control.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/control.c' object='audio/bluetoothd-control.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-control.obj `if test -f 'audio/control.c'; then $(CYGPATH_W) 'audio/control.c'; else $(CYGPATH_W) '$(srcdir)/audio/control.c'; fi`
+
+audio/bluetoothd-avctp.o: audio/avctp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-avctp.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-avctp.Tpo -c -o audio/bluetoothd-avctp.o `test -f 'audio/avctp.c' || echo '$(srcdir)/'`audio/avctp.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-avctp.Tpo audio/$(DEPDIR)/bluetoothd-avctp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/avctp.c' object='audio/bluetoothd-avctp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-avctp.o `test -f 'audio/avctp.c' || echo '$(srcdir)/'`audio/avctp.c
+
+audio/bluetoothd-avctp.obj: audio/avctp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-avctp.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-avctp.Tpo -c -o audio/bluetoothd-avctp.obj `if test -f 'audio/avctp.c'; then $(CYGPATH_W) 'audio/avctp.c'; else $(CYGPATH_W) '$(srcdir)/audio/avctp.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-avctp.Tpo audio/$(DEPDIR)/bluetoothd-avctp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/avctp.c' object='audio/bluetoothd-avctp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-avctp.obj `if test -f 'audio/avctp.c'; then $(CYGPATH_W) 'audio/avctp.c'; else $(CYGPATH_W) '$(srcdir)/audio/avctp.c'; fi`
+
+audio/bluetoothd-avrcp.o: audio/avrcp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-avrcp.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-avrcp.Tpo -c -o audio/bluetoothd-avrcp.o `test -f 'audio/avrcp.c' || echo '$(srcdir)/'`audio/avrcp.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-avrcp.Tpo audio/$(DEPDIR)/bluetoothd-avrcp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/avrcp.c' object='audio/bluetoothd-avrcp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-avrcp.o `test -f 'audio/avrcp.c' || echo '$(srcdir)/'`audio/avrcp.c
+
+audio/bluetoothd-avrcp.obj: audio/avrcp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-avrcp.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-avrcp.Tpo -c -o audio/bluetoothd-avrcp.obj `if test -f 'audio/avrcp.c'; then $(CYGPATH_W) 'audio/avrcp.c'; else $(CYGPATH_W) '$(srcdir)/audio/avrcp.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-avrcp.Tpo audio/$(DEPDIR)/bluetoothd-avrcp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/avrcp.c' object='audio/bluetoothd-avrcp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-avrcp.obj `if test -f 'audio/avrcp.c'; then $(CYGPATH_W) 'audio/avrcp.c'; else $(CYGPATH_W) '$(srcdir)/audio/avrcp.c'; fi`
+
+audio/bluetoothd-device.o: audio/device.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-device.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-device.Tpo -c -o audio/bluetoothd-device.o `test -f 'audio/device.c' || echo '$(srcdir)/'`audio/device.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-device.Tpo audio/$(DEPDIR)/bluetoothd-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/device.c' object='audio/bluetoothd-device.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-device.o `test -f 'audio/device.c' || echo '$(srcdir)/'`audio/device.c
+
+audio/bluetoothd-device.obj: audio/device.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-device.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-device.Tpo -c -o audio/bluetoothd-device.obj `if test -f 'audio/device.c'; then $(CYGPATH_W) 'audio/device.c'; else $(CYGPATH_W) '$(srcdir)/audio/device.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-device.Tpo audio/$(DEPDIR)/bluetoothd-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/device.c' object='audio/bluetoothd-device.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-device.obj `if test -f 'audio/device.c'; then $(CYGPATH_W) 'audio/device.c'; else $(CYGPATH_W) '$(srcdir)/audio/device.c'; fi`
+
+audio/bluetoothd-source.o: audio/source.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-source.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-source.Tpo -c -o audio/bluetoothd-source.o `test -f 'audio/source.c' || echo '$(srcdir)/'`audio/source.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-source.Tpo audio/$(DEPDIR)/bluetoothd-source.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/source.c' object='audio/bluetoothd-source.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-source.o `test -f 'audio/source.c' || echo '$(srcdir)/'`audio/source.c
+
+audio/bluetoothd-source.obj: audio/source.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-source.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-source.Tpo -c -o audio/bluetoothd-source.obj `if test -f 'audio/source.c'; then $(CYGPATH_W) 'audio/source.c'; else $(CYGPATH_W) '$(srcdir)/audio/source.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-source.Tpo audio/$(DEPDIR)/bluetoothd-source.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/source.c' object='audio/bluetoothd-source.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-source.obj `if test -f 'audio/source.c'; then $(CYGPATH_W) 'audio/source.c'; else $(CYGPATH_W) '$(srcdir)/audio/source.c'; fi`
+
+audio/bluetoothd-sink.o: audio/sink.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-sink.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-sink.Tpo -c -o audio/bluetoothd-sink.o `test -f 'audio/sink.c' || echo '$(srcdir)/'`audio/sink.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-sink.Tpo audio/$(DEPDIR)/bluetoothd-sink.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/sink.c' object='audio/bluetoothd-sink.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-sink.o `test -f 'audio/sink.c' || echo '$(srcdir)/'`audio/sink.c
+
+audio/bluetoothd-sink.obj: audio/sink.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-sink.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-sink.Tpo -c -o audio/bluetoothd-sink.obj `if test -f 'audio/sink.c'; then $(CYGPATH_W) 'audio/sink.c'; else $(CYGPATH_W) '$(srcdir)/audio/sink.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-sink.Tpo audio/$(DEPDIR)/bluetoothd-sink.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/sink.c' object='audio/bluetoothd-sink.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-sink.obj `if test -f 'audio/sink.c'; then $(CYGPATH_W) 'audio/sink.c'; else $(CYGPATH_W) '$(srcdir)/audio/sink.c'; fi`
+
+audio/bluetoothd-a2dp.o: audio/a2dp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-a2dp.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-a2dp.Tpo -c -o audio/bluetoothd-a2dp.o `test -f 'audio/a2dp.c' || echo '$(srcdir)/'`audio/a2dp.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-a2dp.Tpo audio/$(DEPDIR)/bluetoothd-a2dp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/a2dp.c' object='audio/bluetoothd-a2dp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-a2dp.o `test -f 'audio/a2dp.c' || echo '$(srcdir)/'`audio/a2dp.c
+
+audio/bluetoothd-a2dp.obj: audio/a2dp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-a2dp.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-a2dp.Tpo -c -o audio/bluetoothd-a2dp.obj `if test -f 'audio/a2dp.c'; then $(CYGPATH_W) 'audio/a2dp.c'; else $(CYGPATH_W) '$(srcdir)/audio/a2dp.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-a2dp.Tpo audio/$(DEPDIR)/bluetoothd-a2dp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/a2dp.c' object='audio/bluetoothd-a2dp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-a2dp.obj `if test -f 'audio/a2dp.c'; then $(CYGPATH_W) 'audio/a2dp.c'; else $(CYGPATH_W) '$(srcdir)/audio/a2dp.c'; fi`
+
+audio/bluetoothd-avdtp.o: audio/avdtp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-avdtp.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-avdtp.Tpo -c -o audio/bluetoothd-avdtp.o `test -f 'audio/avdtp.c' || echo '$(srcdir)/'`audio/avdtp.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-avdtp.Tpo audio/$(DEPDIR)/bluetoothd-avdtp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/avdtp.c' object='audio/bluetoothd-avdtp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-avdtp.o `test -f 'audio/avdtp.c' || echo '$(srcdir)/'`audio/avdtp.c
+
+audio/bluetoothd-avdtp.obj: audio/avdtp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-avdtp.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-avdtp.Tpo -c -o audio/bluetoothd-avdtp.obj `if test -f 'audio/avdtp.c'; then $(CYGPATH_W) 'audio/avdtp.c'; else $(CYGPATH_W) '$(srcdir)/audio/avdtp.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-avdtp.Tpo audio/$(DEPDIR)/bluetoothd-avdtp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/avdtp.c' object='audio/bluetoothd-avdtp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-avdtp.obj `if test -f 'audio/avdtp.c'; then $(CYGPATH_W) 'audio/avdtp.c'; else $(CYGPATH_W) '$(srcdir)/audio/avdtp.c'; fi`
+
+audio/bluetoothd-ipc.o: audio/ipc.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-ipc.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-ipc.Tpo -c -o audio/bluetoothd-ipc.o `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-ipc.Tpo audio/$(DEPDIR)/bluetoothd-ipc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/ipc.c' object='audio/bluetoothd-ipc.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-ipc.o `test -f 'audio/ipc.c' || echo '$(srcdir)/'`audio/ipc.c
+
+audio/bluetoothd-ipc.obj: audio/ipc.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-ipc.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-ipc.Tpo -c -o audio/bluetoothd-ipc.obj `if test -f 'audio/ipc.c'; then $(CYGPATH_W) 'audio/ipc.c'; else $(CYGPATH_W) '$(srcdir)/audio/ipc.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-ipc.Tpo audio/$(DEPDIR)/bluetoothd-ipc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/ipc.c' object='audio/bluetoothd-ipc.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-ipc.obj `if test -f 'audio/ipc.c'; then $(CYGPATH_W) 'audio/ipc.c'; else $(CYGPATH_W) '$(srcdir)/audio/ipc.c'; fi`
+
+audio/bluetoothd-unix.o: audio/unix.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-unix.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-unix.Tpo -c -o audio/bluetoothd-unix.o `test -f 'audio/unix.c' || echo '$(srcdir)/'`audio/unix.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-unix.Tpo audio/$(DEPDIR)/bluetoothd-unix.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/unix.c' object='audio/bluetoothd-unix.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-unix.o `test -f 'audio/unix.c' || echo '$(srcdir)/'`audio/unix.c
+
+audio/bluetoothd-unix.obj: audio/unix.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-unix.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-unix.Tpo -c -o audio/bluetoothd-unix.obj `if test -f 'audio/unix.c'; then $(CYGPATH_W) 'audio/unix.c'; else $(CYGPATH_W) '$(srcdir)/audio/unix.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-unix.Tpo audio/$(DEPDIR)/bluetoothd-unix.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/unix.c' object='audio/bluetoothd-unix.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-unix.obj `if test -f 'audio/unix.c'; then $(CYGPATH_W) 'audio/unix.c'; else $(CYGPATH_W) '$(srcdir)/audio/unix.c'; fi`
+
+audio/bluetoothd-media.o: audio/media.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-media.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-media.Tpo -c -o audio/bluetoothd-media.o `test -f 'audio/media.c' || echo '$(srcdir)/'`audio/media.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-media.Tpo audio/$(DEPDIR)/bluetoothd-media.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/media.c' object='audio/bluetoothd-media.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-media.o `test -f 'audio/media.c' || echo '$(srcdir)/'`audio/media.c
+
+audio/bluetoothd-media.obj: audio/media.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-media.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-media.Tpo -c -o audio/bluetoothd-media.obj `if test -f 'audio/media.c'; then $(CYGPATH_W) 'audio/media.c'; else $(CYGPATH_W) '$(srcdir)/audio/media.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-media.Tpo audio/$(DEPDIR)/bluetoothd-media.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/media.c' object='audio/bluetoothd-media.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-media.obj `if test -f 'audio/media.c'; then $(CYGPATH_W) 'audio/media.c'; else $(CYGPATH_W) '$(srcdir)/audio/media.c'; fi`
+
+audio/bluetoothd-transport.o: audio/transport.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-transport.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-transport.Tpo -c -o audio/bluetoothd-transport.o `test -f 'audio/transport.c' || echo '$(srcdir)/'`audio/transport.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-transport.Tpo audio/$(DEPDIR)/bluetoothd-transport.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/transport.c' object='audio/bluetoothd-transport.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-transport.o `test -f 'audio/transport.c' || echo '$(srcdir)/'`audio/transport.c
+
+audio/bluetoothd-transport.obj: audio/transport.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-transport.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-transport.Tpo -c -o audio/bluetoothd-transport.obj `if test -f 'audio/transport.c'; then $(CYGPATH_W) 'audio/transport.c'; else $(CYGPATH_W) '$(srcdir)/audio/transport.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-transport.Tpo audio/$(DEPDIR)/bluetoothd-transport.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/transport.c' object='audio/bluetoothd-transport.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-transport.obj `if test -f 'audio/transport.c'; then $(CYGPATH_W) 'audio/transport.c'; else $(CYGPATH_W) '$(srcdir)/audio/transport.c'; fi`
+
+sap/bluetoothd-main.o: sap/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT sap/bluetoothd-main.o -MD -MP -MF sap/$(DEPDIR)/bluetoothd-main.Tpo -c -o sap/bluetoothd-main.o `test -f 'sap/main.c' || echo '$(srcdir)/'`sap/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sap/$(DEPDIR)/bluetoothd-main.Tpo sap/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sap/main.c' object='sap/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o sap/bluetoothd-main.o `test -f 'sap/main.c' || echo '$(srcdir)/'`sap/main.c
+
+sap/bluetoothd-main.obj: sap/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT sap/bluetoothd-main.obj -MD -MP -MF sap/$(DEPDIR)/bluetoothd-main.Tpo -c -o sap/bluetoothd-main.obj `if test -f 'sap/main.c'; then $(CYGPATH_W) 'sap/main.c'; else $(CYGPATH_W) '$(srcdir)/sap/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sap/$(DEPDIR)/bluetoothd-main.Tpo sap/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sap/main.c' object='sap/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o sap/bluetoothd-main.obj `if test -f 'sap/main.c'; then $(CYGPATH_W) 'sap/main.c'; else $(CYGPATH_W) '$(srcdir)/sap/main.c'; fi`
+
+sap/bluetoothd-manager.o: sap/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT sap/bluetoothd-manager.o -MD -MP -MF sap/$(DEPDIR)/bluetoothd-manager.Tpo -c -o sap/bluetoothd-manager.o `test -f 'sap/manager.c' || echo '$(srcdir)/'`sap/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sap/$(DEPDIR)/bluetoothd-manager.Tpo sap/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sap/manager.c' object='sap/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o sap/bluetoothd-manager.o `test -f 'sap/manager.c' || echo '$(srcdir)/'`sap/manager.c
+
+sap/bluetoothd-manager.obj: sap/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT sap/bluetoothd-manager.obj -MD -MP -MF sap/$(DEPDIR)/bluetoothd-manager.Tpo -c -o sap/bluetoothd-manager.obj `if test -f 'sap/manager.c'; then $(CYGPATH_W) 'sap/manager.c'; else $(CYGPATH_W) '$(srcdir)/sap/manager.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sap/$(DEPDIR)/bluetoothd-manager.Tpo sap/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sap/manager.c' object='sap/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o sap/bluetoothd-manager.obj `if test -f 'sap/manager.c'; then $(CYGPATH_W) 'sap/manager.c'; else $(CYGPATH_W) '$(srcdir)/sap/manager.c'; fi`
+
+sap/bluetoothd-server.o: sap/server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT sap/bluetoothd-server.o -MD -MP -MF sap/$(DEPDIR)/bluetoothd-server.Tpo -c -o sap/bluetoothd-server.o `test -f 'sap/server.c' || echo '$(srcdir)/'`sap/server.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sap/$(DEPDIR)/bluetoothd-server.Tpo sap/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sap/server.c' object='sap/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o sap/bluetoothd-server.o `test -f 'sap/server.c' || echo '$(srcdir)/'`sap/server.c
+
+sap/bluetoothd-server.obj: sap/server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT sap/bluetoothd-server.obj -MD -MP -MF sap/$(DEPDIR)/bluetoothd-server.Tpo -c -o sap/bluetoothd-server.obj `if test -f 'sap/server.c'; then $(CYGPATH_W) 'sap/server.c'; else $(CYGPATH_W) '$(srcdir)/sap/server.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sap/$(DEPDIR)/bluetoothd-server.Tpo sap/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sap/server.c' object='sap/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o sap/bluetoothd-server.obj `if test -f 'sap/server.c'; then $(CYGPATH_W) 'sap/server.c'; else $(CYGPATH_W) '$(srcdir)/sap/server.c'; fi`
+
+input/bluetoothd-main.o: input/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT input/bluetoothd-main.o -MD -MP -MF input/$(DEPDIR)/bluetoothd-main.Tpo -c -o input/bluetoothd-main.o `test -f 'input/main.c' || echo '$(srcdir)/'`input/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) input/$(DEPDIR)/bluetoothd-main.Tpo input/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='input/main.c' object='input/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o input/bluetoothd-main.o `test -f 'input/main.c' || echo '$(srcdir)/'`input/main.c
+
+input/bluetoothd-main.obj: input/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT input/bluetoothd-main.obj -MD -MP -MF input/$(DEPDIR)/bluetoothd-main.Tpo -c -o input/bluetoothd-main.obj `if test -f 'input/main.c'; then $(CYGPATH_W) 'input/main.c'; else $(CYGPATH_W) '$(srcdir)/input/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) input/$(DEPDIR)/bluetoothd-main.Tpo input/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='input/main.c' object='input/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o input/bluetoothd-main.obj `if test -f 'input/main.c'; then $(CYGPATH_W) 'input/main.c'; else $(CYGPATH_W) '$(srcdir)/input/main.c'; fi`
+
+input/bluetoothd-manager.o: input/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT input/bluetoothd-manager.o -MD -MP -MF input/$(DEPDIR)/bluetoothd-manager.Tpo -c -o input/bluetoothd-manager.o `test -f 'input/manager.c' || echo '$(srcdir)/'`input/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) input/$(DEPDIR)/bluetoothd-manager.Tpo input/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='input/manager.c' object='input/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o input/bluetoothd-manager.o `test -f 'input/manager.c' || echo '$(srcdir)/'`input/manager.c
+
+input/bluetoothd-manager.obj: input/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT input/bluetoothd-manager.obj -MD -MP -MF input/$(DEPDIR)/bluetoothd-manager.Tpo -c -o input/bluetoothd-manager.obj `if test -f 'input/manager.c'; then $(CYGPATH_W) 'input/manager.c'; else $(CYGPATH_W) '$(srcdir)/input/manager.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) input/$(DEPDIR)/bluetoothd-manager.Tpo input/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='input/manager.c' object='input/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o input/bluetoothd-manager.obj `if test -f 'input/manager.c'; then $(CYGPATH_W) 'input/manager.c'; else $(CYGPATH_W) '$(srcdir)/input/manager.c'; fi`
+
+input/bluetoothd-server.o: input/server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT input/bluetoothd-server.o -MD -MP -MF input/$(DEPDIR)/bluetoothd-server.Tpo -c -o input/bluetoothd-server.o `test -f 'input/server.c' || echo '$(srcdir)/'`input/server.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) input/$(DEPDIR)/bluetoothd-server.Tpo input/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='input/server.c' object='input/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o input/bluetoothd-server.o `test -f 'input/server.c' || echo '$(srcdir)/'`input/server.c
+
+input/bluetoothd-server.obj: input/server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT input/bluetoothd-server.obj -MD -MP -MF input/$(DEPDIR)/bluetoothd-server.Tpo -c -o input/bluetoothd-server.obj `if test -f 'input/server.c'; then $(CYGPATH_W) 'input/server.c'; else $(CYGPATH_W) '$(srcdir)/input/server.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) input/$(DEPDIR)/bluetoothd-server.Tpo input/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='input/server.c' object='input/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o input/bluetoothd-server.obj `if test -f 'input/server.c'; then $(CYGPATH_W) 'input/server.c'; else $(CYGPATH_W) '$(srcdir)/input/server.c'; fi`
+
+input/bluetoothd-device.o: input/device.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT input/bluetoothd-device.o -MD -MP -MF input/$(DEPDIR)/bluetoothd-device.Tpo -c -o input/bluetoothd-device.o `test -f 'input/device.c' || echo '$(srcdir)/'`input/device.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) input/$(DEPDIR)/bluetoothd-device.Tpo input/$(DEPDIR)/bluetoothd-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='input/device.c' object='input/bluetoothd-device.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o input/bluetoothd-device.o `test -f 'input/device.c' || echo '$(srcdir)/'`input/device.c
+
+input/bluetoothd-device.obj: input/device.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT input/bluetoothd-device.obj -MD -MP -MF input/$(DEPDIR)/bluetoothd-device.Tpo -c -o input/bluetoothd-device.obj `if test -f 'input/device.c'; then $(CYGPATH_W) 'input/device.c'; else $(CYGPATH_W) '$(srcdir)/input/device.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) input/$(DEPDIR)/bluetoothd-device.Tpo input/$(DEPDIR)/bluetoothd-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='input/device.c' object='input/bluetoothd-device.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o input/bluetoothd-device.obj `if test -f 'input/device.c'; then $(CYGPATH_W) 'input/device.c'; else $(CYGPATH_W) '$(srcdir)/input/device.c'; fi`
+
+input/bluetoothd-fakehid.o: input/fakehid.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT input/bluetoothd-fakehid.o -MD -MP -MF input/$(DEPDIR)/bluetoothd-fakehid.Tpo -c -o input/bluetoothd-fakehid.o `test -f 'input/fakehid.c' || echo '$(srcdir)/'`input/fakehid.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) input/$(DEPDIR)/bluetoothd-fakehid.Tpo input/$(DEPDIR)/bluetoothd-fakehid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='input/fakehid.c' object='input/bluetoothd-fakehid.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o input/bluetoothd-fakehid.o `test -f 'input/fakehid.c' || echo '$(srcdir)/'`input/fakehid.c
+
+input/bluetoothd-fakehid.obj: input/fakehid.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT input/bluetoothd-fakehid.obj -MD -MP -MF input/$(DEPDIR)/bluetoothd-fakehid.Tpo -c -o input/bluetoothd-fakehid.obj `if test -f 'input/fakehid.c'; then $(CYGPATH_W) 'input/fakehid.c'; else $(CYGPATH_W) '$(srcdir)/input/fakehid.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) input/$(DEPDIR)/bluetoothd-fakehid.Tpo input/$(DEPDIR)/bluetoothd-fakehid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='input/fakehid.c' object='input/bluetoothd-fakehid.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o input/bluetoothd-fakehid.obj `if test -f 'input/fakehid.c'; then $(CYGPATH_W) 'input/fakehid.c'; else $(CYGPATH_W) '$(srcdir)/input/fakehid.c'; fi`
+
+serial/bluetoothd-main.o: serial/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT serial/bluetoothd-main.o -MD -MP -MF serial/$(DEPDIR)/bluetoothd-main.Tpo -c -o serial/bluetoothd-main.o `test -f 'serial/main.c' || echo '$(srcdir)/'`serial/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) serial/$(DEPDIR)/bluetoothd-main.Tpo serial/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='serial/main.c' object='serial/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o serial/bluetoothd-main.o `test -f 'serial/main.c' || echo '$(srcdir)/'`serial/main.c
+
+serial/bluetoothd-main.obj: serial/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT serial/bluetoothd-main.obj -MD -MP -MF serial/$(DEPDIR)/bluetoothd-main.Tpo -c -o serial/bluetoothd-main.obj `if test -f 'serial/main.c'; then $(CYGPATH_W) 'serial/main.c'; else $(CYGPATH_W) '$(srcdir)/serial/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) serial/$(DEPDIR)/bluetoothd-main.Tpo serial/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='serial/main.c' object='serial/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o serial/bluetoothd-main.obj `if test -f 'serial/main.c'; then $(CYGPATH_W) 'serial/main.c'; else $(CYGPATH_W) '$(srcdir)/serial/main.c'; fi`
+
+serial/bluetoothd-manager.o: serial/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT serial/bluetoothd-manager.o -MD -MP -MF serial/$(DEPDIR)/bluetoothd-manager.Tpo -c -o serial/bluetoothd-manager.o `test -f 'serial/manager.c' || echo '$(srcdir)/'`serial/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) serial/$(DEPDIR)/bluetoothd-manager.Tpo serial/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='serial/manager.c' object='serial/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o serial/bluetoothd-manager.o `test -f 'serial/manager.c' || echo '$(srcdir)/'`serial/manager.c
+
+serial/bluetoothd-manager.obj: serial/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT serial/bluetoothd-manager.obj -MD -MP -MF serial/$(DEPDIR)/bluetoothd-manager.Tpo -c -o serial/bluetoothd-manager.obj `if test -f 'serial/manager.c'; then $(CYGPATH_W) 'serial/manager.c'; else $(CYGPATH_W) '$(srcdir)/serial/manager.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) serial/$(DEPDIR)/bluetoothd-manager.Tpo serial/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='serial/manager.c' object='serial/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o serial/bluetoothd-manager.obj `if test -f 'serial/manager.c'; then $(CYGPATH_W) 'serial/manager.c'; else $(CYGPATH_W) '$(srcdir)/serial/manager.c'; fi`
+
+serial/bluetoothd-proxy.o: serial/proxy.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT serial/bluetoothd-proxy.o -MD -MP -MF serial/$(DEPDIR)/bluetoothd-proxy.Tpo -c -o serial/bluetoothd-proxy.o `test -f 'serial/proxy.c' || echo '$(srcdir)/'`serial/proxy.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) serial/$(DEPDIR)/bluetoothd-proxy.Tpo serial/$(DEPDIR)/bluetoothd-proxy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='serial/proxy.c' object='serial/bluetoothd-proxy.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o serial/bluetoothd-proxy.o `test -f 'serial/proxy.c' || echo '$(srcdir)/'`serial/proxy.c
+
+serial/bluetoothd-proxy.obj: serial/proxy.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT serial/bluetoothd-proxy.obj -MD -MP -MF serial/$(DEPDIR)/bluetoothd-proxy.Tpo -c -o serial/bluetoothd-proxy.obj `if test -f 'serial/proxy.c'; then $(CYGPATH_W) 'serial/proxy.c'; else $(CYGPATH_W) '$(srcdir)/serial/proxy.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) serial/$(DEPDIR)/bluetoothd-proxy.Tpo serial/$(DEPDIR)/bluetoothd-proxy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='serial/proxy.c' object='serial/bluetoothd-proxy.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o serial/bluetoothd-proxy.obj `if test -f 'serial/proxy.c'; then $(CYGPATH_W) 'serial/proxy.c'; else $(CYGPATH_W) '$(srcdir)/serial/proxy.c'; fi`
+
+serial/bluetoothd-port.o: serial/port.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT serial/bluetoothd-port.o -MD -MP -MF serial/$(DEPDIR)/bluetoothd-port.Tpo -c -o serial/bluetoothd-port.o `test -f 'serial/port.c' || echo '$(srcdir)/'`serial/port.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) serial/$(DEPDIR)/bluetoothd-port.Tpo serial/$(DEPDIR)/bluetoothd-port.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='serial/port.c' object='serial/bluetoothd-port.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o serial/bluetoothd-port.o `test -f 'serial/port.c' || echo '$(srcdir)/'`serial/port.c
+
+serial/bluetoothd-port.obj: serial/port.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT serial/bluetoothd-port.obj -MD -MP -MF serial/$(DEPDIR)/bluetoothd-port.Tpo -c -o serial/bluetoothd-port.obj `if test -f 'serial/port.c'; then $(CYGPATH_W) 'serial/port.c'; else $(CYGPATH_W) '$(srcdir)/serial/port.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) serial/$(DEPDIR)/bluetoothd-port.Tpo serial/$(DEPDIR)/bluetoothd-port.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='serial/port.c' object='serial/bluetoothd-port.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o serial/bluetoothd-port.obj `if test -f 'serial/port.c'; then $(CYGPATH_W) 'serial/port.c'; else $(CYGPATH_W) '$(srcdir)/serial/port.c'; fi`
+
+network/bluetoothd-main.o: network/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT network/bluetoothd-main.o -MD -MP -MF network/$(DEPDIR)/bluetoothd-main.Tpo -c -o network/bluetoothd-main.o `test -f 'network/main.c' || echo '$(srcdir)/'`network/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) network/$(DEPDIR)/bluetoothd-main.Tpo network/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='network/main.c' object='network/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o network/bluetoothd-main.o `test -f 'network/main.c' || echo '$(srcdir)/'`network/main.c
+
+network/bluetoothd-main.obj: network/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT network/bluetoothd-main.obj -MD -MP -MF network/$(DEPDIR)/bluetoothd-main.Tpo -c -o network/bluetoothd-main.obj `if test -f 'network/main.c'; then $(CYGPATH_W) 'network/main.c'; else $(CYGPATH_W) '$(srcdir)/network/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) network/$(DEPDIR)/bluetoothd-main.Tpo network/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='network/main.c' object='network/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o network/bluetoothd-main.obj `if test -f 'network/main.c'; then $(CYGPATH_W) 'network/main.c'; else $(CYGPATH_W) '$(srcdir)/network/main.c'; fi`
+
+network/bluetoothd-manager.o: network/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT network/bluetoothd-manager.o -MD -MP -MF network/$(DEPDIR)/bluetoothd-manager.Tpo -c -o network/bluetoothd-manager.o `test -f 'network/manager.c' || echo '$(srcdir)/'`network/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) network/$(DEPDIR)/bluetoothd-manager.Tpo network/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='network/manager.c' object='network/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o network/bluetoothd-manager.o `test -f 'network/manager.c' || echo '$(srcdir)/'`network/manager.c
+
+network/bluetoothd-manager.obj: network/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT network/bluetoothd-manager.obj -MD -MP -MF network/$(DEPDIR)/bluetoothd-manager.Tpo -c -o network/bluetoothd-manager.obj `if test -f 'network/manager.c'; then $(CYGPATH_W) 'network/manager.c'; else $(CYGPATH_W) '$(srcdir)/network/manager.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) network/$(DEPDIR)/bluetoothd-manager.Tpo network/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='network/manager.c' object='network/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o network/bluetoothd-manager.obj `if test -f 'network/manager.c'; then $(CYGPATH_W) 'network/manager.c'; else $(CYGPATH_W) '$(srcdir)/network/manager.c'; fi`
+
+network/bluetoothd-common.o: network/common.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT network/bluetoothd-common.o -MD -MP -MF network/$(DEPDIR)/bluetoothd-common.Tpo -c -o network/bluetoothd-common.o `test -f 'network/common.c' || echo '$(srcdir)/'`network/common.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) network/$(DEPDIR)/bluetoothd-common.Tpo network/$(DEPDIR)/bluetoothd-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='network/common.c' object='network/bluetoothd-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o network/bluetoothd-common.o `test -f 'network/common.c' || echo '$(srcdir)/'`network/common.c
+
+network/bluetoothd-common.obj: network/common.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT network/bluetoothd-common.obj -MD -MP -MF network/$(DEPDIR)/bluetoothd-common.Tpo -c -o network/bluetoothd-common.obj `if test -f 'network/common.c'; then $(CYGPATH_W) 'network/common.c'; else $(CYGPATH_W) '$(srcdir)/network/common.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) network/$(DEPDIR)/bluetoothd-common.Tpo network/$(DEPDIR)/bluetoothd-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='network/common.c' object='network/bluetoothd-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o network/bluetoothd-common.obj `if test -f 'network/common.c'; then $(CYGPATH_W) 'network/common.c'; else $(CYGPATH_W) '$(srcdir)/network/common.c'; fi`
+
+network/bluetoothd-server.o: network/server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT network/bluetoothd-server.o -MD -MP -MF network/$(DEPDIR)/bluetoothd-server.Tpo -c -o network/bluetoothd-server.o `test -f 'network/server.c' || echo '$(srcdir)/'`network/server.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) network/$(DEPDIR)/bluetoothd-server.Tpo network/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='network/server.c' object='network/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o network/bluetoothd-server.o `test -f 'network/server.c' || echo '$(srcdir)/'`network/server.c
+
+network/bluetoothd-server.obj: network/server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT network/bluetoothd-server.obj -MD -MP -MF network/$(DEPDIR)/bluetoothd-server.Tpo -c -o network/bluetoothd-server.obj `if test -f 'network/server.c'; then $(CYGPATH_W) 'network/server.c'; else $(CYGPATH_W) '$(srcdir)/network/server.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) network/$(DEPDIR)/bluetoothd-server.Tpo network/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='network/server.c' object='network/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o network/bluetoothd-server.obj `if test -f 'network/server.c'; then $(CYGPATH_W) 'network/server.c'; else $(CYGPATH_W) '$(srcdir)/network/server.c'; fi`
+
+network/bluetoothd-connection.o: network/connection.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT network/bluetoothd-connection.o -MD -MP -MF network/$(DEPDIR)/bluetoothd-connection.Tpo -c -o network/bluetoothd-connection.o `test -f 'network/connection.c' || echo '$(srcdir)/'`network/connection.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) network/$(DEPDIR)/bluetoothd-connection.Tpo network/$(DEPDIR)/bluetoothd-connection.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='network/connection.c' object='network/bluetoothd-connection.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o network/bluetoothd-connection.o `test -f 'network/connection.c' || echo '$(srcdir)/'`network/connection.c
+
+network/bluetoothd-connection.obj: network/connection.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT network/bluetoothd-connection.obj -MD -MP -MF network/$(DEPDIR)/bluetoothd-connection.Tpo -c -o network/bluetoothd-connection.obj `if test -f 'network/connection.c'; then $(CYGPATH_W) 'network/connection.c'; else $(CYGPATH_W) '$(srcdir)/network/connection.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) network/$(DEPDIR)/bluetoothd-connection.Tpo network/$(DEPDIR)/bluetoothd-connection.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='network/connection.c' object='network/bluetoothd-connection.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o network/bluetoothd-connection.obj `if test -f 'network/connection.c'; then $(CYGPATH_W) 'network/connection.c'; else $(CYGPATH_W) '$(srcdir)/network/connection.c'; fi`
+
+plugins/bluetoothd-service.o: plugins/service.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-service.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-service.Tpo -c -o plugins/bluetoothd-service.o `test -f 'plugins/service.c' || echo '$(srcdir)/'`plugins/service.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-service.Tpo plugins/$(DEPDIR)/bluetoothd-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/service.c' object='plugins/bluetoothd-service.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-service.o `test -f 'plugins/service.c' || echo '$(srcdir)/'`plugins/service.c
+
+plugins/bluetoothd-service.obj: plugins/service.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-service.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-service.Tpo -c -o plugins/bluetoothd-service.obj `if test -f 'plugins/service.c'; then $(CYGPATH_W) 'plugins/service.c'; else $(CYGPATH_W) '$(srcdir)/plugins/service.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-service.Tpo plugins/$(DEPDIR)/bluetoothd-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/service.c' object='plugins/bluetoothd-service.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-service.obj `if test -f 'plugins/service.c'; then $(CYGPATH_W) 'plugins/service.c'; else $(CYGPATH_W) '$(srcdir)/plugins/service.c'; fi`
+
+health/bluetoothd-hdp_main.o: health/hdp_main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-hdp_main.o -MD -MP -MF health/$(DEPDIR)/bluetoothd-hdp_main.Tpo -c -o health/bluetoothd-hdp_main.o `test -f 'health/hdp_main.c' || echo '$(srcdir)/'`health/hdp_main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-hdp_main.Tpo health/$(DEPDIR)/bluetoothd-hdp_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/hdp_main.c' object='health/bluetoothd-hdp_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-hdp_main.o `test -f 'health/hdp_main.c' || echo '$(srcdir)/'`health/hdp_main.c
+
+health/bluetoothd-hdp_main.obj: health/hdp_main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-hdp_main.obj -MD -MP -MF health/$(DEPDIR)/bluetoothd-hdp_main.Tpo -c -o health/bluetoothd-hdp_main.obj `if test -f 'health/hdp_main.c'; then $(CYGPATH_W) 'health/hdp_main.c'; else $(CYGPATH_W) '$(srcdir)/health/hdp_main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-hdp_main.Tpo health/$(DEPDIR)/bluetoothd-hdp_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/hdp_main.c' object='health/bluetoothd-hdp_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-hdp_main.obj `if test -f 'health/hdp_main.c'; then $(CYGPATH_W) 'health/hdp_main.c'; else $(CYGPATH_W) '$(srcdir)/health/hdp_main.c'; fi`
+
+health/bluetoothd-hdp_manager.o: health/hdp_manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-hdp_manager.o -MD -MP -MF health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo -c -o health/bluetoothd-hdp_manager.o `test -f 'health/hdp_manager.c' || echo '$(srcdir)/'`health/hdp_manager.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo health/$(DEPDIR)/bluetoothd-hdp_manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/hdp_manager.c' object='health/bluetoothd-hdp_manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-hdp_manager.o `test -f 'health/hdp_manager.c' || echo '$(srcdir)/'`health/hdp_manager.c
+
+health/bluetoothd-hdp_manager.obj: health/hdp_manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-hdp_manager.obj -MD -MP -MF health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo -c -o health/bluetoothd-hdp_manager.obj `if test -f 'health/hdp_manager.c'; then $(CYGPATH_W) 'health/hdp_manager.c'; else $(CYGPATH_W) '$(srcdir)/health/hdp_manager.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-hdp_manager.Tpo health/$(DEPDIR)/bluetoothd-hdp_manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/hdp_manager.c' object='health/bluetoothd-hdp_manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-hdp_manager.obj `if test -f 'health/hdp_manager.c'; then $(CYGPATH_W) 'health/hdp_manager.c'; else $(CYGPATH_W) '$(srcdir)/health/hdp_manager.c'; fi`
+
+health/bluetoothd-hdp.o: health/hdp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-hdp.o -MD -MP -MF health/$(DEPDIR)/bluetoothd-hdp.Tpo -c -o health/bluetoothd-hdp.o `test -f 'health/hdp.c' || echo '$(srcdir)/'`health/hdp.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-hdp.Tpo health/$(DEPDIR)/bluetoothd-hdp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/hdp.c' object='health/bluetoothd-hdp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-hdp.o `test -f 'health/hdp.c' || echo '$(srcdir)/'`health/hdp.c
+
+health/bluetoothd-hdp.obj: health/hdp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-hdp.obj -MD -MP -MF health/$(DEPDIR)/bluetoothd-hdp.Tpo -c -o health/bluetoothd-hdp.obj `if test -f 'health/hdp.c'; then $(CYGPATH_W) 'health/hdp.c'; else $(CYGPATH_W) '$(srcdir)/health/hdp.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-hdp.Tpo health/$(DEPDIR)/bluetoothd-hdp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/hdp.c' object='health/bluetoothd-hdp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-hdp.obj `if test -f 'health/hdp.c'; then $(CYGPATH_W) 'health/hdp.c'; else $(CYGPATH_W) '$(srcdir)/health/hdp.c'; fi`
+
+health/bluetoothd-hdp_util.o: health/hdp_util.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-hdp_util.o -MD -MP -MF health/$(DEPDIR)/bluetoothd-hdp_util.Tpo -c -o health/bluetoothd-hdp_util.o `test -f 'health/hdp_util.c' || echo '$(srcdir)/'`health/hdp_util.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-hdp_util.Tpo health/$(DEPDIR)/bluetoothd-hdp_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/hdp_util.c' object='health/bluetoothd-hdp_util.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-hdp_util.o `test -f 'health/hdp_util.c' || echo '$(srcdir)/'`health/hdp_util.c
+
+health/bluetoothd-hdp_util.obj: health/hdp_util.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-hdp_util.obj -MD -MP -MF health/$(DEPDIR)/bluetoothd-hdp_util.Tpo -c -o health/bluetoothd-hdp_util.obj `if test -f 'health/hdp_util.c'; then $(CYGPATH_W) 'health/hdp_util.c'; else $(CYGPATH_W) '$(srcdir)/health/hdp_util.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-hdp_util.Tpo health/$(DEPDIR)/bluetoothd-hdp_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/hdp_util.c' object='health/bluetoothd-hdp_util.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-hdp_util.obj `if test -f 'health/hdp_util.c'; then $(CYGPATH_W) 'health/hdp_util.c'; else $(CYGPATH_W) '$(srcdir)/health/hdp_util.c'; fi`
+
+thermometer/bluetoothd-main.o: thermometer/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT thermometer/bluetoothd-main.o -MD -MP -MF thermometer/$(DEPDIR)/bluetoothd-main.Tpo -c -o thermometer/bluetoothd-main.o `test -f 'thermometer/main.c' || echo '$(srcdir)/'`thermometer/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) thermometer/$(DEPDIR)/bluetoothd-main.Tpo thermometer/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='thermometer/main.c' object='thermometer/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o thermometer/bluetoothd-main.o `test -f 'thermometer/main.c' || echo '$(srcdir)/'`thermometer/main.c
+
+thermometer/bluetoothd-main.obj: thermometer/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT thermometer/bluetoothd-main.obj -MD -MP -MF thermometer/$(DEPDIR)/bluetoothd-main.Tpo -c -o thermometer/bluetoothd-main.obj `if test -f 'thermometer/main.c'; then $(CYGPATH_W) 'thermometer/main.c'; else $(CYGPATH_W) '$(srcdir)/thermometer/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) thermometer/$(DEPDIR)/bluetoothd-main.Tpo thermometer/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='thermometer/main.c' object='thermometer/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o thermometer/bluetoothd-main.obj `if test -f 'thermometer/main.c'; then $(CYGPATH_W) 'thermometer/main.c'; else $(CYGPATH_W) '$(srcdir)/thermometer/main.c'; fi`
+
+thermometer/bluetoothd-manager.o: thermometer/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT thermometer/bluetoothd-manager.o -MD -MP -MF thermometer/$(DEPDIR)/bluetoothd-manager.Tpo -c -o thermometer/bluetoothd-manager.o `test -f 'thermometer/manager.c' || echo '$(srcdir)/'`thermometer/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) thermometer/$(DEPDIR)/bluetoothd-manager.Tpo thermometer/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='thermometer/manager.c' object='thermometer/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o thermometer/bluetoothd-manager.o `test -f 'thermometer/manager.c' || echo '$(srcdir)/'`thermometer/manager.c
+
+thermometer/bluetoothd-manager.obj: thermometer/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT thermometer/bluetoothd-manager.obj -MD -MP -MF thermometer/$(DEPDIR)/bluetoothd-manager.Tpo -c -o thermometer/bluetoothd-manager.obj `if test -f 'thermometer/manager.c'; then $(CYGPATH_W) 'thermometer/manager.c'; else $(CYGPATH_W) '$(srcdir)/thermometer/manager.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) thermometer/$(DEPDIR)/bluetoothd-manager.Tpo thermometer/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='thermometer/manager.c' object='thermometer/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o thermometer/bluetoothd-manager.obj `if test -f 'thermometer/manager.c'; then $(CYGPATH_W) 'thermometer/manager.c'; else $(CYGPATH_W) '$(srcdir)/thermometer/manager.c'; fi`
+
+thermometer/bluetoothd-thermometer.o: thermometer/thermometer.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT thermometer/bluetoothd-thermometer.o -MD -MP -MF thermometer/$(DEPDIR)/bluetoothd-thermometer.Tpo -c -o thermometer/bluetoothd-thermometer.o `test -f 'thermometer/thermometer.c' || echo '$(srcdir)/'`thermometer/thermometer.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) thermometer/$(DEPDIR)/bluetoothd-thermometer.Tpo thermometer/$(DEPDIR)/bluetoothd-thermometer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='thermometer/thermometer.c' object='thermometer/bluetoothd-thermometer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o thermometer/bluetoothd-thermometer.o `test -f 'thermometer/thermometer.c' || echo '$(srcdir)/'`thermometer/thermometer.c
+
+thermometer/bluetoothd-thermometer.obj: thermometer/thermometer.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT thermometer/bluetoothd-thermometer.obj -MD -MP -MF thermometer/$(DEPDIR)/bluetoothd-thermometer.Tpo -c -o thermometer/bluetoothd-thermometer.obj `if test -f 'thermometer/thermometer.c'; then $(CYGPATH_W) 'thermometer/thermometer.c'; else $(CYGPATH_W) '$(srcdir)/thermometer/thermometer.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) thermometer/$(DEPDIR)/bluetoothd-thermometer.Tpo thermometer/$(DEPDIR)/bluetoothd-thermometer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='thermometer/thermometer.c' object='thermometer/bluetoothd-thermometer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o thermometer/bluetoothd-thermometer.obj `if test -f 'thermometer/thermometer.c'; then $(CYGPATH_W) 'thermometer/thermometer.c'; else $(CYGPATH_W) '$(srcdir)/thermometer/thermometer.c'; fi`
+
+alert/bluetoothd-main.o: alert/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT alert/bluetoothd-main.o -MD -MP -MF alert/$(DEPDIR)/bluetoothd-main.Tpo -c -o alert/bluetoothd-main.o `test -f 'alert/main.c' || echo '$(srcdir)/'`alert/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) alert/$(DEPDIR)/bluetoothd-main.Tpo alert/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='alert/main.c' object='alert/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o alert/bluetoothd-main.o `test -f 'alert/main.c' || echo '$(srcdir)/'`alert/main.c
+
+alert/bluetoothd-main.obj: alert/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT alert/bluetoothd-main.obj -MD -MP -MF alert/$(DEPDIR)/bluetoothd-main.Tpo -c -o alert/bluetoothd-main.obj `if test -f 'alert/main.c'; then $(CYGPATH_W) 'alert/main.c'; else $(CYGPATH_W) '$(srcdir)/alert/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) alert/$(DEPDIR)/bluetoothd-main.Tpo alert/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='alert/main.c' object='alert/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o alert/bluetoothd-main.obj `if test -f 'alert/main.c'; then $(CYGPATH_W) 'alert/main.c'; else $(CYGPATH_W) '$(srcdir)/alert/main.c'; fi`
+
+alert/bluetoothd-server.o: alert/server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT alert/bluetoothd-server.o -MD -MP -MF alert/$(DEPDIR)/bluetoothd-server.Tpo -c -o alert/bluetoothd-server.o `test -f 'alert/server.c' || echo '$(srcdir)/'`alert/server.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) alert/$(DEPDIR)/bluetoothd-server.Tpo alert/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='alert/server.c' object='alert/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o alert/bluetoothd-server.o `test -f 'alert/server.c' || echo '$(srcdir)/'`alert/server.c
+
+alert/bluetoothd-server.obj: alert/server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT alert/bluetoothd-server.obj -MD -MP -MF alert/$(DEPDIR)/bluetoothd-server.Tpo -c -o alert/bluetoothd-server.obj `if test -f 'alert/server.c'; then $(CYGPATH_W) 'alert/server.c'; else $(CYGPATH_W) '$(srcdir)/alert/server.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) alert/$(DEPDIR)/bluetoothd-server.Tpo alert/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='alert/server.c' object='alert/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o alert/bluetoothd-server.obj `if test -f 'alert/server.c'; then $(CYGPATH_W) 'alert/server.c'; else $(CYGPATH_W) '$(srcdir)/alert/server.c'; fi`
+
+time/bluetoothd-main.o: time/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT time/bluetoothd-main.o -MD -MP -MF time/$(DEPDIR)/bluetoothd-main.Tpo -c -o time/bluetoothd-main.o `test -f 'time/main.c' || echo '$(srcdir)/'`time/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) time/$(DEPDIR)/bluetoothd-main.Tpo time/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='time/main.c' object='time/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o time/bluetoothd-main.o `test -f 'time/main.c' || echo '$(srcdir)/'`time/main.c
+
+time/bluetoothd-main.obj: time/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT time/bluetoothd-main.obj -MD -MP -MF time/$(DEPDIR)/bluetoothd-main.Tpo -c -o time/bluetoothd-main.obj `if test -f 'time/main.c'; then $(CYGPATH_W) 'time/main.c'; else $(CYGPATH_W) '$(srcdir)/time/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) time/$(DEPDIR)/bluetoothd-main.Tpo time/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='time/main.c' object='time/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o time/bluetoothd-main.obj `if test -f 'time/main.c'; then $(CYGPATH_W) 'time/main.c'; else $(CYGPATH_W) '$(srcdir)/time/main.c'; fi`
+
+time/bluetoothd-server.o: time/server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT time/bluetoothd-server.o -MD -MP -MF time/$(DEPDIR)/bluetoothd-server.Tpo -c -o time/bluetoothd-server.o `test -f 'time/server.c' || echo '$(srcdir)/'`time/server.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) time/$(DEPDIR)/bluetoothd-server.Tpo time/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='time/server.c' object='time/bluetoothd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o time/bluetoothd-server.o `test -f 'time/server.c' || echo '$(srcdir)/'`time/server.c
+
+time/bluetoothd-server.obj: time/server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT time/bluetoothd-server.obj -MD -MP -MF time/$(DEPDIR)/bluetoothd-server.Tpo -c -o time/bluetoothd-server.obj `if test -f 'time/server.c'; then $(CYGPATH_W) 'time/server.c'; else $(CYGPATH_W) '$(srcdir)/time/server.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) time/$(DEPDIR)/bluetoothd-server.Tpo time/$(DEPDIR)/bluetoothd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='time/server.c' object='time/bluetoothd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o time/bluetoothd-server.obj `if test -f 'time/server.c'; then $(CYGPATH_W) 'time/server.c'; else $(CYGPATH_W) '$(srcdir)/time/server.c'; fi`
+
+plugins/bluetoothd-gatt-example.o: plugins/gatt-example.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-gatt-example.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-gatt-example.Tpo -c -o plugins/bluetoothd-gatt-example.o `test -f 'plugins/gatt-example.c' || echo '$(srcdir)/'`plugins/gatt-example.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-gatt-example.Tpo plugins/$(DEPDIR)/bluetoothd-gatt-example.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/gatt-example.c' object='plugins/bluetoothd-gatt-example.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-gatt-example.o `test -f 'plugins/gatt-example.c' || echo '$(srcdir)/'`plugins/gatt-example.c
+
+plugins/bluetoothd-gatt-example.obj: plugins/gatt-example.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-gatt-example.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-gatt-example.Tpo -c -o plugins/bluetoothd-gatt-example.obj `if test -f 'plugins/gatt-example.c'; then $(CYGPATH_W) 'plugins/gatt-example.c'; else $(CYGPATH_W) '$(srcdir)/plugins/gatt-example.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-gatt-example.Tpo plugins/$(DEPDIR)/bluetoothd-gatt-example.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/gatt-example.c' object='plugins/bluetoothd-gatt-example.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-gatt-example.obj `if test -f 'plugins/gatt-example.c'; then $(CYGPATH_W) 'plugins/gatt-example.c'; else $(CYGPATH_W) '$(srcdir)/plugins/gatt-example.c'; fi`
+
+proximity/bluetoothd-main.o: proximity/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-main.o -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-main.Tpo -c -o proximity/bluetoothd-main.o `test -f 'proximity/main.c' || echo '$(srcdir)/'`proximity/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-main.Tpo proximity/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/main.c' object='proximity/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-main.o `test -f 'proximity/main.c' || echo '$(srcdir)/'`proximity/main.c
+
+proximity/bluetoothd-main.obj: proximity/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-main.obj -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-main.Tpo -c -o proximity/bluetoothd-main.obj `if test -f 'proximity/main.c'; then $(CYGPATH_W) 'proximity/main.c'; else $(CYGPATH_W) '$(srcdir)/proximity/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-main.Tpo proximity/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/main.c' object='proximity/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-main.obj `if test -f 'proximity/main.c'; then $(CYGPATH_W) 'proximity/main.c'; else $(CYGPATH_W) '$(srcdir)/proximity/main.c'; fi`
+
+proximity/bluetoothd-manager.o: proximity/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-manager.o -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-manager.Tpo -c -o proximity/bluetoothd-manager.o `test -f 'proximity/manager.c' || echo '$(srcdir)/'`proximity/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-manager.Tpo proximity/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/manager.c' object='proximity/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-manager.o `test -f 'proximity/manager.c' || echo '$(srcdir)/'`proximity/manager.c
+
+proximity/bluetoothd-manager.obj: proximity/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-manager.obj -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-manager.Tpo -c -o proximity/bluetoothd-manager.obj `if test -f 'proximity/manager.c'; then $(CYGPATH_W) 'proximity/manager.c'; else $(CYGPATH_W) '$(srcdir)/proximity/manager.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-manager.Tpo proximity/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/manager.c' object='proximity/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-manager.obj `if test -f 'proximity/manager.c'; then $(CYGPATH_W) 'proximity/manager.c'; else $(CYGPATH_W) '$(srcdir)/proximity/manager.c'; fi`
+
+proximity/bluetoothd-monitor.o: proximity/monitor.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-monitor.o -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-monitor.Tpo -c -o proximity/bluetoothd-monitor.o `test -f 'proximity/monitor.c' || echo '$(srcdir)/'`proximity/monitor.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-monitor.Tpo proximity/$(DEPDIR)/bluetoothd-monitor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/monitor.c' object='proximity/bluetoothd-monitor.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-monitor.o `test -f 'proximity/monitor.c' || echo '$(srcdir)/'`proximity/monitor.c
+
+proximity/bluetoothd-monitor.obj: proximity/monitor.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-monitor.obj -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-monitor.Tpo -c -o proximity/bluetoothd-monitor.obj `if test -f 'proximity/monitor.c'; then $(CYGPATH_W) 'proximity/monitor.c'; else $(CYGPATH_W) '$(srcdir)/proximity/monitor.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-monitor.Tpo proximity/$(DEPDIR)/bluetoothd-monitor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/monitor.c' object='proximity/bluetoothd-monitor.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-monitor.obj `if test -f 'proximity/monitor.c'; then $(CYGPATH_W) 'proximity/monitor.c'; else $(CYGPATH_W) '$(srcdir)/proximity/monitor.c'; fi`
+
+proximity/bluetoothd-reporter.o: proximity/reporter.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-reporter.o -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-reporter.Tpo -c -o proximity/bluetoothd-reporter.o `test -f 'proximity/reporter.c' || echo '$(srcdir)/'`proximity/reporter.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-reporter.Tpo proximity/$(DEPDIR)/bluetoothd-reporter.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/reporter.c' object='proximity/bluetoothd-reporter.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-reporter.o `test -f 'proximity/reporter.c' || echo '$(srcdir)/'`proximity/reporter.c
+
+proximity/bluetoothd-reporter.obj: proximity/reporter.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-reporter.obj -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-reporter.Tpo -c -o proximity/bluetoothd-reporter.obj `if test -f 'proximity/reporter.c'; then $(CYGPATH_W) 'proximity/reporter.c'; else $(CYGPATH_W) '$(srcdir)/proximity/reporter.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-reporter.Tpo proximity/$(DEPDIR)/bluetoothd-reporter.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/reporter.c' object='proximity/bluetoothd-reporter.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-reporter.obj `if test -f 'proximity/reporter.c'; then $(CYGPATH_W) 'proximity/reporter.c'; else $(CYGPATH_W) '$(srcdir)/proximity/reporter.c'; fi`
+
+proximity/bluetoothd-linkloss.o: proximity/linkloss.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-linkloss.o -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-linkloss.Tpo -c -o proximity/bluetoothd-linkloss.o `test -f 'proximity/linkloss.c' || echo '$(srcdir)/'`proximity/linkloss.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-linkloss.Tpo proximity/$(DEPDIR)/bluetoothd-linkloss.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/linkloss.c' object='proximity/bluetoothd-linkloss.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-linkloss.o `test -f 'proximity/linkloss.c' || echo '$(srcdir)/'`proximity/linkloss.c
+
+proximity/bluetoothd-linkloss.obj: proximity/linkloss.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-linkloss.obj -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-linkloss.Tpo -c -o proximity/bluetoothd-linkloss.obj `if test -f 'proximity/linkloss.c'; then $(CYGPATH_W) 'proximity/linkloss.c'; else $(CYGPATH_W) '$(srcdir)/proximity/linkloss.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-linkloss.Tpo proximity/$(DEPDIR)/bluetoothd-linkloss.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/linkloss.c' object='proximity/bluetoothd-linkloss.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-linkloss.obj `if test -f 'proximity/linkloss.c'; then $(CYGPATH_W) 'proximity/linkloss.c'; else $(CYGPATH_W) '$(srcdir)/proximity/linkloss.c'; fi`
+
+proximity/bluetoothd-immalert.o: proximity/immalert.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-immalert.o -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-immalert.Tpo -c -o proximity/bluetoothd-immalert.o `test -f 'proximity/immalert.c' || echo '$(srcdir)/'`proximity/immalert.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-immalert.Tpo proximity/$(DEPDIR)/bluetoothd-immalert.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/immalert.c' object='proximity/bluetoothd-immalert.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-immalert.o `test -f 'proximity/immalert.c' || echo '$(srcdir)/'`proximity/immalert.c
+
+proximity/bluetoothd-immalert.obj: proximity/immalert.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT proximity/bluetoothd-immalert.obj -MD -MP -MF proximity/$(DEPDIR)/bluetoothd-immalert.Tpo -c -o proximity/bluetoothd-immalert.obj `if test -f 'proximity/immalert.c'; then $(CYGPATH_W) 'proximity/immalert.c'; else $(CYGPATH_W) '$(srcdir)/proximity/immalert.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) proximity/$(DEPDIR)/bluetoothd-immalert.Tpo proximity/$(DEPDIR)/bluetoothd-immalert.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='proximity/immalert.c' object='proximity/bluetoothd-immalert.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o proximity/bluetoothd-immalert.obj `if test -f 'proximity/immalert.c'; then $(CYGPATH_W) 'proximity/immalert.c'; else $(CYGPATH_W) '$(srcdir)/proximity/immalert.c'; fi`
+
+deviceinfo/bluetoothd-main.o: deviceinfo/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT deviceinfo/bluetoothd-main.o -MD -MP -MF deviceinfo/$(DEPDIR)/bluetoothd-main.Tpo -c -o deviceinfo/bluetoothd-main.o `test -f 'deviceinfo/main.c' || echo '$(srcdir)/'`deviceinfo/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) deviceinfo/$(DEPDIR)/bluetoothd-main.Tpo deviceinfo/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='deviceinfo/main.c' object='deviceinfo/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o deviceinfo/bluetoothd-main.o `test -f 'deviceinfo/main.c' || echo '$(srcdir)/'`deviceinfo/main.c
+
+deviceinfo/bluetoothd-main.obj: deviceinfo/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT deviceinfo/bluetoothd-main.obj -MD -MP -MF deviceinfo/$(DEPDIR)/bluetoothd-main.Tpo -c -o deviceinfo/bluetoothd-main.obj `if test -f 'deviceinfo/main.c'; then $(CYGPATH_W) 'deviceinfo/main.c'; else $(CYGPATH_W) '$(srcdir)/deviceinfo/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) deviceinfo/$(DEPDIR)/bluetoothd-main.Tpo deviceinfo/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='deviceinfo/main.c' object='deviceinfo/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o deviceinfo/bluetoothd-main.obj `if test -f 'deviceinfo/main.c'; then $(CYGPATH_W) 'deviceinfo/main.c'; else $(CYGPATH_W) '$(srcdir)/deviceinfo/main.c'; fi`
+
+deviceinfo/bluetoothd-manager.o: deviceinfo/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT deviceinfo/bluetoothd-manager.o -MD -MP -MF deviceinfo/$(DEPDIR)/bluetoothd-manager.Tpo -c -o deviceinfo/bluetoothd-manager.o `test -f 'deviceinfo/manager.c' || echo '$(srcdir)/'`deviceinfo/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) deviceinfo/$(DEPDIR)/bluetoothd-manager.Tpo deviceinfo/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='deviceinfo/manager.c' object='deviceinfo/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o deviceinfo/bluetoothd-manager.o `test -f 'deviceinfo/manager.c' || echo '$(srcdir)/'`deviceinfo/manager.c
+
+deviceinfo/bluetoothd-manager.obj: deviceinfo/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT deviceinfo/bluetoothd-manager.obj -MD -MP -MF deviceinfo/$(DEPDIR)/bluetoothd-manager.Tpo -c -o deviceinfo/bluetoothd-manager.obj `if test -f 'deviceinfo/manager.c'; then $(CYGPATH_W) 'deviceinfo/manager.c'; else $(CYGPATH_W) '$(srcdir)/deviceinfo/manager.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) deviceinfo/$(DEPDIR)/bluetoothd-manager.Tpo deviceinfo/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='deviceinfo/manager.c' object='deviceinfo/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o deviceinfo/bluetoothd-manager.obj `if test -f 'deviceinfo/manager.c'; then $(CYGPATH_W) 'deviceinfo/manager.c'; else $(CYGPATH_W) '$(srcdir)/deviceinfo/manager.c'; fi`
+
+deviceinfo/bluetoothd-deviceinfo.o: deviceinfo/deviceinfo.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT deviceinfo/bluetoothd-deviceinfo.o -MD -MP -MF deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo -c -o deviceinfo/bluetoothd-deviceinfo.o `test -f 'deviceinfo/deviceinfo.c' || echo '$(srcdir)/'`deviceinfo/deviceinfo.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='deviceinfo/deviceinfo.c' object='deviceinfo/bluetoothd-deviceinfo.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o deviceinfo/bluetoothd-deviceinfo.o `test -f 'deviceinfo/deviceinfo.c' || echo '$(srcdir)/'`deviceinfo/deviceinfo.c
+
+deviceinfo/bluetoothd-deviceinfo.obj: deviceinfo/deviceinfo.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT deviceinfo/bluetoothd-deviceinfo.obj -MD -MP -MF deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo -c -o deviceinfo/bluetoothd-deviceinfo.obj `if test -f 'deviceinfo/deviceinfo.c'; then $(CYGPATH_W) 'deviceinfo/deviceinfo.c'; else $(CYGPATH_W) '$(srcdir)/deviceinfo/deviceinfo.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Tpo deviceinfo/$(DEPDIR)/bluetoothd-deviceinfo.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='deviceinfo/deviceinfo.c' object='deviceinfo/bluetoothd-deviceinfo.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o deviceinfo/bluetoothd-deviceinfo.obj `if test -f 'deviceinfo/deviceinfo.c'; then $(CYGPATH_W) 'deviceinfo/deviceinfo.c'; else $(CYGPATH_W) '$(srcdir)/deviceinfo/deviceinfo.c'; fi`
+
+plugins/bluetoothd-hciops.o: plugins/hciops.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-hciops.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-hciops.Tpo -c -o plugins/bluetoothd-hciops.o `test -f 'plugins/hciops.c' || echo '$(srcdir)/'`plugins/hciops.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-hciops.Tpo plugins/$(DEPDIR)/bluetoothd-hciops.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/hciops.c' object='plugins/bluetoothd-hciops.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-hciops.o `test -f 'plugins/hciops.c' || echo '$(srcdir)/'`plugins/hciops.c
+
+plugins/bluetoothd-hciops.obj: plugins/hciops.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-hciops.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-hciops.Tpo -c -o plugins/bluetoothd-hciops.obj `if test -f 'plugins/hciops.c'; then $(CYGPATH_W) 'plugins/hciops.c'; else $(CYGPATH_W) '$(srcdir)/plugins/hciops.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-hciops.Tpo plugins/$(DEPDIR)/bluetoothd-hciops.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/hciops.c' object='plugins/bluetoothd-hciops.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-hciops.obj `if test -f 'plugins/hciops.c'; then $(CYGPATH_W) 'plugins/hciops.c'; else $(CYGPATH_W) '$(srcdir)/plugins/hciops.c'; fi`
+
+plugins/bluetoothd-mgmtops.o: plugins/mgmtops.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-mgmtops.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-mgmtops.Tpo -c -o plugins/bluetoothd-mgmtops.o `test -f 'plugins/mgmtops.c' || echo '$(srcdir)/'`plugins/mgmtops.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-mgmtops.Tpo plugins/$(DEPDIR)/bluetoothd-mgmtops.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/mgmtops.c' object='plugins/bluetoothd-mgmtops.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-mgmtops.o `test -f 'plugins/mgmtops.c' || echo '$(srcdir)/'`plugins/mgmtops.c
+
+plugins/bluetoothd-mgmtops.obj: plugins/mgmtops.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-mgmtops.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-mgmtops.Tpo -c -o plugins/bluetoothd-mgmtops.obj `if test -f 'plugins/mgmtops.c'; then $(CYGPATH_W) 'plugins/mgmtops.c'; else $(CYGPATH_W) '$(srcdir)/plugins/mgmtops.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-mgmtops.Tpo plugins/$(DEPDIR)/bluetoothd-mgmtops.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/mgmtops.c' object='plugins/bluetoothd-mgmtops.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-mgmtops.obj `if test -f 'plugins/mgmtops.c'; then $(CYGPATH_W) 'plugins/mgmtops.c'; else $(CYGPATH_W) '$(srcdir)/plugins/mgmtops.c'; fi`
+
+plugins/bluetoothd-hal.o: plugins/hal.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-hal.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-hal.Tpo -c -o plugins/bluetoothd-hal.o `test -f 'plugins/hal.c' || echo '$(srcdir)/'`plugins/hal.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-hal.Tpo plugins/$(DEPDIR)/bluetoothd-hal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/hal.c' object='plugins/bluetoothd-hal.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-hal.o `test -f 'plugins/hal.c' || echo '$(srcdir)/'`plugins/hal.c
+
+plugins/bluetoothd-hal.obj: plugins/hal.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-hal.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-hal.Tpo -c -o plugins/bluetoothd-hal.obj `if test -f 'plugins/hal.c'; then $(CYGPATH_W) 'plugins/hal.c'; else $(CYGPATH_W) '$(srcdir)/plugins/hal.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-hal.Tpo plugins/$(DEPDIR)/bluetoothd-hal.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/hal.c' object='plugins/bluetoothd-hal.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-hal.obj `if test -f 'plugins/hal.c'; then $(CYGPATH_W) 'plugins/hal.c'; else $(CYGPATH_W) '$(srcdir)/plugins/hal.c'; fi`
+
+plugins/bluetoothd-formfactor.o: plugins/formfactor.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-formfactor.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-formfactor.Tpo -c -o plugins/bluetoothd-formfactor.o `test -f 'plugins/formfactor.c' || echo '$(srcdir)/'`plugins/formfactor.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-formfactor.Tpo plugins/$(DEPDIR)/bluetoothd-formfactor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/formfactor.c' object='plugins/bluetoothd-formfactor.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-formfactor.o `test -f 'plugins/formfactor.c' || echo '$(srcdir)/'`plugins/formfactor.c
+
+plugins/bluetoothd-formfactor.obj: plugins/formfactor.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-formfactor.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-formfactor.Tpo -c -o plugins/bluetoothd-formfactor.obj `if test -f 'plugins/formfactor.c'; then $(CYGPATH_W) 'plugins/formfactor.c'; else $(CYGPATH_W) '$(srcdir)/plugins/formfactor.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-formfactor.Tpo plugins/$(DEPDIR)/bluetoothd-formfactor.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/formfactor.c' object='plugins/bluetoothd-formfactor.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-formfactor.obj `if test -f 'plugins/formfactor.c'; then $(CYGPATH_W) 'plugins/formfactor.c'; else $(CYGPATH_W) '$(srcdir)/plugins/formfactor.c'; fi`
+
+plugins/bluetoothd-storage.o: plugins/storage.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-storage.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-storage.Tpo -c -o plugins/bluetoothd-storage.o `test -f 'plugins/storage.c' || echo '$(srcdir)/'`plugins/storage.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-storage.Tpo plugins/$(DEPDIR)/bluetoothd-storage.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/storage.c' object='plugins/bluetoothd-storage.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-storage.o `test -f 'plugins/storage.c' || echo '$(srcdir)/'`plugins/storage.c
+
+plugins/bluetoothd-storage.obj: plugins/storage.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-storage.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-storage.Tpo -c -o plugins/bluetoothd-storage.obj `if test -f 'plugins/storage.c'; then $(CYGPATH_W) 'plugins/storage.c'; else $(CYGPATH_W) '$(srcdir)/plugins/storage.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-storage.Tpo plugins/$(DEPDIR)/bluetoothd-storage.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/storage.c' object='plugins/bluetoothd-storage.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-storage.obj `if test -f 'plugins/storage.c'; then $(CYGPATH_W) 'plugins/storage.c'; else $(CYGPATH_W) '$(srcdir)/plugins/storage.c'; fi`
+
+plugins/bluetoothd-adaptername.o: plugins/adaptername.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-adaptername.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-adaptername.Tpo -c -o plugins/bluetoothd-adaptername.o `test -f 'plugins/adaptername.c' || echo '$(srcdir)/'`plugins/adaptername.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-adaptername.Tpo plugins/$(DEPDIR)/bluetoothd-adaptername.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/adaptername.c' object='plugins/bluetoothd-adaptername.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-adaptername.o `test -f 'plugins/adaptername.c' || echo '$(srcdir)/'`plugins/adaptername.c
+
+plugins/bluetoothd-adaptername.obj: plugins/adaptername.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-adaptername.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-adaptername.Tpo -c -o plugins/bluetoothd-adaptername.obj `if test -f 'plugins/adaptername.c'; then $(CYGPATH_W) 'plugins/adaptername.c'; else $(CYGPATH_W) '$(srcdir)/plugins/adaptername.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-adaptername.Tpo plugins/$(DEPDIR)/bluetoothd-adaptername.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/adaptername.c' object='plugins/bluetoothd-adaptername.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-adaptername.obj `if test -f 'plugins/adaptername.c'; then $(CYGPATH_W) 'plugins/adaptername.c'; else $(CYGPATH_W) '$(srcdir)/plugins/adaptername.c'; fi`
+
+plugins/bluetoothd-wiimote.o: plugins/wiimote.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-wiimote.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-wiimote.Tpo -c -o plugins/bluetoothd-wiimote.o `test -f 'plugins/wiimote.c' || echo '$(srcdir)/'`plugins/wiimote.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-wiimote.Tpo plugins/$(DEPDIR)/bluetoothd-wiimote.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/wiimote.c' object='plugins/bluetoothd-wiimote.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-wiimote.o `test -f 'plugins/wiimote.c' || echo '$(srcdir)/'`plugins/wiimote.c
+
+plugins/bluetoothd-wiimote.obj: plugins/wiimote.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-wiimote.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-wiimote.Tpo -c -o plugins/bluetoothd-wiimote.obj `if test -f 'plugins/wiimote.c'; then $(CYGPATH_W) 'plugins/wiimote.c'; else $(CYGPATH_W) '$(srcdir)/plugins/wiimote.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-wiimote.Tpo plugins/$(DEPDIR)/bluetoothd-wiimote.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/wiimote.c' object='plugins/bluetoothd-wiimote.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-wiimote.obj `if test -f 'plugins/wiimote.c'; then $(CYGPATH_W) 'plugins/wiimote.c'; else $(CYGPATH_W) '$(srcdir)/plugins/wiimote.c'; fi`
+
+plugins/bluetoothd-maemo6.o: plugins/maemo6.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-maemo6.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-maemo6.Tpo -c -o plugins/bluetoothd-maemo6.o `test -f 'plugins/maemo6.c' || echo '$(srcdir)/'`plugins/maemo6.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-maemo6.Tpo plugins/$(DEPDIR)/bluetoothd-maemo6.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/maemo6.c' object='plugins/bluetoothd-maemo6.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-maemo6.o `test -f 'plugins/maemo6.c' || echo '$(srcdir)/'`plugins/maemo6.c
+
+plugins/bluetoothd-maemo6.obj: plugins/maemo6.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-maemo6.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-maemo6.Tpo -c -o plugins/bluetoothd-maemo6.obj `if test -f 'plugins/maemo6.c'; then $(CYGPATH_W) 'plugins/maemo6.c'; else $(CYGPATH_W) '$(srcdir)/plugins/maemo6.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-maemo6.Tpo plugins/$(DEPDIR)/bluetoothd-maemo6.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/maemo6.c' object='plugins/bluetoothd-maemo6.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-maemo6.obj `if test -f 'plugins/maemo6.c'; then $(CYGPATH_W) 'plugins/maemo6.c'; else $(CYGPATH_W) '$(srcdir)/plugins/maemo6.c'; fi`
+
+plugins/bluetoothd-dbusoob.o: plugins/dbusoob.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-dbusoob.o -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-dbusoob.Tpo -c -o plugins/bluetoothd-dbusoob.o `test -f 'plugins/dbusoob.c' || echo '$(srcdir)/'`plugins/dbusoob.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-dbusoob.Tpo plugins/$(DEPDIR)/bluetoothd-dbusoob.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/dbusoob.c' object='plugins/bluetoothd-dbusoob.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-dbusoob.o `test -f 'plugins/dbusoob.c' || echo '$(srcdir)/'`plugins/dbusoob.c
+
+plugins/bluetoothd-dbusoob.obj: plugins/dbusoob.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT plugins/bluetoothd-dbusoob.obj -MD -MP -MF plugins/$(DEPDIR)/bluetoothd-dbusoob.Tpo -c -o plugins/bluetoothd-dbusoob.obj `if test -f 'plugins/dbusoob.c'; then $(CYGPATH_W) 'plugins/dbusoob.c'; else $(CYGPATH_W) '$(srcdir)/plugins/dbusoob.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/bluetoothd-dbusoob.Tpo plugins/$(DEPDIR)/bluetoothd-dbusoob.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/dbusoob.c' object='plugins/bluetoothd-dbusoob.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o plugins/bluetoothd-dbusoob.obj `if test -f 'plugins/dbusoob.c'; then $(CYGPATH_W) 'plugins/dbusoob.c'; else $(CYGPATH_W) '$(srcdir)/plugins/dbusoob.c'; fi`
+
+attrib/bluetoothd-att.o: attrib/att.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-att.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-att.Tpo -c -o attrib/bluetoothd-att.o `test -f 'attrib/att.c' || echo '$(srcdir)/'`attrib/att.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-att.Tpo attrib/$(DEPDIR)/bluetoothd-att.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='attrib/att.c' object='attrib/bluetoothd-att.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-att.o `test -f 'attrib/att.c' || echo '$(srcdir)/'`attrib/att.c
+
+attrib/bluetoothd-att.obj: attrib/att.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-att.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-att.Tpo -c -o attrib/bluetoothd-att.obj `if test -f 'attrib/att.c'; then $(CYGPATH_W) 'attrib/att.c'; else $(CYGPATH_W) '$(srcdir)/attrib/att.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-att.Tpo attrib/$(DEPDIR)/bluetoothd-att.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='attrib/att.c' object='attrib/bluetoothd-att.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-att.obj `if test -f 'attrib/att.c'; then $(CYGPATH_W) 'attrib/att.c'; else $(CYGPATH_W) '$(srcdir)/attrib/att.c'; fi`
+
+attrib/bluetoothd-gatt.o: attrib/gatt.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gatt.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o attrib/bluetoothd-gatt.o `test -f 'attrib/gatt.c' || echo '$(srcdir)/'`attrib/gatt.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gatt.Tpo attrib/$(DEPDIR)/bluetoothd-gatt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='attrib/gatt.c' object='attrib/bluetoothd-gatt.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gatt.o `test -f 'attrib/gatt.c' || echo '$(srcdir)/'`attrib/gatt.c
+
+attrib/bluetoothd-gatt.obj: attrib/gatt.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gatt.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o attrib/bluetoothd-gatt.obj `if test -f 'attrib/gatt.c'; then $(CYGPATH_W) 'attrib/gatt.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gatt.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gatt.Tpo attrib/$(DEPDIR)/bluetoothd-gatt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='attrib/gatt.c' object='attrib/bluetoothd-gatt.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gatt.obj `if test -f 'attrib/gatt.c'; then $(CYGPATH_W) 'attrib/gatt.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gatt.c'; fi`
+
+attrib/bluetoothd-gattrib.o: attrib/gattrib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gattrib.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo -c -o attrib/bluetoothd-gattrib.o `test -f 'attrib/gattrib.c' || echo '$(srcdir)/'`attrib/gattrib.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo attrib/$(DEPDIR)/bluetoothd-gattrib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='attrib/gattrib.c' object='attrib/bluetoothd-gattrib.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gattrib.o `test -f 'attrib/gattrib.c' || echo '$(srcdir)/'`attrib/gattrib.c
+
+attrib/bluetoothd-gattrib.obj: attrib/gattrib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gattrib.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo -c -o attrib/bluetoothd-gattrib.obj `if test -f 'attrib/gattrib.c'; then $(CYGPATH_W) 'attrib/gattrib.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gattrib.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gattrib.Tpo attrib/$(DEPDIR)/bluetoothd-gattrib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='attrib/gattrib.c' object='attrib/bluetoothd-gattrib.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gattrib.obj `if test -f 'attrib/gattrib.c'; then $(CYGPATH_W) 'attrib/gattrib.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gattrib.c'; fi`
+
+attrib/bluetoothd-client.o: attrib/client.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-client.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-client.Tpo -c -o attrib/bluetoothd-client.o `test -f 'attrib/client.c' || echo '$(srcdir)/'`attrib/client.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-client.Tpo attrib/$(DEPDIR)/bluetoothd-client.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='attrib/client.c' object='attrib/bluetoothd-client.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-client.o `test -f 'attrib/client.c' || echo '$(srcdir)/'`attrib/client.c
+
+attrib/bluetoothd-client.obj: attrib/client.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-client.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-client.Tpo -c -o attrib/bluetoothd-client.obj `if test -f 'attrib/client.c'; then $(CYGPATH_W) 'attrib/client.c'; else $(CYGPATH_W) '$(srcdir)/attrib/client.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-client.Tpo attrib/$(DEPDIR)/bluetoothd-client.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='attrib/client.c' object='attrib/bluetoothd-client.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-client.obj `if test -f 'attrib/client.c'; then $(CYGPATH_W) 'attrib/client.c'; else $(CYGPATH_W) '$(srcdir)/attrib/client.c'; fi`
+
+attrib/bluetoothd-gatt-service.o: attrib/gatt-service.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gatt-service.o -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gatt-service.Tpo -c -o attrib/bluetoothd-gatt-service.o `test -f 'attrib/gatt-service.c' || echo '$(srcdir)/'`attrib/gatt-service.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gatt-service.Tpo attrib/$(DEPDIR)/bluetoothd-gatt-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='attrib/gatt-service.c' object='attrib/bluetoothd-gatt-service.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gatt-service.o `test -f 'attrib/gatt-service.c' || echo '$(srcdir)/'`attrib/gatt-service.c
+
+attrib/bluetoothd-gatt-service.obj: attrib/gatt-service.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT attrib/bluetoothd-gatt-service.obj -MD -MP -MF attrib/$(DEPDIR)/bluetoothd-gatt-service.Tpo -c -o attrib/bluetoothd-gatt-service.obj `if test -f 'attrib/gatt-service.c'; then $(CYGPATH_W) 'attrib/gatt-service.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gatt-service.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) attrib/$(DEPDIR)/bluetoothd-gatt-service.Tpo attrib/$(DEPDIR)/bluetoothd-gatt-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='attrib/gatt-service.c' object='attrib/bluetoothd-gatt-service.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o attrib/bluetoothd-gatt-service.obj `if test -f 'attrib/gatt-service.c'; then $(CYGPATH_W) 'attrib/gatt-service.c'; else $(CYGPATH_W) '$(srcdir)/attrib/gatt-service.c'; fi`
+
+btio/bluetoothd-btio.o: btio/btio.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT btio/bluetoothd-btio.o -MD -MP -MF btio/$(DEPDIR)/bluetoothd-btio.Tpo -c -o btio/bluetoothd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) btio/$(DEPDIR)/bluetoothd-btio.Tpo btio/$(DEPDIR)/bluetoothd-btio.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='btio/btio.c' object='btio/bluetoothd-btio.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o btio/bluetoothd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c
+
+btio/bluetoothd-btio.obj: btio/btio.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT btio/bluetoothd-btio.obj -MD -MP -MF btio/$(DEPDIR)/bluetoothd-btio.Tpo -c -o btio/bluetoothd-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) btio/$(DEPDIR)/bluetoothd-btio.Tpo btio/$(DEPDIR)/bluetoothd-btio.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='btio/btio.c' object='btio/bluetoothd-btio.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o btio/bluetoothd-btio.obj `if test -f 'btio/btio.c'; then $(CYGPATH_W) 'btio/btio.c'; else $(CYGPATH_W) '$(srcdir)/btio/btio.c'; fi`
+
+health/bluetoothd-mcap.o: health/mcap.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-mcap.o -MD -MP -MF health/$(DEPDIR)/bluetoothd-mcap.Tpo -c -o health/bluetoothd-mcap.o `test -f 'health/mcap.c' || echo '$(srcdir)/'`health/mcap.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-mcap.Tpo health/$(DEPDIR)/bluetoothd-mcap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/mcap.c' object='health/bluetoothd-mcap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-mcap.o `test -f 'health/mcap.c' || echo '$(srcdir)/'`health/mcap.c
+
+health/bluetoothd-mcap.obj: health/mcap.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-mcap.obj -MD -MP -MF health/$(DEPDIR)/bluetoothd-mcap.Tpo -c -o health/bluetoothd-mcap.obj `if test -f 'health/mcap.c'; then $(CYGPATH_W) 'health/mcap.c'; else $(CYGPATH_W) '$(srcdir)/health/mcap.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-mcap.Tpo health/$(DEPDIR)/bluetoothd-mcap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/mcap.c' object='health/bluetoothd-mcap.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-mcap.obj `if test -f 'health/mcap.c'; then $(CYGPATH_W) 'health/mcap.c'; else $(CYGPATH_W) '$(srcdir)/health/mcap.c'; fi`
+
+health/bluetoothd-mcap_sync.o: health/mcap_sync.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-mcap_sync.o -MD -MP -MF health/$(DEPDIR)/bluetoothd-mcap_sync.Tpo -c -o health/bluetoothd-mcap_sync.o `test -f 'health/mcap_sync.c' || echo '$(srcdir)/'`health/mcap_sync.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-mcap_sync.Tpo health/$(DEPDIR)/bluetoothd-mcap_sync.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/mcap_sync.c' object='health/bluetoothd-mcap_sync.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-mcap_sync.o `test -f 'health/mcap_sync.c' || echo '$(srcdir)/'`health/mcap_sync.c
+
+health/bluetoothd-mcap_sync.obj: health/mcap_sync.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT health/bluetoothd-mcap_sync.obj -MD -MP -MF health/$(DEPDIR)/bluetoothd-mcap_sync.Tpo -c -o health/bluetoothd-mcap_sync.obj `if test -f 'health/mcap_sync.c'; then $(CYGPATH_W) 'health/mcap_sync.c'; else $(CYGPATH_W) '$(srcdir)/health/mcap_sync.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) health/$(DEPDIR)/bluetoothd-mcap_sync.Tpo health/$(DEPDIR)/bluetoothd-mcap_sync.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='health/mcap_sync.c' object='health/bluetoothd-mcap_sync.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o health/bluetoothd-mcap_sync.obj `if test -f 'health/mcap_sync.c'; then $(CYGPATH_W) 'health/mcap_sync.c'; else $(CYGPATH_W) '$(srcdir)/health/mcap_sync.c'; fi`
+
+src/bluetoothd-main.o: src/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-main.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-main.Tpo -c -o src/bluetoothd-main.o `test -f 'src/main.c' || echo '$(srcdir)/'`src/main.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-main.Tpo src/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/main.c' object='src/bluetoothd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-main.o `test -f 'src/main.c' || echo '$(srcdir)/'`src/main.c
+
+src/bluetoothd-main.obj: src/main.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-main.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-main.Tpo -c -o src/bluetoothd-main.obj `if test -f 'src/main.c'; then $(CYGPATH_W) 'src/main.c'; else $(CYGPATH_W) '$(srcdir)/src/main.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-main.Tpo src/$(DEPDIR)/bluetoothd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/main.c' object='src/bluetoothd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-main.obj `if test -f 'src/main.c'; then $(CYGPATH_W) 'src/main.c'; else $(CYGPATH_W) '$(srcdir)/src/main.c'; fi`
+
+src/bluetoothd-log.o: src/log.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-log.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-log.Tpo -c -o src/bluetoothd-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-log.Tpo src/$(DEPDIR)/bluetoothd-log.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/log.c' object='src/bluetoothd-log.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-log.o `test -f 'src/log.c' || echo '$(srcdir)/'`src/log.c
+
+src/bluetoothd-log.obj: src/log.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-log.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-log.Tpo -c -o src/bluetoothd-log.obj `if test -f 'src/log.c'; then $(CYGPATH_W) 'src/log.c'; else $(CYGPATH_W) '$(srcdir)/src/log.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-log.Tpo src/$(DEPDIR)/bluetoothd-log.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/log.c' object='src/bluetoothd-log.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-log.obj `if test -f 'src/log.c'; then $(CYGPATH_W) 'src/log.c'; else $(CYGPATH_W) '$(srcdir)/src/log.c'; fi`
+
+src/bluetoothd-rfkill.o: src/rfkill.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-rfkill.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-rfkill.Tpo -c -o src/bluetoothd-rfkill.o `test -f 'src/rfkill.c' || echo '$(srcdir)/'`src/rfkill.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-rfkill.Tpo src/$(DEPDIR)/bluetoothd-rfkill.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/rfkill.c' object='src/bluetoothd-rfkill.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-rfkill.o `test -f 'src/rfkill.c' || echo '$(srcdir)/'`src/rfkill.c
+
+src/bluetoothd-rfkill.obj: src/rfkill.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-rfkill.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-rfkill.Tpo -c -o src/bluetoothd-rfkill.obj `if test -f 'src/rfkill.c'; then $(CYGPATH_W) 'src/rfkill.c'; else $(CYGPATH_W) '$(srcdir)/src/rfkill.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-rfkill.Tpo src/$(DEPDIR)/bluetoothd-rfkill.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/rfkill.c' object='src/bluetoothd-rfkill.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-rfkill.obj `if test -f 'src/rfkill.c'; then $(CYGPATH_W) 'src/rfkill.c'; else $(CYGPATH_W) '$(srcdir)/src/rfkill.c'; fi`
+
+src/bluetoothd-sdpd-server.o: src/sdpd-server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-server.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo -c -o src/bluetoothd-sdpd-server.o `test -f 'src/sdpd-server.c' || echo '$(srcdir)/'`src/sdpd-server.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo src/$(DEPDIR)/bluetoothd-sdpd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdpd-server.c' object='src/bluetoothd-sdpd-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-server.o `test -f 'src/sdpd-server.c' || echo '$(srcdir)/'`src/sdpd-server.c
+
+src/bluetoothd-sdpd-server.obj: src/sdpd-server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-server.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo -c -o src/bluetoothd-sdpd-server.obj `if test -f 'src/sdpd-server.c'; then $(CYGPATH_W) 'src/sdpd-server.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-server.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-server.Tpo src/$(DEPDIR)/bluetoothd-sdpd-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdpd-server.c' object='src/bluetoothd-sdpd-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-server.obj `if test -f 'src/sdpd-server.c'; then $(CYGPATH_W) 'src/sdpd-server.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-server.c'; fi`
+
+src/bluetoothd-sdpd-request.o: src/sdpd-request.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-request.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo -c -o src/bluetoothd-sdpd-request.o `test -f 'src/sdpd-request.c' || echo '$(srcdir)/'`src/sdpd-request.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo src/$(DEPDIR)/bluetoothd-sdpd-request.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdpd-request.c' object='src/bluetoothd-sdpd-request.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-request.o `test -f 'src/sdpd-request.c' || echo '$(srcdir)/'`src/sdpd-request.c
+
+src/bluetoothd-sdpd-request.obj: src/sdpd-request.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-request.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo -c -o src/bluetoothd-sdpd-request.obj `if test -f 'src/sdpd-request.c'; then $(CYGPATH_W) 'src/sdpd-request.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-request.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-request.Tpo src/$(DEPDIR)/bluetoothd-sdpd-request.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdpd-request.c' object='src/bluetoothd-sdpd-request.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-request.obj `if test -f 'src/sdpd-request.c'; then $(CYGPATH_W) 'src/sdpd-request.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-request.c'; fi`
+
+src/bluetoothd-sdpd-service.o: src/sdpd-service.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-service.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo -c -o src/bluetoothd-sdpd-service.o `test -f 'src/sdpd-service.c' || echo '$(srcdir)/'`src/sdpd-service.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo src/$(DEPDIR)/bluetoothd-sdpd-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdpd-service.c' object='src/bluetoothd-sdpd-service.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-service.o `test -f 'src/sdpd-service.c' || echo '$(srcdir)/'`src/sdpd-service.c
+
+src/bluetoothd-sdpd-service.obj: src/sdpd-service.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-service.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo -c -o src/bluetoothd-sdpd-service.obj `if test -f 'src/sdpd-service.c'; then $(CYGPATH_W) 'src/sdpd-service.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-service.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-service.Tpo src/$(DEPDIR)/bluetoothd-sdpd-service.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdpd-service.c' object='src/bluetoothd-sdpd-service.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-service.obj `if test -f 'src/sdpd-service.c'; then $(CYGPATH_W) 'src/sdpd-service.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-service.c'; fi`
+
+src/bluetoothd-sdpd-database.o: src/sdpd-database.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-database.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo -c -o src/bluetoothd-sdpd-database.o `test -f 'src/sdpd-database.c' || echo '$(srcdir)/'`src/sdpd-database.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo src/$(DEPDIR)/bluetoothd-sdpd-database.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdpd-database.c' object='src/bluetoothd-sdpd-database.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-database.o `test -f 'src/sdpd-database.c' || echo '$(srcdir)/'`src/sdpd-database.c
+
+src/bluetoothd-sdpd-database.obj: src/sdpd-database.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdpd-database.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo -c -o src/bluetoothd-sdpd-database.obj `if test -f 'src/sdpd-database.c'; then $(CYGPATH_W) 'src/sdpd-database.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-database.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdpd-database.Tpo src/$(DEPDIR)/bluetoothd-sdpd-database.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdpd-database.c' object='src/bluetoothd-sdpd-database.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdpd-database.obj `if test -f 'src/sdpd-database.c'; then $(CYGPATH_W) 'src/sdpd-database.c'; else $(CYGPATH_W) '$(srcdir)/src/sdpd-database.c'; fi`
+
+src/bluetoothd-attrib-server.o: src/attrib-server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-attrib-server.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-attrib-server.Tpo -c -o src/bluetoothd-attrib-server.o `test -f 'src/attrib-server.c' || echo '$(srcdir)/'`src/attrib-server.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-attrib-server.Tpo src/$(DEPDIR)/bluetoothd-attrib-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/attrib-server.c' object='src/bluetoothd-attrib-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-attrib-server.o `test -f 'src/attrib-server.c' || echo '$(srcdir)/'`src/attrib-server.c
+
+src/bluetoothd-attrib-server.obj: src/attrib-server.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-attrib-server.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-attrib-server.Tpo -c -o src/bluetoothd-attrib-server.obj `if test -f 'src/attrib-server.c'; then $(CYGPATH_W) 'src/attrib-server.c'; else $(CYGPATH_W) '$(srcdir)/src/attrib-server.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-attrib-server.Tpo src/$(DEPDIR)/bluetoothd-attrib-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/attrib-server.c' object='src/bluetoothd-attrib-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-attrib-server.obj `if test -f 'src/attrib-server.c'; then $(CYGPATH_W) 'src/attrib-server.c'; else $(CYGPATH_W) '$(srcdir)/src/attrib-server.c'; fi`
+
+src/bluetoothd-sdp-xml.o: src/sdp-xml.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-xml.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo -c -o src/bluetoothd-sdp-xml.o `test -f 'src/sdp-xml.c' || echo '$(srcdir)/'`src/sdp-xml.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo src/$(DEPDIR)/bluetoothd-sdp-xml.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdp-xml.c' object='src/bluetoothd-sdp-xml.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-xml.o `test -f 'src/sdp-xml.c' || echo '$(srcdir)/'`src/sdp-xml.c
+
+src/bluetoothd-sdp-xml.obj: src/sdp-xml.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-xml.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo -c -o src/bluetoothd-sdp-xml.obj `if test -f 'src/sdp-xml.c'; then $(CYGPATH_W) 'src/sdp-xml.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-xml.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-xml.Tpo src/$(DEPDIR)/bluetoothd-sdp-xml.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdp-xml.c' object='src/bluetoothd-sdp-xml.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-xml.obj `if test -f 'src/sdp-xml.c'; then $(CYGPATH_W) 'src/sdp-xml.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-xml.c'; fi`
+
+src/bluetoothd-sdp-client.o: src/sdp-client.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-client.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-client.Tpo -c -o src/bluetoothd-sdp-client.o `test -f 'src/sdp-client.c' || echo '$(srcdir)/'`src/sdp-client.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-client.Tpo src/$(DEPDIR)/bluetoothd-sdp-client.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdp-client.c' object='src/bluetoothd-sdp-client.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-client.o `test -f 'src/sdp-client.c' || echo '$(srcdir)/'`src/sdp-client.c
+
+src/bluetoothd-sdp-client.obj: src/sdp-client.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-sdp-client.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-sdp-client.Tpo -c -o src/bluetoothd-sdp-client.obj `if test -f 'src/sdp-client.c'; then $(CYGPATH_W) 'src/sdp-client.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-client.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-sdp-client.Tpo src/$(DEPDIR)/bluetoothd-sdp-client.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/sdp-client.c' object='src/bluetoothd-sdp-client.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-sdp-client.obj `if test -f 'src/sdp-client.c'; then $(CYGPATH_W) 'src/sdp-client.c'; else $(CYGPATH_W) '$(srcdir)/src/sdp-client.c'; fi`
+
+src/bluetoothd-textfile.o: src/textfile.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-textfile.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-textfile.Tpo -c -o src/bluetoothd-textfile.o `test -f 'src/textfile.c' || echo '$(srcdir)/'`src/textfile.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-textfile.Tpo src/$(DEPDIR)/bluetoothd-textfile.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/textfile.c' object='src/bluetoothd-textfile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-textfile.o `test -f 'src/textfile.c' || echo '$(srcdir)/'`src/textfile.c
+
+src/bluetoothd-textfile.obj: src/textfile.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-textfile.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-textfile.Tpo -c -o src/bluetoothd-textfile.obj `if test -f 'src/textfile.c'; then $(CYGPATH_W) 'src/textfile.c'; else $(CYGPATH_W) '$(srcdir)/src/textfile.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-textfile.Tpo src/$(DEPDIR)/bluetoothd-textfile.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/textfile.c' object='src/bluetoothd-textfile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-textfile.obj `if test -f 'src/textfile.c'; then $(CYGPATH_W) 'src/textfile.c'; else $(CYGPATH_W) '$(srcdir)/src/textfile.c'; fi`
+
+src/bluetoothd-glib-helper.o: src/glib-helper.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-glib-helper.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-glib-helper.Tpo -c -o src/bluetoothd-glib-helper.o `test -f 'src/glib-helper.c' || echo '$(srcdir)/'`src/glib-helper.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-glib-helper.Tpo src/$(DEPDIR)/bluetoothd-glib-helper.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/glib-helper.c' object='src/bluetoothd-glib-helper.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-glib-helper.o `test -f 'src/glib-helper.c' || echo '$(srcdir)/'`src/glib-helper.c
+
+src/bluetoothd-glib-helper.obj: src/glib-helper.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-glib-helper.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-glib-helper.Tpo -c -o src/bluetoothd-glib-helper.obj `if test -f 'src/glib-helper.c'; then $(CYGPATH_W) 'src/glib-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/glib-helper.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-glib-helper.Tpo src/$(DEPDIR)/bluetoothd-glib-helper.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/glib-helper.c' object='src/bluetoothd-glib-helper.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-glib-helper.obj `if test -f 'src/glib-helper.c'; then $(CYGPATH_W) 'src/glib-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/glib-helper.c'; fi`
+
+src/bluetoothd-oui.o: src/oui.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-oui.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-oui.Tpo -c -o src/bluetoothd-oui.o `test -f 'src/oui.c' || echo '$(srcdir)/'`src/oui.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-oui.Tpo src/$(DEPDIR)/bluetoothd-oui.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/oui.c' object='src/bluetoothd-oui.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-oui.o `test -f 'src/oui.c' || echo '$(srcdir)/'`src/oui.c
+
+src/bluetoothd-oui.obj: src/oui.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-oui.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-oui.Tpo -c -o src/bluetoothd-oui.obj `if test -f 'src/oui.c'; then $(CYGPATH_W) 'src/oui.c'; else $(CYGPATH_W) '$(srcdir)/src/oui.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-oui.Tpo src/$(DEPDIR)/bluetoothd-oui.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/oui.c' object='src/bluetoothd-oui.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-oui.obj `if test -f 'src/oui.c'; then $(CYGPATH_W) 'src/oui.c'; else $(CYGPATH_W) '$(srcdir)/src/oui.c'; fi`
+
+src/bluetoothd-plugin.o: src/plugin.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-plugin.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-plugin.Tpo -c -o src/bluetoothd-plugin.o `test -f 'src/plugin.c' || echo '$(srcdir)/'`src/plugin.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-plugin.Tpo src/$(DEPDIR)/bluetoothd-plugin.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/plugin.c' object='src/bluetoothd-plugin.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-plugin.o `test -f 'src/plugin.c' || echo '$(srcdir)/'`src/plugin.c
+
+src/bluetoothd-plugin.obj: src/plugin.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-plugin.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-plugin.Tpo -c -o src/bluetoothd-plugin.obj `if test -f 'src/plugin.c'; then $(CYGPATH_W) 'src/plugin.c'; else $(CYGPATH_W) '$(srcdir)/src/plugin.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-plugin.Tpo src/$(DEPDIR)/bluetoothd-plugin.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/plugin.c' object='src/bluetoothd-plugin.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-plugin.obj `if test -f 'src/plugin.c'; then $(CYGPATH_W) 'src/plugin.c'; else $(CYGPATH_W) '$(srcdir)/src/plugin.c'; fi`
+
+src/bluetoothd-storage.o: src/storage.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-storage.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-storage.Tpo -c -o src/bluetoothd-storage.o `test -f 'src/storage.c' || echo '$(srcdir)/'`src/storage.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-storage.Tpo src/$(DEPDIR)/bluetoothd-storage.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/storage.c' object='src/bluetoothd-storage.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-storage.o `test -f 'src/storage.c' || echo '$(srcdir)/'`src/storage.c
+
+src/bluetoothd-storage.obj: src/storage.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-storage.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-storage.Tpo -c -o src/bluetoothd-storage.obj `if test -f 'src/storage.c'; then $(CYGPATH_W) 'src/storage.c'; else $(CYGPATH_W) '$(srcdir)/src/storage.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-storage.Tpo src/$(DEPDIR)/bluetoothd-storage.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/storage.c' object='src/bluetoothd-storage.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-storage.obj `if test -f 'src/storage.c'; then $(CYGPATH_W) 'src/storage.c'; else $(CYGPATH_W) '$(srcdir)/src/storage.c'; fi`
+
+src/bluetoothd-agent.o: src/agent.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-agent.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-agent.Tpo -c -o src/bluetoothd-agent.o `test -f 'src/agent.c' || echo '$(srcdir)/'`src/agent.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-agent.Tpo src/$(DEPDIR)/bluetoothd-agent.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/agent.c' object='src/bluetoothd-agent.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-agent.o `test -f 'src/agent.c' || echo '$(srcdir)/'`src/agent.c
+
+src/bluetoothd-agent.obj: src/agent.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-agent.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-agent.Tpo -c -o src/bluetoothd-agent.obj `if test -f 'src/agent.c'; then $(CYGPATH_W) 'src/agent.c'; else $(CYGPATH_W) '$(srcdir)/src/agent.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-agent.Tpo src/$(DEPDIR)/bluetoothd-agent.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/agent.c' object='src/bluetoothd-agent.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-agent.obj `if test -f 'src/agent.c'; then $(CYGPATH_W) 'src/agent.c'; else $(CYGPATH_W) '$(srcdir)/src/agent.c'; fi`
+
+src/bluetoothd-error.o: src/error.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-error.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-error.Tpo -c -o src/bluetoothd-error.o `test -f 'src/error.c' || echo '$(srcdir)/'`src/error.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-error.Tpo src/$(DEPDIR)/bluetoothd-error.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/error.c' object='src/bluetoothd-error.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-error.o `test -f 'src/error.c' || echo '$(srcdir)/'`src/error.c
+
+src/bluetoothd-error.obj: src/error.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-error.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-error.Tpo -c -o src/bluetoothd-error.obj `if test -f 'src/error.c'; then $(CYGPATH_W) 'src/error.c'; else $(CYGPATH_W) '$(srcdir)/src/error.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-error.Tpo src/$(DEPDIR)/bluetoothd-error.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/error.c' object='src/bluetoothd-error.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-error.obj `if test -f 'src/error.c'; then $(CYGPATH_W) 'src/error.c'; else $(CYGPATH_W) '$(srcdir)/src/error.c'; fi`
+
+src/bluetoothd-manager.o: src/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-manager.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-manager.Tpo -c -o src/bluetoothd-manager.o `test -f 'src/manager.c' || echo '$(srcdir)/'`src/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-manager.Tpo src/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/manager.c' object='src/bluetoothd-manager.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-manager.o `test -f 'src/manager.c' || echo '$(srcdir)/'`src/manager.c
+
+src/bluetoothd-manager.obj: src/manager.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-manager.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-manager.Tpo -c -o src/bluetoothd-manager.obj `if test -f 'src/manager.c'; then $(CYGPATH_W) 'src/manager.c'; else $(CYGPATH_W) '$(srcdir)/src/manager.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-manager.Tpo src/$(DEPDIR)/bluetoothd-manager.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/manager.c' object='src/bluetoothd-manager.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-manager.obj `if test -f 'src/manager.c'; then $(CYGPATH_W) 'src/manager.c'; else $(CYGPATH_W) '$(srcdir)/src/manager.c'; fi`
+
+src/bluetoothd-adapter.o: src/adapter.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-adapter.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-adapter.Tpo -c -o src/bluetoothd-adapter.o `test -f 'src/adapter.c' || echo '$(srcdir)/'`src/adapter.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-adapter.Tpo src/$(DEPDIR)/bluetoothd-adapter.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/adapter.c' object='src/bluetoothd-adapter.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-adapter.o `test -f 'src/adapter.c' || echo '$(srcdir)/'`src/adapter.c
+
+src/bluetoothd-adapter.obj: src/adapter.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-adapter.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-adapter.Tpo -c -o src/bluetoothd-adapter.obj `if test -f 'src/adapter.c'; then $(CYGPATH_W) 'src/adapter.c'; else $(CYGPATH_W) '$(srcdir)/src/adapter.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-adapter.Tpo src/$(DEPDIR)/bluetoothd-adapter.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/adapter.c' object='src/bluetoothd-adapter.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-adapter.obj `if test -f 'src/adapter.c'; then $(CYGPATH_W) 'src/adapter.c'; else $(CYGPATH_W) '$(srcdir)/src/adapter.c'; fi`
+
+src/bluetoothd-device.o: src/device.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-device.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-device.Tpo -c -o src/bluetoothd-device.o `test -f 'src/device.c' || echo '$(srcdir)/'`src/device.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-device.Tpo src/$(DEPDIR)/bluetoothd-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/device.c' object='src/bluetoothd-device.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-device.o `test -f 'src/device.c' || echo '$(srcdir)/'`src/device.c
+
+src/bluetoothd-device.obj: src/device.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-device.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-device.Tpo -c -o src/bluetoothd-device.obj `if test -f 'src/device.c'; then $(CYGPATH_W) 'src/device.c'; else $(CYGPATH_W) '$(srcdir)/src/device.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-device.Tpo src/$(DEPDIR)/bluetoothd-device.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/device.c' object='src/bluetoothd-device.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-device.obj `if test -f 'src/device.c'; then $(CYGPATH_W) 'src/device.c'; else $(CYGPATH_W) '$(srcdir)/src/device.c'; fi`
+
+src/bluetoothd-dbus-common.o: src/dbus-common.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-dbus-common.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-dbus-common.Tpo -c -o src/bluetoothd-dbus-common.o `test -f 'src/dbus-common.c' || echo '$(srcdir)/'`src/dbus-common.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-dbus-common.Tpo src/$(DEPDIR)/bluetoothd-dbus-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/dbus-common.c' object='src/bluetoothd-dbus-common.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-dbus-common.o `test -f 'src/dbus-common.c' || echo '$(srcdir)/'`src/dbus-common.c
+
+src/bluetoothd-dbus-common.obj: src/dbus-common.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-dbus-common.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-dbus-common.Tpo -c -o src/bluetoothd-dbus-common.obj `if test -f 'src/dbus-common.c'; then $(CYGPATH_W) 'src/dbus-common.c'; else $(CYGPATH_W) '$(srcdir)/src/dbus-common.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-dbus-common.Tpo src/$(DEPDIR)/bluetoothd-dbus-common.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/dbus-common.c' object='src/bluetoothd-dbus-common.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-dbus-common.obj `if test -f 'src/dbus-common.c'; then $(CYGPATH_W) 'src/dbus-common.c'; else $(CYGPATH_W) '$(srcdir)/src/dbus-common.c'; fi`
+
+src/bluetoothd-event.o: src/event.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-event.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-event.Tpo -c -o src/bluetoothd-event.o `test -f 'src/event.c' || echo '$(srcdir)/'`src/event.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-event.Tpo src/$(DEPDIR)/bluetoothd-event.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/event.c' object='src/bluetoothd-event.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-event.o `test -f 'src/event.c' || echo '$(srcdir)/'`src/event.c
+
+src/bluetoothd-event.obj: src/event.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-event.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-event.Tpo -c -o src/bluetoothd-event.obj `if test -f 'src/event.c'; then $(CYGPATH_W) 'src/event.c'; else $(CYGPATH_W) '$(srcdir)/src/event.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-event.Tpo src/$(DEPDIR)/bluetoothd-event.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/event.c' object='src/bluetoothd-event.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-event.obj `if test -f 'src/event.c'; then $(CYGPATH_W) 'src/event.c'; else $(CYGPATH_W) '$(srcdir)/src/event.c'; fi`
+
+src/bluetoothd-oob.o: src/oob.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-oob.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-oob.Tpo -c -o src/bluetoothd-oob.o `test -f 'src/oob.c' || echo '$(srcdir)/'`src/oob.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-oob.Tpo src/$(DEPDIR)/bluetoothd-oob.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/oob.c' object='src/bluetoothd-oob.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-oob.o `test -f 'src/oob.c' || echo '$(srcdir)/'`src/oob.c
+
+src/bluetoothd-oob.obj: src/oob.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-oob.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-oob.Tpo -c -o src/bluetoothd-oob.obj `if test -f 'src/oob.c'; then $(CYGPATH_W) 'src/oob.c'; else $(CYGPATH_W) '$(srcdir)/src/oob.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-oob.Tpo src/$(DEPDIR)/bluetoothd-oob.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/oob.c' object='src/bluetoothd-oob.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-oob.obj `if test -f 'src/oob.c'; then $(CYGPATH_W) 'src/oob.c'; else $(CYGPATH_W) '$(srcdir)/src/oob.c'; fi`
+
+src/bluetoothd-eir.o: src/eir.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-eir.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-eir.Tpo -c -o src/bluetoothd-eir.o `test -f 'src/eir.c' || echo '$(srcdir)/'`src/eir.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-eir.Tpo src/$(DEPDIR)/bluetoothd-eir.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/eir.c' object='src/bluetoothd-eir.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-eir.o `test -f 'src/eir.c' || echo '$(srcdir)/'`src/eir.c
+
+src/bluetoothd-eir.obj: src/eir.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-eir.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-eir.Tpo -c -o src/bluetoothd-eir.obj `if test -f 'src/eir.c'; then $(CYGPATH_W) 'src/eir.c'; else $(CYGPATH_W) '$(srcdir)/src/eir.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-eir.Tpo src/$(DEPDIR)/bluetoothd-eir.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/eir.c' object='src/bluetoothd-eir.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-eir.obj `if test -f 'src/eir.c'; then $(CYGPATH_W) 'src/eir.c'; else $(CYGPATH_W) '$(srcdir)/src/eir.c'; fi`
+
+audio/bluetoothd-telephony.o: audio/telephony.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-telephony.o -MD -MP -MF audio/$(DEPDIR)/bluetoothd-telephony.Tpo -c -o audio/bluetoothd-telephony.o `test -f 'audio/telephony.c' || echo '$(srcdir)/'`audio/telephony.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-telephony.Tpo audio/$(DEPDIR)/bluetoothd-telephony.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/telephony.c' object='audio/bluetoothd-telephony.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-telephony.o `test -f 'audio/telephony.c' || echo '$(srcdir)/'`audio/telephony.c
+
+audio/bluetoothd-telephony.obj: audio/telephony.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT audio/bluetoothd-telephony.obj -MD -MP -MF audio/$(DEPDIR)/bluetoothd-telephony.Tpo -c -o audio/bluetoothd-telephony.obj `if test -f 'audio/telephony.c'; then $(CYGPATH_W) 'audio/telephony.c'; else $(CYGPATH_W) '$(srcdir)/audio/telephony.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) audio/$(DEPDIR)/bluetoothd-telephony.Tpo audio/$(DEPDIR)/bluetoothd-telephony.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='audio/telephony.c' object='audio/bluetoothd-telephony.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o audio/bluetoothd-telephony.obj `if test -f 'audio/telephony.c'; then $(CYGPATH_W) 'audio/telephony.c'; else $(CYGPATH_W) '$(srcdir)/audio/telephony.c'; fi`
+
+sap/bluetoothd-sap.o: sap/sap.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT sap/bluetoothd-sap.o -MD -MP -MF sap/$(DEPDIR)/bluetoothd-sap.Tpo -c -o sap/bluetoothd-sap.o `test -f 'sap/sap.c' || echo '$(srcdir)/'`sap/sap.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sap/$(DEPDIR)/bluetoothd-sap.Tpo sap/$(DEPDIR)/bluetoothd-sap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sap/sap.c' object='sap/bluetoothd-sap.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o sap/bluetoothd-sap.o `test -f 'sap/sap.c' || echo '$(srcdir)/'`sap/sap.c
+
+sap/bluetoothd-sap.obj: sap/sap.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT sap/bluetoothd-sap.obj -MD -MP -MF sap/$(DEPDIR)/bluetoothd-sap.Tpo -c -o sap/bluetoothd-sap.obj `if test -f 'sap/sap.c'; then $(CYGPATH_W) 'sap/sap.c'; else $(CYGPATH_W) '$(srcdir)/sap/sap.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) sap/$(DEPDIR)/bluetoothd-sap.Tpo sap/$(DEPDIR)/bluetoothd-sap.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='sap/sap.c' object='sap/bluetoothd-sap.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o sap/bluetoothd-sap.obj `if test -f 'sap/sap.c'; then $(CYGPATH_W) 'sap/sap.c'; else $(CYGPATH_W) '$(srcdir)/sap/sap.c'; fi`
+
+unit/unit_test_eir-test-eir.o: unit/test-eir.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -MT unit/unit_test_eir-test-eir.o -MD -MP -MF unit/$(DEPDIR)/unit_test_eir-test-eir.Tpo -c -o unit/unit_test_eir-test-eir.o `test -f 'unit/test-eir.c' || echo '$(srcdir)/'`unit/test-eir.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) unit/$(DEPDIR)/unit_test_eir-test-eir.Tpo unit/$(DEPDIR)/unit_test_eir-test-eir.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='unit/test-eir.c' object='unit/unit_test_eir-test-eir.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -c -o unit/unit_test_eir-test-eir.o `test -f 'unit/test-eir.c' || echo '$(srcdir)/'`unit/test-eir.c
+
+unit/unit_test_eir-test-eir.obj: unit/test-eir.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -MT unit/unit_test_eir-test-eir.obj -MD -MP -MF unit/$(DEPDIR)/unit_test_eir-test-eir.Tpo -c -o unit/unit_test_eir-test-eir.obj `if test -f 'unit/test-eir.c'; then $(CYGPATH_W) 'unit/test-eir.c'; else $(CYGPATH_W) '$(srcdir)/unit/test-eir.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) unit/$(DEPDIR)/unit_test_eir-test-eir.Tpo unit/$(DEPDIR)/unit_test_eir-test-eir.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='unit/test-eir.c' object='unit/unit_test_eir-test-eir.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -c -o unit/unit_test_eir-test-eir.obj `if test -f 'unit/test-eir.c'; then $(CYGPATH_W) 'unit/test-eir.c'; else $(CYGPATH_W) '$(srcdir)/unit/test-eir.c'; fi`
+
+src/unit_test_eir-eir.o: src/eir.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -MT src/unit_test_eir-eir.o -MD -MP -MF src/$(DEPDIR)/unit_test_eir-eir.Tpo -c -o src/unit_test_eir-eir.o `test -f 'src/eir.c' || echo '$(srcdir)/'`src/eir.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/unit_test_eir-eir.Tpo src/$(DEPDIR)/unit_test_eir-eir.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/eir.c' object='src/unit_test_eir-eir.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -c -o src/unit_test_eir-eir.o `test -f 'src/eir.c' || echo '$(srcdir)/'`src/eir.c
+
+src/unit_test_eir-eir.obj: src/eir.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -MT src/unit_test_eir-eir.obj -MD -MP -MF src/$(DEPDIR)/unit_test_eir-eir.Tpo -c -o src/unit_test_eir-eir.obj `if test -f 'src/eir.c'; then $(CYGPATH_W) 'src/eir.c'; else $(CYGPATH_W) '$(srcdir)/src/eir.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/unit_test_eir-eir.Tpo src/$(DEPDIR)/unit_test_eir-eir.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/eir.c' object='src/unit_test_eir-eir.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -c -o src/unit_test_eir-eir.obj `if test -f 'src/eir.c'; then $(CYGPATH_W) 'src/eir.c'; else $(CYGPATH_W) '$(srcdir)/src/eir.c'; fi`
+
+src/unit_test_eir-glib-helper.o: src/glib-helper.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -MT src/unit_test_eir-glib-helper.o -MD -MP -MF src/$(DEPDIR)/unit_test_eir-glib-helper.Tpo -c -o src/unit_test_eir-glib-helper.o `test -f 'src/glib-helper.c' || echo '$(srcdir)/'`src/glib-helper.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/unit_test_eir-glib-helper.Tpo src/$(DEPDIR)/unit_test_eir-glib-helper.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/glib-helper.c' object='src/unit_test_eir-glib-helper.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -c -o src/unit_test_eir-glib-helper.o `test -f 'src/glib-helper.c' || echo '$(srcdir)/'`src/glib-helper.c
+
+src/unit_test_eir-glib-helper.obj: src/glib-helper.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -MT src/unit_test_eir-glib-helper.obj -MD -MP -MF src/$(DEPDIR)/unit_test_eir-glib-helper.Tpo -c -o src/unit_test_eir-glib-helper.obj `if test -f 'src/glib-helper.c'; then $(CYGPATH_W) 'src/glib-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/glib-helper.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/unit_test_eir-glib-helper.Tpo src/$(DEPDIR)/unit_test_eir-glib-helper.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/glib-helper.c' object='src/unit_test_eir-glib-helper.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(unit_test_eir_CFLAGS) $(CFLAGS) -c -o src/unit_test_eir-glib-helper.obj `if test -f 'src/glib-helper.c'; then $(CYGPATH_W) 'src/glib-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/glib-helper.c'; fi`
+
+.l.c:
+       $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+
+.y.c:
+       $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE)
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+       -rm -rf attrib/.libs attrib/_libs
+       -rm -rf audio/.libs audio/_libs
+       -rm -rf compat/.libs compat/_libs
+       -rm -rf cups/.libs cups/_libs
+       -rm -rf emulator/.libs emulator/_libs
+       -rm -rf lib/.libs lib/_libs
+       -rm -rf mgmt/.libs mgmt/_libs
+       -rm -rf monitor/.libs monitor/_libs
+       -rm -rf plugins/.libs plugins/_libs
+       -rm -rf sbc/.libs sbc/_libs
+       -rm -rf src/.libs src/_libs
+       -rm -rf test/.libs test/_libs
+       -rm -rf tools/.libs tools/_libs
+       -rm -rf unit/.libs unit/_libs
+
+distclean-libtool:
+       -rm -f libtool config.lt
+install-man1: $(dist_man_MANS) $(man_MANS)
+       @$(NORMAL_INSTALL)
+       test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)"
+       @list=''; test -n "$(man1dir)" || exit 0; \
+       { for i in $$list; do echo "$$i"; done; \
+       l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+         sed -n '/\.1[a-z]*$$/p'; \
+       } | while read p; do \
+         if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; echo "$$p"; \
+       done | \
+       sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+             -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+       sed 'N;N;s,\n, ,g' | { \
+       list=; while read file base inst; do \
+         if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+           echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+           $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+         fi; \
+       done; \
+       for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+       while read files; do \
+         test -z "$$files" || { \
+           echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+           $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+       done; }
+
+uninstall-man1:
+       @$(NORMAL_UNINSTALL)
+       @list=''; test -n "$(man1dir)" || exit 0; \
+       files=`{ for i in $$list; do echo "$$i"; done; \
+       l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+         sed -n '/\.1[a-z]*$$/p'; \
+       } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+             -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+       dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir)
+install-man8: $(dist_man_MANS) $(man_MANS)
+       @$(NORMAL_INSTALL)
+       test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+       @list=''; test -n "$(man8dir)" || exit 0; \
+       { for i in $$list; do echo "$$i"; done; \
+       l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+         sed -n '/\.8[a-z]*$$/p'; \
+       } | while read p; do \
+         if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; echo "$$p"; \
+       done | \
+       sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+             -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+       sed 'N;N;s,\n, ,g' | { \
+       list=; while read file base inst; do \
+         if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+           echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+           $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+         fi; \
+       done; \
+       for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+       while read files; do \
+         test -z "$$files" || { \
+           echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+           $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+       done; }
+
+uninstall-man8:
+       @$(NORMAL_UNINSTALL)
+       @list=''; test -n "$(man8dir)" || exit 0; \
+       files=`{ for i in $$list; do echo "$$i"; done; \
+       l2='$(dist_man_MANS) $(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+         sed -n '/\.8[a-z]*$$/p'; \
+       } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+             -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+       dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+install-alsaconfDATA: $(alsaconf_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(alsaconfdir)" || $(MKDIR_P) "$(DESTDIR)$(alsaconfdir)"
+       @list='$(alsaconf_DATA)'; test -n "$(alsaconfdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(alsaconfdir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(alsaconfdir)" || exit $$?; \
+       done
+
+uninstall-alsaconfDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(alsaconf_DATA)'; test -n "$(alsaconfdir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       dir='$(DESTDIR)$(alsaconfdir)'; $(am__uninstall_files_from_dir)
+install-confDATA: $(conf_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(confdir)" || $(MKDIR_P) "$(DESTDIR)$(confdir)"
+       @list='$(conf_DATA)'; test -n "$(confdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(confdir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(confdir)" || exit $$?; \
+       done
+
+uninstall-confDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(conf_DATA)'; test -n "$(confdir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       dir='$(DESTDIR)$(confdir)'; $(am__uninstall_files_from_dir)
+install-dbusDATA: $(dbus_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(dbusdir)" || $(MKDIR_P) "$(DESTDIR)$(dbusdir)"
+       @list='$(dbus_DATA)'; test -n "$(dbusdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbusdir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(dbusdir)" || exit $$?; \
+       done
+
+uninstall-dbusDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(dbus_DATA)'; test -n "$(dbusdir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       dir='$(DESTDIR)$(dbusdir)'; $(am__uninstall_files_from_dir)
+install-dbusserviceDATA: $(dbusservice_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(dbusservicedir)" || $(MKDIR_P) "$(DESTDIR)$(dbusservicedir)"
+       @list='$(dbusservice_DATA)'; test -n "$(dbusservicedir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbusservicedir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(dbusservicedir)" || exit $$?; \
+       done
+
+uninstall-dbusserviceDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(dbusservice_DATA)'; test -n "$(dbusservicedir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       dir='$(DESTDIR)$(dbusservicedir)'; $(am__uninstall_files_from_dir)
+install-pkgconfigDATA: $(pkgconfig_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(pkgconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)"
+       @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
+       done
+
+uninstall-pkgconfigDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
+install-rulesDATA: $(rules_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(rulesdir)" || $(MKDIR_P) "$(DESTDIR)$(rulesdir)"
+       @list='$(rules_DATA)'; test -n "$(rulesdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(rulesdir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(rulesdir)" || exit $$?; \
+       done
+
+uninstall-rulesDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(rules_DATA)'; test -n "$(rulesdir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       dir='$(DESTDIR)$(rulesdir)'; $(am__uninstall_files_from_dir)
+install-stateDATA: $(state_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(statedir)" || $(MKDIR_P) "$(DESTDIR)$(statedir)"
+       @list='$(state_DATA)'; test -n "$(statedir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(statedir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(statedir)" || exit $$?; \
+       done
+
+uninstall-stateDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(state_DATA)'; test -n "$(statedir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       dir='$(DESTDIR)$(statedir)'; $(am__uninstall_files_from_dir)
+install-systemdunitDATA: $(systemdunit_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(systemdunitdir)" || $(MKDIR_P) "$(DESTDIR)$(systemdunitdir)"
+       @list='$(systemdunit_DATA)'; test -n "$(systemdunitdir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemdunitdir)'"; \
+         $(INSTALL_DATA) $$files "$(DESTDIR)$(systemdunitdir)" || exit $$?; \
+       done
+
+uninstall-systemdunitDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(systemdunit_DATA)'; test -n "$(systemdunitdir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       dir='$(DESTDIR)$(systemdunitdir)'; $(am__uninstall_files_from_dir)
+install-includeHEADERS: $(include_HEADERS)
+       @$(NORMAL_INSTALL)
+       test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+       @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+       for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         echo "$$d$$p"; \
+       done | $(am__base_list) | \
+       while read files; do \
+         echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+         $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+       done
+
+uninstall-includeHEADERS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+       files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+       dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       set x; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       shift; \
+       if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         if test $$# -gt 0; then \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             "$$@" $$unique; \
+         else \
+           $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+             $$unique; \
+         fi; \
+       fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       test -z "$(CTAGS_ARGS)$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && $(am__cd) $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+       @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+       srcdir=$(srcdir); export srcdir; \
+       list=' $(TESTS) '; \
+       $(am__tty_colors); \
+       if test -n "$$list"; then \
+         for tst in $$list; do \
+           if test -f ./$$tst; then dir=./; \
+           elif test -f $$tst; then dir=; \
+           else dir="$(srcdir)/"; fi; \
+           if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+             all=`expr $$all + 1`; \
+             case " $(XFAIL_TESTS) " in \
+             *[\ \     ]$$tst[\ \      ]*) \
+               xpass=`expr $$xpass + 1`; \
+               failed=`expr $$failed + 1`; \
+               col=$$red; res=XPASS; \
+             ;; \
+             *) \
+               col=$$grn; res=PASS; \
+             ;; \
+             esac; \
+           elif test $$? -ne 77; then \
+             all=`expr $$all + 1`; \
+             case " $(XFAIL_TESTS) " in \
+             *[\ \     ]$$tst[\ \      ]*) \
+               xfail=`expr $$xfail + 1`; \
+               col=$$lgn; res=XFAIL; \
+             ;; \
+             *) \
+               failed=`expr $$failed + 1`; \
+               col=$$red; res=FAIL; \
+             ;; \
+             esac; \
+           else \
+             skip=`expr $$skip + 1`; \
+             col=$$blu; res=SKIP; \
+           fi; \
+           echo "$${col}$$res$${std}: $$tst"; \
+         done; \
+         if test "$$all" -eq 1; then \
+           tests="test"; \
+           All=""; \
+         else \
+           tests="tests"; \
+           All="All "; \
+         fi; \
+         if test "$$failed" -eq 0; then \
+           if test "$$xfail" -eq 0; then \
+             banner="$$All$$all $$tests passed"; \
+           else \
+             if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+             banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+           fi; \
+         else \
+           if test "$$xpass" -eq 0; then \
+             banner="$$failed of $$all $$tests failed"; \
+           else \
+             if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+             banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+           fi; \
+         fi; \
+         dashes="$$banner"; \
+         skipped=""; \
+         if test "$$skip" -ne 0; then \
+           if test "$$skip" -eq 1; then \
+             skipped="($$skip test was not run)"; \
+           else \
+             skipped="($$skip tests were not run)"; \
+           fi; \
+           test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+             dashes="$$skipped"; \
+         fi; \
+         report=""; \
+         if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+           report="Please report to $(PACKAGE_BUGREPORT)"; \
+           test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+             dashes="$$report"; \
+         fi; \
+         dashes=`echo "$$dashes" | sed s/./=/g`; \
+         if test "$$failed" -eq 0; then \
+           col="$$grn"; \
+         else \
+           col="$$red"; \
+         fi; \
+         echo "$${col}$$dashes$${std}"; \
+         echo "$${col}$$banner$${std}"; \
+         test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+         test -z "$$report" || echo "$${col}$$report$${std}"; \
+         echo "$${col}$$dashes$${std}"; \
+         test "$$failed" -eq 0; \
+       else :; fi
+
+distdir: $(DISTFILES)
+       @list='$(MANS)'; if test -n "$$list"; then \
+         list=`for p in $$list; do \
+           if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+           if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+         if test -n "$$list" && \
+           grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+           echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
+           grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/         /' >&2; \
+           echo "       to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+           echo "       typically \`make maintainer-clean' will remove them" >&2; \
+           exit 1; \
+         else :; fi; \
+       else :; fi
+       $(am__remove_distdir)
+       test -d "$(distdir)" || mkdir "$(distdir)"
+       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       list='$(DISTFILES)'; \
+         dist_files=`for file in $$list; do echo $$file; done | \
+         sed -e "s|^$$srcdirstrip/||;t" \
+             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+       case $$dist_files in \
+         */*) $(MKDIR_P) `echo "$$dist_files" | \
+                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+                          sort -u` ;; \
+       esac; \
+       for file in $$dist_files; do \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         if test -d $$d/$$file; then \
+           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+           if test -d "$(distdir)/$$file"; then \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+             find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+           fi; \
+           cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+         else \
+           test -f "$(distdir)/$$file" \
+           || cp -p $$d/$$file "$(distdir)/$$file" \
+           || exit 1; \
+         fi; \
+       done
+       -test -n "$(am__skip_mode_fix)" \
+       || find "$(distdir)" -type d ! -perm -755 \
+               -exec chmod u+rwx,go+rx {} \; -o \
+         ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+         ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+         ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+       || chmod -R a+r "$(distdir)"
+dist-gzip: distdir
+       tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+       $(am__remove_distdir)
+
+dist-bzip2: distdir
+       tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
+       $(am__remove_distdir)
+
+dist-lzip: distdir
+       tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
+       $(am__remove_distdir)
+
+dist-lzma: distdir
+       tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
+       $(am__remove_distdir)
+
+dist-xz: distdir
+       tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
+       $(am__remove_distdir)
+
+dist-tarZ: distdir
+       tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+       $(am__remove_distdir)
+
+dist-shar: distdir
+       shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+       $(am__remove_distdir)
+
+dist-zip: distdir
+       -rm -f $(distdir).zip
+       zip -rq $(distdir).zip $(distdir)
+       $(am__remove_distdir)
+
+dist dist-all: distdir
+       tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+       $(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration.  Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+       case '$(DIST_ARCHIVES)' in \
+       *.tar.gz*) \
+         GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
+       *.tar.bz2*) \
+         bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+       *.tar.lzma*) \
+         lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\
+       *.tar.lz*) \
+         lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
+       *.tar.xz*) \
+         xz -dc $(distdir).tar.xz | $(am__untar) ;;\
+       *.tar.Z*) \
+         uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+       *.shar.gz*) \
+         GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
+       *.zip*) \
+         unzip $(distdir).zip ;;\
+       esac
+       chmod -R a-w $(distdir); chmod a+w $(distdir)
+       mkdir $(distdir)/_build
+       mkdir $(distdir)/_inst
+       chmod a-w $(distdir)
+       test -d $(distdir)/_build || exit 0; \
+       dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+         && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+         && am__cwd=`pwd` \
+         && $(am__cd) $(distdir)/_build \
+         && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+           $(AM_DISTCHECK_CONFIGURE_FLAGS) \
+           $(DISTCHECK_CONFIGURE_FLAGS) \
+         && $(MAKE) $(AM_MAKEFLAGS) \
+         && $(MAKE) $(AM_MAKEFLAGS) dvi \
+         && $(MAKE) $(AM_MAKEFLAGS) check \
+         && $(MAKE) $(AM_MAKEFLAGS) install \
+         && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+         && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+         && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+               distuninstallcheck \
+         && chmod -R a-w "$$dc_install_base" \
+         && ({ \
+              (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+                   distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+             } || { rm -rf "$$dc_destdir"; exit 1; }) \
+         && rm -rf "$$dc_destdir" \
+         && $(MAKE) $(AM_MAKEFLAGS) dist \
+         && rm -rf $(DIST_ARCHIVES) \
+         && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+         && cd "$$am__cwd" \
+         || exit 1
+       $(am__remove_distdir)
+       @(echo "$(distdir) archives ready for distribution: "; \
+         list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+         sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+       @test -n '$(distuninstallcheck_dir)' || { \
+         echo 'ERROR: trying to run $@ with an empty' \
+              '$$(distuninstallcheck_dir)' >&2; \
+         exit 1; \
+       }; \
+       $(am__cd) '$(distuninstallcheck_dir)' || { \
+         echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
+         exit 1; \
+       }; \
+       test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
+          || { echo "ERROR: files left after uninstall:" ; \
+               if test -n "$(DESTDIR)"; then \
+                 echo "  (check DESTDIR support)"; \
+               fi ; \
+               $(distuninstallcheck_listfiles) ; \
+               exit 1; } >&2
+distcleancheck: distclean
+       @if test '$(srcdir)' = . ; then \
+         echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+         exit 1 ; \
+       fi
+       @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+         || { echo "ERROR: files left in build directory after distclean:" ; \
+              $(distcleancheck_listfiles) ; \
+              exit 1; } >&2
+check-am: all-am
+       $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) \
+               $(MANS) $(DATA) $(HEADERS) config.h
+install-binPROGRAMS: install-libLTLIBRARIES
+
+installdirs:
+       for dir in "$(DESTDIR)$(alsadir)" "$(DESTDIR)$(gstreamerdir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(cupsdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(udevdir)" "$(DESTDIR)$(udevdir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(alsaconfdir)" "$(DESTDIR)$(confdir)" "$(DESTDIR)$(dbusdir)" "$(DESTDIR)$(dbusservicedir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(rulesdir)" "$(DESTDIR)$(statedir)" "$(DESTDIR)$(systemdunitdir)" "$(DESTDIR)$(includedir)"; do \
+         test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+       done
+install: $(BUILT_SOURCES)
+       $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       if test -z '$(STRIP)'; then \
+         $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+           install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+             install; \
+       else \
+         $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+           install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+           "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+       fi
+mostlyclean-generic:
+
+clean-generic:
+       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+       -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+       -rm -f alert/$(DEPDIR)/$(am__dirstamp)
+       -rm -f alert/$(am__dirstamp)
+       -rm -f attrib/$(DEPDIR)/$(am__dirstamp)
+       -rm -f attrib/$(am__dirstamp)
+       -rm -f audio/$(DEPDIR)/$(am__dirstamp)
+       -rm -f audio/$(am__dirstamp)
+       -rm -f btio/$(DEPDIR)/$(am__dirstamp)
+       -rm -f btio/$(am__dirstamp)
+       -rm -f compat/$(DEPDIR)/$(am__dirstamp)
+       -rm -f compat/$(am__dirstamp)
+       -rm -f cups/$(DEPDIR)/$(am__dirstamp)
+       -rm -f cups/$(am__dirstamp)
+       -rm -f deviceinfo/$(DEPDIR)/$(am__dirstamp)
+       -rm -f deviceinfo/$(am__dirstamp)
+       -rm -f emulator/$(DEPDIR)/$(am__dirstamp)
+       -rm -f emulator/$(am__dirstamp)
+       -rm -f gdbus/$(DEPDIR)/$(am__dirstamp)
+       -rm -f gdbus/$(am__dirstamp)
+       -rm -f health/$(DEPDIR)/$(am__dirstamp)
+       -rm -f health/$(am__dirstamp)
+       -rm -f input/$(DEPDIR)/$(am__dirstamp)
+       -rm -f input/$(am__dirstamp)
+       -rm -f lib/$(DEPDIR)/$(am__dirstamp)
+       -rm -f lib/$(am__dirstamp)
+       -rm -f mgmt/$(DEPDIR)/$(am__dirstamp)
+       -rm -f mgmt/$(am__dirstamp)
+       -rm -f monitor/$(DEPDIR)/$(am__dirstamp)
+       -rm -f monitor/$(am__dirstamp)
+       -rm -f network/$(DEPDIR)/$(am__dirstamp)
+       -rm -f network/$(am__dirstamp)
+       -rm -f plugins/$(DEPDIR)/$(am__dirstamp)
+       -rm -f plugins/$(am__dirstamp)
+       -rm -f proximity/$(DEPDIR)/$(am__dirstamp)
+       -rm -f proximity/$(am__dirstamp)
+       -rm -f sap/$(DEPDIR)/$(am__dirstamp)
+       -rm -f sap/$(am__dirstamp)
+       -rm -f sbc/$(DEPDIR)/$(am__dirstamp)
+       -rm -f sbc/$(am__dirstamp)
+       -rm -f serial/$(DEPDIR)/$(am__dirstamp)
+       -rm -f serial/$(am__dirstamp)
+       -rm -f src/$(DEPDIR)/$(am__dirstamp)
+       -rm -f src/$(am__dirstamp)
+       -rm -f test/$(DEPDIR)/$(am__dirstamp)
+       -rm -f test/$(am__dirstamp)
+       -rm -f thermometer/$(DEPDIR)/$(am__dirstamp)
+       -rm -f thermometer/$(am__dirstamp)
+       -rm -f time/$(DEPDIR)/$(am__dirstamp)
+       -rm -f time/$(am__dirstamp)
+       -rm -f tools/$(DEPDIR)/$(am__dirstamp)
+       -rm -f tools/$(am__dirstamp)
+       -rm -f unit/$(DEPDIR)/$(am__dirstamp)
+       -rm -f unit/$(am__dirstamp)
+       -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+       -rm -f tools/lexer.c
+       -rm -f tools/parser.c
+       -rm -f tools/parser.h
+       -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+       -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-alsaLTLIBRARIES clean-binPROGRAMS clean-cupsPROGRAMS \
+       clean-generic clean-gstreamerLTLIBRARIES clean-libLTLIBRARIES \
+       clean-libtool clean-local clean-noinstLIBRARIES \
+       clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
+       clean-pluginLTLIBRARIES clean-sbinPROGRAMS clean-udevPROGRAMS \
+       mostlyclean-am
+
+distclean: distclean-am
+       -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+       -rm -rf alert/$(DEPDIR) attrib/$(DEPDIR) audio/$(DEPDIR) btio/$(DEPDIR) compat/$(DEPDIR) cups/$(DEPDIR) deviceinfo/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) health/$(DEPDIR) input/$(DEPDIR) lib/$(DEPDIR) mgmt/$(DEPDIR) monitor/$(DEPDIR) network/$(DEPDIR) plugins/$(DEPDIR) proximity/$(DEPDIR) sap/$(DEPDIR) sbc/$(DEPDIR) serial/$(DEPDIR) src/$(DEPDIR) test/$(DEPDIR) thermometer/$(DEPDIR) time/$(DEPDIR) tools/$(DEPDIR) unit/$(DEPDIR)
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-hdr distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-alsaLTLIBRARIES install-alsaconfDATA \
+       install-confDATA install-cupsPROGRAMS install-dbusDATA \
+       install-dbusserviceDATA install-dist_udevSCRIPTS \
+       install-gstreamerLTLIBRARIES install-includeHEADERS \
+       install-man install-pkgconfigDATA install-pluginLTLIBRARIES \
+       install-rulesDATA install-stateDATA install-systemdunitDATA \
+       install-udevPROGRAMS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \
+       install-sbinPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man1 install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+       -rm -rf $(top_srcdir)/autom4te.cache
+       -rm -rf alert/$(DEPDIR) attrib/$(DEPDIR) audio/$(DEPDIR) btio/$(DEPDIR) compat/$(DEPDIR) cups/$(DEPDIR) deviceinfo/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) health/$(DEPDIR) input/$(DEPDIR) lib/$(DEPDIR) mgmt/$(DEPDIR) monitor/$(DEPDIR) network/$(DEPDIR) plugins/$(DEPDIR) proximity/$(DEPDIR) sap/$(DEPDIR) sbc/$(DEPDIR) serial/$(DEPDIR) src/$(DEPDIR) test/$(DEPDIR) thermometer/$(DEPDIR) time/$(DEPDIR) tools/$(DEPDIR) unit/$(DEPDIR)
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-alsaLTLIBRARIES uninstall-alsaconfDATA \
+       uninstall-binPROGRAMS uninstall-confDATA \
+       uninstall-cupsPROGRAMS uninstall-dbusDATA \
+       uninstall-dbusserviceDATA uninstall-dist_udevSCRIPTS \
+       uninstall-gstreamerLTLIBRARIES uninstall-includeHEADERS \
+       uninstall-libLTLIBRARIES uninstall-man uninstall-pkgconfigDATA \
+       uninstall-pluginLTLIBRARIES uninstall-rulesDATA \
+       uninstall-sbinPROGRAMS uninstall-stateDATA \
+       uninstall-systemdunitDATA uninstall-udevPROGRAMS
+
+uninstall-man: uninstall-man1 uninstall-man8
+
+.MAKE: all check check-am install install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am am--refresh check check-TESTS check-am \
+       clean clean-alsaLTLIBRARIES clean-binPROGRAMS \
+       clean-cupsPROGRAMS clean-generic clean-gstreamerLTLIBRARIES \
+       clean-libLTLIBRARIES clean-libtool clean-local \
+       clean-noinstLIBRARIES clean-noinstLTLIBRARIES \
+       clean-noinstPROGRAMS clean-pluginLTLIBRARIES \
+       clean-sbinPROGRAMS clean-udevPROGRAMS ctags dist dist-all \
+       dist-bzip2 dist-gzip dist-lzip dist-lzma dist-shar dist-tarZ \
+       dist-xz dist-zip distcheck distclean distclean-compile \
+       distclean-generic distclean-hdr distclean-libtool \
+       distclean-tags distcleancheck distdir distuninstallcheck dvi \
+       dvi-am html html-am info info-am install \
+       install-alsaLTLIBRARIES install-alsaconfDATA install-am \
+       install-binPROGRAMS install-confDATA install-cupsPROGRAMS \
+       install-data install-data-am install-dbusDATA \
+       install-dbusserviceDATA install-dist_udevSCRIPTS install-dvi \
+       install-dvi-am install-exec install-exec-am \
+       install-gstreamerLTLIBRARIES install-html install-html-am \
+       install-includeHEADERS install-info install-info-am \
+       install-libLTLIBRARIES install-man install-man1 install-man8 \
+       install-pdf install-pdf-am install-pkgconfigDATA \
+       install-pluginLTLIBRARIES install-ps install-ps-am \
+       install-rulesDATA install-sbinPROGRAMS install-stateDATA \
+       install-strip install-systemdunitDATA install-udevPROGRAMS \
+       installcheck installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags uninstall uninstall-alsaLTLIBRARIES \
+       uninstall-alsaconfDATA uninstall-am uninstall-binPROGRAMS \
+       uninstall-confDATA uninstall-cupsPROGRAMS uninstall-dbusDATA \
+       uninstall-dbusserviceDATA uninstall-dist_udevSCRIPTS \
+       uninstall-gstreamerLTLIBRARIES uninstall-includeHEADERS \
+       uninstall-libLTLIBRARIES uninstall-man uninstall-man1 \
+       uninstall-man8 uninstall-pkgconfigDATA \
+       uninstall-pluginLTLIBRARIES uninstall-rulesDATA \
+       uninstall-sbinPROGRAMS uninstall-stateDATA \
+       uninstall-systemdunitDATA uninstall-udevPROGRAMS
+
+
+@TOOLS_TRUE@tools/kword.c: tools/parser.h
+
+src/builtin.h: src/genbuiltin $(builtin_sources)
+       $(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
+
+audio/telephony.c: audio/@TELEPHONY_DRIVER@
+       $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
+
+sap/sap.c: sap/@SAP_DRIVER@
+       $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+
+scripts/%.rules:
+       $(AM_V_GEN)cp $(subst 97-,,$@) $@
+
+$(lib_libbluetooth_la_OBJECTS): $(local_headers)
+
+lib/bluetooth/%.h: lib/%.h
+       $(AM_V_at)$(MKDIR_P) lib/bluetooth
+       $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
+
+clean-local:
+       $(RM) -r lib/bluetooth
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Makefile.tools b/Makefile.tools
new file mode 100644 (file)
index 0000000..5b1efb8
--- /dev/null
@@ -0,0 +1,267 @@
+
+if TOOLS
+if DATAFILES
+conf_DATA += tools/rfcomm.conf
+endif
+
+bin_PROGRAMS += tools/rfcomm tools/l2ping \
+                               tools/hcitool tools/sdptool tools/ciptool
+
+sbin_PROGRAMS += tools/hciattach tools/hciconfig
+
+noinst_PROGRAMS += tools/avinfo tools/ppporc \
+                               tools/hcieventmask tools/hcisecfilter
+
+tools/kword.c: tools/parser.h
+
+tools_rfcomm_SOURCES = tools/rfcomm.c tools/parser.y tools/lexer.l \
+                                       tools/kword.h tools/kword.c
+EXTRA_tools_rfcomm_SOURCES = tools/parser.h tools/parser.c \
+                                                       tools/lexer.c
+tools_rfcomm_LDADD = lib/libbluetooth-private.la
+
+tools_l2ping_LDADD = lib/libbluetooth-private.la
+
+tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
+                                               tools/hciattach_st.c \
+                                               tools/hciattach_ti.c \
+                                               tools/hciattach_tialt.c \
+                                               tools/hciattach_ath3k.c \
+                                               tools/hciattach_qualcomm.c \
+                                               tools/hciattach_intel.c
+tools_hciattach_LDADD = lib/libbluetooth-private.la
+
+tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c \
+                                               src/textfile.h src/textfile.c
+tools_hciconfig_LDADD = lib/libbluetooth-private.la
+
+tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c \
+                                               src/textfile.h src/textfile.c
+tools_hcitool_LDADD = lib/libbluetooth-private.la
+
+tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
+tools_sdptool_LDADD = lib/libbluetooth-private.la
+
+tools_ciptool_LDADD = lib/libbluetooth-private.la
+
+tools_avinfo_LDADD = lib/libbluetooth-private.la
+
+tools_ppporc_LDADD = lib/libbluetooth-private.la
+
+tools_hcieventmask_LDADD = lib/libbluetooth-private.la
+
+noinst_PROGRAMS += mgmt/btmgmt monitor/btmon emulator/btvirt
+
+mgmt_btmgmt_SOURCES = mgmt/main.c src/glib-helper.c
+mgmt_btmgmt_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@
+
+monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
+                                       monitor/mainloop.h monitor/mainloop.c \
+                                       monitor/hcidump.h monitor/hcidump.c \
+                                       monitor/btsnoop.h monitor/btsnoop.c \
+                                       monitor/control.h monitor/control.c \
+                                       monitor/packet.h monitor/packet.c
+monitor_btmon_LDADD = lib/libbluetooth-private.la
+
+emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
+                                       monitor/mainloop.h monitor/mainloop.c \
+                                       emulator/server.h emulator/server.c \
+                                       emulator/vhci.h emulator/vhci.c \
+                                       emulator/btdev.h emulator/btdev.c
+
+if READLINE
+bin_PROGRAMS += attrib/gatttool
+
+attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
+                               attrib/gattrib.c btio/btio.c \
+                               attrib/gatttool.h attrib/interactive.c \
+                               attrib/utils.c src/log.c
+attrib_gatttool_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @READLINE_LIBS@
+endif
+
+dist_man_MANS += tools/rfcomm.1 tools/l2ping.8 \
+                       tools/hciattach.8 tools/hciconfig.8 \
+                       tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
+else
+EXTRA_DIST += tools/rfcomm.1 tools/l2ping.8 \
+                       tools/hciattach.8 tools/hciconfig.8 \
+                       tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
+endif
+
+CLEANFILES += tools/lexer.c tools/parser.c tools/parser.h
+
+EXTRA_DIST += tools/rfcomm.conf
+
+if BCCMD
+sbin_PROGRAMS += tools/bccmd
+
+tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h tools/csr.c \
+                       tools/csr_hci.c tools/csr_h4.c tools/csr_3wire.c \
+                       tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c
+tools_bccmd_LDADD = lib/libbluetooth-private.la
+
+if USB
+tools_bccmd_SOURCES += tools/csr_usb.c
+tools_bccmd_LDADD += @USB_LIBS@
+endif
+
+dist_man_MANS += tools/bccmd.8
+else
+EXTRA_DIST += tools/bccmd.8
+endif
+
+if HID2HCI
+udevdir = @UDEV_DIR@
+
+udev_PROGRAMS = tools/hid2hci
+
+tools_hid2hci_LDADD = @USB_LIBS@ @UDEV_LIBS@
+
+dist_man_MANS += tools/hid2hci.8
+else
+EXTRA_DIST += tools/hid2hci.8
+endif
+
+if DFUTOOL
+bin_PROGRAMS += tools/dfutool
+
+tools_dfutool_SOURCES = tools/dfutool.c tools/dfu.h tools/dfu.c
+tools_dfutool_LDADD = @USB_LIBS@
+
+dist_man_MANS += tools/dfutool.1
+else
+EXTRA_DIST += tools/dfutool.1
+endif
+
+
+if USB
+noinst_PROGRAMS += tools/dfubabel tools/avctrl
+
+tools_dfubabel_LDADD = @USB_LIBS@
+
+tools_avctrl_LDADD = @USB_LIBS@
+endif
+
+EXTRA_DIST += tools/dfubabel.1 tools/avctrl.8
+
+
+if CUPS
+cupsdir = $(libdir)/cups/backend
+
+cups_PROGRAMS = cups/bluetooth
+
+cups_bluetooth_SOURCES = $(gdbus_sources) cups/main.c cups/cups.h \
+                                       cups/sdp.c cups/spp.c cups/hcrp.c
+
+cups_bluetooth_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ lib/libbluetooth-private.la
+endif
+
+
+if TEST
+sbin_PROGRAMS += test/hciemu
+
+bin_PROGRAMS += test/l2test test/rctest
+
+noinst_PROGRAMS += test/gaptest test/sdptest test/scotest \
+                       test/attest test/hstest test/avtest test/ipctest \
+                                       test/lmptest test/bdaddr test/agent \
+                                       test/btiotest test/test-textfile \
+                                       test/uuidtest test/mpris-player
+
+test_hciemu_LDADD = lib/libbluetooth-private.la
+
+test_l2test_LDADD = lib/libbluetooth-private.la
+
+test_rctest_LDADD = lib/libbluetooth-private.la
+
+test_gaptest_LDADD = @DBUS_LIBS@
+
+test_sdptest_LDADD = lib/libbluetooth-private.la
+
+test_scotest_LDADD = lib/libbluetooth-private.la
+
+test_attest_LDADD = lib/libbluetooth-private.la
+
+test_hstest_LDADD = lib/libbluetooth-private.la
+
+test_avtest_LDADD = lib/libbluetooth-private.la
+
+test_lmptest_LDADD = lib/libbluetooth-private.la
+
+test_ipctest_SOURCES = test/ipctest.c audio/ipc.h audio/ipc.c
+test_ipctest_LDADD= @GLIB_LIBS@ sbc/libsbc.la
+
+test_bdaddr_SOURCES = test/bdaddr.c src/oui.h src/oui.c
+test_bdaddr_LDADD = lib/libbluetooth-private.la
+
+test_agent_LDADD = @DBUS_LIBS@
+
+test_btiotest_SOURCES = test/btiotest.c btio/btio.h btio/btio.c
+test_btiotest_LDADD = @GLIB_LIBS@ lib/libbluetooth-private.la
+
+test_uuidtest_SOURCES = test/uuidtest.c
+test_uuidtest_LDADD = lib/libbluetooth-private.la
+
+test_mpris_player_SOURCES = test/mpris-player.c
+test_mpris_player_LDADD = @DBUS_LIBS@ @GLIB_LIBS@
+
+test_test_textfile_SOURCES = test/test-textfile.c src/textfile.h src/textfile.c
+
+dist_man_MANS += test/rctest.1 test/hciemu.1
+
+EXTRA_DIST += test/bdaddr.8
+else
+EXTRA_DIST += test/rctest.1 test/hciemu.1 test/bdaddr.8
+endif
+
+EXTRA_DIST += test/sap-client test/hsplay test/hsmicro \
+               test/dbusdef.py test/monitor-bluetooth test/list-devices \
+               test/test-discovery test/test-manager test/test-adapter \
+               test/test-device test/test-service test/test-serial \
+               test/test-telephony test/test-network test/simple-agent \
+               test/simple-service test/simple-endpoint test/test-audio \
+               test/test-input test/test-sap-server test/test-oob \
+               test/test-attrib test/test-proximity test/test-thermometer \
+               test/test-serial-proxy test/test-health test/test-health-sink \
+               test/service-record.dtd test/service-did.xml \
+               test/service-spp.xml test/service-opp.xml test/service-ftp.xml \
+               test/simple-player test/test-nap
+
+if HIDD
+bin_PROGRAMS += compat/hidd
+
+compat_hidd_SOURCES = compat/hidd.c compat/hidd.h src/uinput.h \
+                               compat/sdp.h compat/sdp.c compat/fakehid.c \
+                                               src/textfile.h src/textfile.c
+compat_hidd_LDADD = -lm lib/libbluetooth-private.la
+
+dist_man_MANS += compat/hidd.1
+else
+EXTRA_DIST += compat/hidd.1
+endif
+
+if PAND
+bin_PROGRAMS += compat/pand
+
+compat_pand_SOURCES = compat/pand.c compat/pand.h \
+                               compat/bnep.c compat/sdp.h compat/sdp.c \
+                                               src/textfile.h src/textfile.c
+compat_pand_LDADD = lib/libbluetooth-private.la
+
+dist_man_MANS += compat/pand.1
+else
+EXTRA_DIST += compat/pand.1
+endif
+
+if DUND
+bin_PROGRAMS += compat/dund
+
+compat_dund_SOURCES = compat/dund.c compat/dund.h compat/lib.h \
+                       compat/sdp.h compat/sdp.c compat/dun.c compat/msdun.c \
+                                               src/textfile.h src/textfile.c
+compat_dund_LDADD = lib/libbluetooth-private.la
+
+dist_man_MANS += compat/dund.1
+else
+EXTRA_DIST += compat/dund.1
+endif
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..77778e0
--- /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-2010  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/TODO b/TODO
new file mode 100644 (file)
index 0000000..17e9848
--- /dev/null
+++ b/TODO
@@ -0,0 +1,229 @@
+Background
+==========
+
+- Priority scale: High, Medium and Low
+
+- Complexity scale: C1, C2, C4 and C8.  The complexity scale is exponential,
+  with complexity 1 being the lowest complexity.  Complexity is a function
+  of both task 'complexity' and task 'scope'.
+
+  The general rule of thumb is that a complexity 1 task should take 1-2 weeks
+  for a person very familiar with BlueZ codebase.  Higher complexity tasks
+  require more time and have higher uncertainty.
+
+  Higher complexity tasks should be refined into several lower complexity tasks
+  once the task is better understood.
+
+General
+==========
+
+- UUID handling: Use the new functions created for UUID handling in all parts
+  of BlueZ code.  Currently, the new bt_uuid_* functions are being used by
+  GATT-related code only.
+
+  Priority: high
+  Complexity: C4
+
+- Rename glib-helper file to a more convenient name. The ideia is try to keep
+  only sdp helpers functions. bt_* prefix shall be also changed.
+
+  Priority: Low
+  Complexity: C1
+
+Low Energy
+==========
+
+- Advertising management. Adapter interface needs to be changed to manage
+  connection modes, adapter type and advertising policy. See Volume 3,
+  Part C, section 9.3. If Attribute Server is enabled the LE capable
+  adapter shall to start advertising. Further investigation is necessary
+  to define which connectable mode needs to be supported: Non-connectable,
+  directed connectable and undirected connectable. Basically, two connectable
+  scenarios shall be addressed:
+  1. GATT client is disconnected, but intends to become a Peripheral to
+     receive indications/notifications.
+  2. GATT server intends to accept connections.
+
+  Priority: Medium
+  Complexity: C2
+
+- Define Auto Connection Establishment Procedure. Some profiles such as
+  Proximity requires an active link to identify path lost situation. It is
+  necessary to define how to manage connections, it seems that White List
+  is appropriated to address auto connections, however is not clear if the
+  this procedure shall be a profile specific detail or if the remote device
+  object can expose a property "WhiteList", maybe "Trusted" property can be
+  also used for this purpose. Another alternative is to define a method to
+  allow application to request/register the wanted scanning/connection
+  parameters. Before start this task, a RFC/PATCH shall be sent to the ML.
+  See Volume 3, Part C, section 9.3.5 for more information.
+
+  Priority: Medium
+  Complexity: C2
+
+- Implement a tool(or extend hciconfig) to setup the advertising parameters
+  and data. Extend hciconfig passing extra arguments when enabling the
+  advertises is not the right approach, it will be almost impossible to
+  address all arguments needed in an acceptable way. For testing, we need
+  a tool to change easily the AD Flags, the UUIDs and other data that can be
+  exported through the advertising data field. Suggestions: 1) extend hciconfig
+  passing a config file when enabling advertises; 2) write a ncurses based tool
+
+  Priority: Medium
+  Complexity: C2
+
+- Add new property in the DeviceFound signal to report the device type:
+  BR/EDR, single mode or dual-mode.
+
+  Priority: Medium
+  Complexity: C1
+
+- Privacy: When privacy is enabled in the adapter, LE scanning/connection
+  should use a private address. StartDiscovery method shall be changed and
+  new adapter property shall be added.
+
+  Priority: Medium
+  Complexity: C1
+
+- Static random address setup and storage. Once this address is written
+  in the a given remote, the address can not be changed anymore.
+
+  Priority: Low
+  Complexity: C1
+
+- Reconnection address: Reconnection address is a non resolvable private
+  address that the central writes in the peripheral. BlueZ will support
+  multiple profiles, it is not clear how it needs to be implemented.
+  Further discussion is necessary.
+
+  Priority: Low
+  Complexity: C2
+
+- Device Name Characteristic is a GAP characteristic for Low Energy. This
+  characteristic shall be integrated/used in the discovery procedure. The
+  ideia is to report the value of this characteristic using DeviceFound signals.
+  Discussion with the community is needed before to start this task. Other GAP
+  characteristics for LE needs to follow a similar approach. It is not clear
+  if all GAP characteristics can be exposed using properties instead of a primary
+  service characteristics.
+  See Volume 3, Part C, section 12.1 for more information.
+
+  Priority: Low
+  Complexity: C2
+
+ATT/GATT
+========
+
+- At the moment authentication and authorization is not supported at the
+  same time, read/write requirements in the attribute server needs to
+  be extended. According to Bluetooth Specification a server shall check
+  authentication and authorization requirements before any other check is
+  performed.
+
+  Priority: Medium
+  Complexity: C1
+
+- ATT/GATT parsing to hcidump. Partially implemented, missing to fix
+  multiple advertises in the same event and RSSI.
+
+  Priority: Medium
+  Complexity: C2
+
+- Implement ATT PDU validation. Malformed PDUs can cause division by zero
+  when decoding PDUs. A proper error PDU should be returned for this case.
+  See decoding function in att.c file.
+
+  Priority: Medium
+  Complexity: C1
+
+- Fix hard-coded PSM for GATT services over basic rate.
+
+  Priority: Low
+  Complexity: C1
+
+- Refactor read_by_group() and read_by_type() in src/attrib-server.c
+  (they've grown simply too big). First step could be to move out the
+  long for-loops to new functions called e.g. get_groups() and get_types().
+
+  Priority: Low
+  Complexity: C1
+
+- Agent for characteristics: Agent interface should be extended to support
+  authorization per characteristic if the remote is not in the trusted list.
+
+  Priority: Low
+  Complexity: C1
+
+- gatttool should have the ability to wait for req responses before
+  quitting (some servers require a small sleep even with cmd's). Maybe a
+  --delay-exit or --timeout command line switch.
+
+  Priority: Low
+  Complexity: C1
+
+- Refactoring of gatt.c functions. Currently, the callbacks of the services
+  and characteristics discovery functions return the ATT PDU and the caller
+  needs to call again the same function to fetch the remaining data when
+  necessary. Investigate if all results can be returned in the callback
+  result to avoid repeated code. Before change the code, please analyze
+  if this change will not break the GATT/ATT qualification tests. Maybe
+  an interactive fetch/query is necessary to pass the tests.
+
+  Priority: Low
+  Complexity: C1
+
+- Client needs to export a property in the Device Characteristic hierarchy
+  to manage characteristic value changes reports in the remote device.
+  Currently, Client Characteristic Configuration attribute is not exposed
+  as an object. The user needs to use gatttool to change the value of the
+  this attribute to receive notification/indications. Export this attribute
+  as a property is a proposal that needs further discussion.
+
+  Priority: Low
+  Complexity: C1
+
+- Attribute server should process queued GATT/ATT commands if the
+  client disconnects. The client can simply send a command and quit,
+  without wait for a response(ex: Write Command). For this scenario
+  that the client disconnects the link quickly the queued received
+  command is ignored.
+
+  Priority: Low
+  Complecity: C1
+
+- Add sdp discovery support to gattool with BR (--sdp, default is 0x1f)
+
+  Priority: Low
+  Complexity: C1
+
+- Implement Server characteristic Configuration support in the attribute
+  server to manage characteristic value broadcasting. There is a single
+  instance of the Server Characteristic Configuration for all clients.
+  See Volume 3, Part G, section 3.3.3.4 for more information.
+
+  Priority: Low
+  Complexity: C1
+
+- Long write is not implemented. Attribute server, client and command line
+  tool shall be changed to support this feature.
+
+  Priority: Low
+  Complexity: C2
+
+- Define attribute server API. External applications needs to register,
+  change attributes and to be notified about changes. Example: Proximity,
+  Time and Alert Profiles. "Local Service hierarchy" in the attribute-api
+  needs to be proposed and a RFC shall be sent to the ML.
+
+  Priority: Low
+  Complexity: C2
+  Owner: Anderson Lizardo <anderson.lizardo@openbossa.org>
+
+Management Interface
+====================
+
+- Whitelist support (initially only for LE)
+
+  Priority: Medium
+  Complexity: C2
+  Owner: Andre Guedes <andre.guedes@openbossa.org>
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644 (file)
index 0000000..1d6d736
--- /dev/null
@@ -0,0 +1,401 @@
+AC_DEFUN([AC_PROG_CC_PIE], [
+       AC_CACHE_CHECK([whether ${CC-cc} accepts -fPIE], ac_cv_prog_cc_pie, [
+               echo 'void f(){}' > conftest.c
+               if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+                       ac_cv_prog_cc_pie=yes
+               else
+                       ac_cv_prog_cc_pie=no
+               fi
+               rm -rf conftest*
+       ])
+])
+
+AC_DEFUN([COMPILER_FLAGS], [
+       with_cflags=""
+       if (test "$USE_MAINTAINER_MODE" = "yes"); then
+               with_cflags="$with_cflags -Wall -Werror -Wextra"
+               with_cflags="$with_cflags -Wno-unused-parameter"
+               with_cflags="$with_cflags -Wno-missing-field-initializers"
+               with_cflags="$with_cflags -Wdeclaration-after-statement"
+               with_cflags="$with_cflags -Wmissing-declarations"
+               with_cflags="$with_cflags -Wredundant-decls"
+               with_cflags="$with_cflags -Wcast-align"
+               with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
+       fi
+
+       AC_SUBST([WARNING_CFLAGS], $with_cflags)
+])
+
+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_DIR="`$PKG_CONFIG --variable=udevdir udev`"
+       if (test -z "${UDEV_DIR}"); then
+               UDEV_DIR="/lib/udev"
+       fi
+       AC_SUBST(UDEV_DIR)
+])
+
+AC_DEFUN([AC_PATH_DBUS], [
+       PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes,
+                               AC_MSG_ERROR(D-Bus >= 1.4 is required))
+       AC_SUBST(DBUS_CFLAGS)
+       AC_SUBST(DBUS_LIBS)
+])
+
+AC_DEFUN([AC_PATH_GLIB], [
+       PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes,
+                               AC_MSG_ERROR(GLib >= 2.28 is required))
+       AC_SUBST(GLIB_CFLAGS)
+       AC_SUBST(GLIB_LIBS)
+])
+
+AC_DEFUN([AC_PATH_GSTREAMER], [
+       PKG_CHECK_MODULES(GSTREAMER, gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10, gstreamer_found=yes,
+                               AC_MSG_WARN(GStreamer library version 0.10.30 or later is required);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_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_UDEV], [
+       PKG_CHECK_MODULES(UDEV, libudev, udev_found=yes, udev_found=no)
+       AC_SUBST(UDEV_CFLAGS)
+       AC_SUBST(UDEV_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_PATH_READLINE], [
+       AC_CHECK_HEADER(readline/readline.h,
+               AC_CHECK_LIB(readline, main,
+                       [ readline_found=yes
+                       AC_SUBST(READLINE_LIBS, "-lreadline")
+                       ], readline_found=no),
+               [])
+])
+
+AC_DEFUN([AC_PATH_CHECK], [
+       PKG_CHECK_MODULES(CHECK, check >= 0.9.6, check_found=yes, check_found=no)
+       AC_SUBST(CHECK_CFLAGS)
+       AC_SUBST(CHECK_LIBS)
+])
+
+AC_DEFUN([AC_PATH_OUI], [
+       AC_ARG_WITH(ouifile,
+                   AS_HELP_STRING([--with-ouifile=PATH],[Path to the oui.txt file @<:@auto@:>@]),
+                   [ac_with_ouifile=$withval],
+                   [ac_with_ouifile="/var/lib/misc/oui.txt"])
+       AC_DEFINE_UNQUOTED(OUIFILE, ["$ac_with_ouifile"], [Define the OUI file path])
+])
+
+AC_DEFUN([AC_ARG_BLUEZ], [
+       debug_enable=no
+       optimization_enable=yes
+       fortify_enable=yes
+       pie_enable=yes
+       sndfile_enable=${sndfile_found}
+       hal_enable=no
+       usb_enable=${usb_found}
+       alsa_enable=${alsa_found}
+       gstreamer_enable=${gstreamer_found}
+       audio_enable=yes
+       input_enable=yes
+       serial_enable=yes
+       network_enable=yes
+       sap_enable=no
+       service_enable=yes
+       health_enable=no
+       pnat_enable=no
+       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
+       datafiles_enable=yes
+       telephony_driver=dummy
+       maemo6_enable=no
+       sap_driver=dummy
+       dbusoob_enable=no
+       wiimote_enable=no
+       gatt_enable=no
+
+       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(sap, AC_HELP_STRING([--enable-sap], [enable sap plugin]), [
+               sap_enable=${enableval}
+       ])
+
+       AC_ARG_WITH(sap, AC_HELP_STRING([--with-sap=DRIVER], [select SAP driver]), [
+               sap_driver=${withval}
+       ])
+       AC_SUBST([SAP_DRIVER], [sap-${sap_driver}.c])
+
+       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(health, AC_HELP_STRING([--enable-health], [enable health plugin]), [
+               health_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(pnat, AC_HELP_STRING([--enable-pnat], [enable pnat plugin]), [
+               pnat_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(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(datafiles, AC_HELP_STRING([--enable-datafiles], [install Bluetooth configuration and data files]), [
+               datafiles_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])
+
+       AC_ARG_ENABLE(maemo6, AC_HELP_STRING([--enable-maemo6], [compile with maemo6 plugin]), [
+               maemo6_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(dbusoob, AC_HELP_STRING([--enable-dbusoob], [compile with D-Bus OOB plugin]), [
+               dbusoob_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(wiimote, AC_HELP_STRING([--enable-wiimote], [compile with Wii Remote plugin]), [
+               wiimote_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(hal, AC_HELP_STRING([--enable-hal], [Use HAL to determine adapter class]), [
+               hal_enable=${enableval}
+       ])
+
+       AC_ARG_ENABLE(gatt, AC_HELP_STRING([--enable-gatt], [enable gatt module]), [
+               gatt_enable=${enableval}
+       ])
+
+       misc_cflags=""
+       misc_ldflags=""
+
+       if (test "${fortify_enable}" = "yes"); then
+               misc_cflags="$misc_cflags -D_FORTIFY_SOURCE=2"
+       fi
+
+       if (test "${pie_enable}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then
+               misc_cflags="$misc_cflags -fPIC"
+               misc_ldflags="$misc_ldflags -pie"
+       fi
+
+       if (test "${debug_enable}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then
+               misc_cflags="$misc_cflags -g"
+       fi
+
+       if (test "${optimization_enable}" = "no"); then
+               misc_cflags="$misc_cflags -O0"
+       fi
+
+       AC_SUBST([MISC_CFLAGS], $misc_cflags)
+       AC_SUBST([MISC_LDFLAGS], $misc_ldflags)
+
+       if (test "${usb_enable}" = "yes" && test "${usb_found}" = "yes"); then
+               AC_DEFINE(HAVE_LIBUSB, 1, [Define to 1 if you have USB library.])
+       fi
+
+       AM_CONDITIONAL(SNDFILE, test "${sndfile_enable}" = "yes" && test "${sndfile_found}" = "yes")
+       AM_CONDITIONAL(USB, test "${usb_enable}" = "yes" && test "${usb_found}" = "yes")
+       AM_CONDITIONAL(SBC, test "${alsa_enable}" = "yes" || test "${gstreamer_enable}" = "yes" ||
+                                                                       test "${test_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(SAPPLUGIN, test "${sap_enable}" = "yes")
+       AM_CONDITIONAL(SERVICEPLUGIN, test "${service_enable}" = "yes")
+       AM_CONDITIONAL(HEALTHPLUGIN, test "${health_enable}" = "yes")
+       AM_CONDITIONAL(MCAP, test "${health_enable}" = "yes")
+       AM_CONDITIONAL(HAL, test "${hal_enable}" = "yes")
+       AM_CONDITIONAL(READLINE, test "${readline_found}" = "yes")
+       AM_CONDITIONAL(PNATPLUGIN, test "${pnat_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" && test "${check_found}" = "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" && test "${udev_found}" = "yes")
+       AM_CONDITIONAL(DFUTOOL, test "${dfutool_enable}" = "yes" && test "${usb_found}" = "yes")
+       AM_CONDITIONAL(DATAFILES, test "${datafiles_enable}" = "yes")
+       AM_CONDITIONAL(MAEMO6PLUGIN, test "${maemo6_enable}" = "yes")
+       AM_CONDITIONAL(DBUSOOBPLUGIN, test "${dbusoob_enable}" = "yes")
+       AM_CONDITIONAL(WIIMOTEPLUGIN, test "${wiimote_enable}" = "yes")
+       AM_CONDITIONAL(GATTMODULES, test "${gatt_enable}" = "yes")
+])
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644 (file)
index 0000000..7e1ecb9
--- /dev/null
@@ -0,0 +1,9903 @@
+# generated automatically by aclocal 1.11.3 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
+# Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
+[m4_warning([this file was generated for autoconf 2.69.
+You have another version of autoconf.  It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically `autoreconf'.])])
+
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+#                 Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+m4_define([_LT_COPYING], [dnl
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+#                 Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+#   This file is part of GNU Libtool.
+#
+# GNU Libtool is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+])
+
+# serial 57 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+       [m4_default([$3],
+                  [m4_fatal([Libtool version $1 or higher is required],
+                            63)])],
+       [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+  *\ * | *\    *)
+    AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
+AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}])
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.
+m4_defun([_LT_CC_BASENAME],
+[for cc_temp in $1""; do
+  case $cc_temp in
+    compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+    distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
+done
+cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+
+_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl
+dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_WITH_SYSROOT])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    _LT_PATH_MAGIC
+  fi
+  ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PREPARE_SED_QUOTE_VARS
+# --------------------------
+# Define a few sed substitution that help us do robust quoting.
+m4_defun([_LT_PREPARE_SED_QUOTE_VARS],
+[# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+])
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from `configure', and `config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool.  Notably,
+# `config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain="$ac_aux_dir/ltmain.sh"
+])# _LT_PROG_LTMAIN
+
+
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the `libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+          [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+                     [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+          [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+                     [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+              [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME.  Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+    [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+       [m4_ifval([$1], [$1], [$2])])
+    lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+    m4_ifval([$4],
+       [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+    lt_dict_add_subkey([lt_decl_dict], [$2],
+       [tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+  [0], [m4_fatal([$0: too few arguments: $#])],
+  [1], [m4_fatal([$0: too few arguments: $#: $1])],
+  [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+  [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+  [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[m4_assert([$# <= 2])dnl
+_$0(m4_quote(m4_default([$1], [[, ]])),
+    m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
+    m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
+m4_define([_lt_decl_varnames_tagged],
+[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+     m4_if([$2], [],
+          m4_quote(lt_decl_varnames),
+       m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+                       lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to `config.status' so that its
+# declaration there will have the same value as in `configure'.  VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly.  In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+#    <var>='`$ECHO "$<var>" | $SED "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+    [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
+available_tags="_LT_TAGS"dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+#    # Some comment about what VAR is for.
+#    visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+                                          [description])))[]dnl
+m4_pushdef([_libtool_name],
+    m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+    [0], [_libtool_name=[$]$1],
+    [1], [_libtool_name=$lt_[]$1],
+    [2], [_libtool_name=$lt_[]$1],
+    [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the `libtool'
+# script.  Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+    m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS.  Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into `config.status', and then the shell code to quote escape them in
+# for loops in `config.status'.  Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+       dnl If the libtool generation code has been placed in $CONFIG_LT,
+       dnl instead of duplicating it all over again into config.status,
+       dnl then we will have config.status run $CONFIG_LT later, so it
+       dnl needs to know what name is stored there:
+        [AC_CONFIG_COMMANDS([libtool],
+            [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+    dnl If the libtool generation code is destined for config.status,
+    dnl expand the accumulated commands and init code now:
+    [AC_CONFIG_COMMANDS([libtool],
+        [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$[]1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[[\\\\\\\`\\"\\\$]]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[[\\\\\\\`\\"\\\$]]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+# _LT_GENERATED_FILE_INIT(FILE, [COMMENT])
+# ------------------------------------
+# Generate a child script FILE with all initialization necessary to
+# reuse the environment learned by the parent script, and make the
+# file executable.  If COMMENT is supplied, it is inserted after the
+# `#!' sequence but before initialization text begins.  After this
+# macro, additional text can be appended to FILE to form the body of
+# the child script.  The macro ends with non-zero status if the
+# file could not be fully written (such as if the disk is full).
+m4_ifdef([AS_INIT_GENERATED],
+[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])],
+[m4_defun([_LT_GENERATED_FILE_INIT],
+[m4_require([AS_PREPARE])]dnl
+[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl
+[lt_write_fail=0
+cat >$1 <<_ASEOF || lt_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+$2
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$1 <<\_ASEOF || lt_write_fail=1
+AS_SHELL_SANITIZE
+_AS_PREPARE
+exec AS_MESSAGE_FD>&1
+_ASEOF
+test $lt_write_fail = 0 && chmod +x $1[]dnl
+m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+_LT_GENERATED_FILE_INIT(["$CONFIG_LT"],
+[# Run this file to recreate a libtool stub with the current configuration.])
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+lt_cl_silent=false
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+  echo
+  AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+\`$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+  -h, --help      print this help, then exit
+  -V, --version   print version number, then exit
+  -q, --quiet     do not print progress messages
+  -d, --debug     don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2011 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test $[#] != 0
+do
+  case $[1] in
+    --version | --v* | -V )
+      echo "$lt_cl_version"; exit 0 ;;
+    --help | --h* | -h )
+      echo "$lt_cl_help"; exit 0 ;;
+    --debug | --d* | -d )
+      debug=: ;;
+    --quiet | --q* | --silent | --s* | -q )
+      lt_cl_silent=: ;;
+
+    -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try \`$[0] --help' for more information.]) ;;
+
+    *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try \`$[0] --help' for more information.]) ;;
+  esac
+  shift
+done
+
+if $lt_cl_silent; then
+  exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure.  Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+lt_cl_success=:
+test "$silent" = yes &&
+  lt_config_lt_args="$lt_config_lt_args --quiet"
+exec AS_MESSAGE_LOG_FD>/dev/null
+$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+exec AS_MESSAGE_LOG_FD>>config.log
+$lt_cl_success || AS_EXIT(1)
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars.  Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+  m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+  m4_if(_LT_TAG, [C], [
+    # See if we are running on zsh, and set the options which allow our
+    # commands through without removal of \ escapes.
+    if test -n "${ZSH_VERSION+set}" ; then
+      setopt NO_GLOB_SUBST
+    fi
+
+    cfgfile="${ofile}T"
+    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+    $RM "$cfgfile"
+
+    cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+  case $host_os in
+  aix3*)
+    cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+_LT_EOF
+    ;;
+  esac
+
+  _LT_PROG_LTMAIN
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "$cfgfile" \
+     || (rm -f "$cfgfile"; exit 1)
+
+  _LT_PROG_REPLACE_SHELLFNS
+
+   mv -f "$cfgfile" "$ofile" ||
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+    PACKAGE='$PACKAGE'
+    VERSION='$VERSION'
+    TIMESTAMP='$TIMESTAMP'
+    RM='$RM'
+    ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+#    autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+  [C],                 [_LT_LANG(C)],
+  [C++],               [_LT_LANG(CXX)],
+  [Go],                        [_LT_LANG(GO)],
+  [Java],              [_LT_LANG(GCJ)],
+  [Fortran 77],                [_LT_LANG(F77)],
+  [Fortran],           [_LT_LANG(FC)],
+  [Windows Resource],  [_LT_LANG(RC)],
+  [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+    [_LT_LANG($1)],
+    [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+  [LT_SUPPORTED_TAG([$1])dnl
+  m4_append([_LT_TAGS], [$1 ])dnl
+  m4_define([_LT_LANG_]$1[_enabled], [])dnl
+  _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+m4_ifndef([AC_PROG_GO], [
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_GO.  When it is available in    #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+m4_defun([AC_PROG_GO],
+[AC_LANG_PUSH(Go)dnl
+AC_ARG_VAR([GOC],     [Go compiler command])dnl
+AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl
+_AC_ARG_VAR_LDFLAGS()dnl
+AC_CHECK_TOOL(GOC, gccgo)
+if test -z "$GOC"; then
+  if test -n "$ac_tool_prefix"; then
+    AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo])
+  fi
+fi
+if test -z "$GOC"; then
+  AC_CHECK_PROG(GOC, gccgo, gccgo, false)
+fi
+])#m4_defun
+])#m4_ifndef
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+  [LT_LANG(CXX)],
+  [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+  [LT_LANG(F77)],
+  [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+  [LT_LANG(FC)],
+  [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+  [LT_LANG(GCJ)],
+  [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+    [LT_LANG(GCJ)],
+    [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+      [LT_LANG(GCJ)],
+      [m4_ifdef([AC_PROG_GCJ],
+       [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+       m4_ifdef([A][M_PROG_GCJ],
+       [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+       m4_ifdef([LT_PROG_GCJ],
+       [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([AC_PROG_GO],
+  [LT_LANG(GO)],
+  [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+  [LT_LANG(RC)],
+  [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+dnl AC_DEFUN([AC_LIBTOOL_RC], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+  case $host_os in
+    rhapsody* | darwin*)
+    AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+    AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+    AC_CHECK_TOOL([LIPO], [lipo], [:])
+    AC_CHECK_TOOL([OTOOL], [otool], [:])
+    AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+    _LT_DECL([], [DSYMUTIL], [1],
+      [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+    _LT_DECL([], [NMEDIT], [1],
+      [Tool to change global to local symbols on Mac OS X])
+    _LT_DECL([], [LIPO], [1],
+      [Tool to manipulate fat objects and archives on Mac OS X])
+    _LT_DECL([], [OTOOL], [1],
+      [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+    _LT_DECL([], [OTOOL64], [1],
+      [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+    AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
+      [lt_cv_apple_cc_single_mod=no
+      if test -z "${LT_MULTI_MODULE}"; then
+       # By default we will add the -single_module flag. You can override
+       # by either setting the environment variable LT_MULTI_MODULE
+       # non-empty at configure time, or by adding -multi_module to the
+       # link flags.
+       rm -rf libconftest.dylib*
+       echo "int foo(void){return 1;}" > conftest.c
+       echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+       $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+         -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+        _lt_result=$?
+       # If there is a non-empty error log, and "single_module"
+       # appears in it, assume the flag caused a linker warning
+        if test -s conftest.err && $GREP single_module conftest.err; then
+         cat conftest.err >&AS_MESSAGE_LOG_FD
+       # Otherwise, if the output was created with a 0 exit code from
+       # the compiler, it worked.
+       elif test -f libconftest.dylib && test $_lt_result -eq 0; then
+         lt_cv_apple_cc_single_mod=yes
+       else
+         cat conftest.err >&AS_MESSAGE_LOG_FD
+       fi
+       rm -rf libconftest.dylib*
+       rm -f conftest.*
+      fi])
+
+    AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+      [lt_cv_ld_exported_symbols_list],
+      [lt_cv_ld_exported_symbols_list=no
+      save_LDFLAGS=$LDFLAGS
+      echo "_main" > conftest.sym
+      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+      AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+       [lt_cv_ld_exported_symbols_list=yes],
+       [lt_cv_ld_exported_symbols_list=no])
+       LDFLAGS="$save_LDFLAGS"
+    ])
+
+    AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load],
+      [lt_cv_ld_force_load=no
+      cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD
+      $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD
+      echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
+      $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
+      echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD
+      $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD
+      cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD
+      $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+      _lt_result=$?
+      if test -s conftest.err && $GREP force_load conftest.err; then
+       cat conftest.err >&AS_MESSAGE_LOG_FD
+      elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then
+       lt_cv_ld_force_load=yes
+      else
+       cat conftest.err >&AS_MESSAGE_LOG_FD
+      fi
+        rm -f conftest.err libconftest.a conftest conftest.c
+        rm -rf conftest.dSYM
+    ])
+    case $host_os in
+    rhapsody* | darwin1.[[012]])
+      _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+    darwin1.*)
+      _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+    darwin*) # darwin 5.x on
+      # if running on 10.5 or later, the deployment target defaults
+      # to the OS version, if on x86, and 10.4, the deployment
+      # target defaults to 10.4. Don't you love it?
+      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+       10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+         _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+       10.[[012]]*)
+         _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+       10.*)
+         _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+      esac
+    ;;
+  esac
+    if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+      _lt_dar_single_mod='$single_module'
+    fi
+    if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+      _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+    else
+      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    fi
+    if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then
+      _lt_dsymutil='~$DSYMUTIL $lib || :'
+    else
+      _lt_dsymutil=
+    fi
+    ;;
+  esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES([TAG])
+# ---------------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+  m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+  _LT_TAGVAR(hardcode_direct, $1)=no
+  _LT_TAGVAR(hardcode_automatic, $1)=yes
+  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  if test "$lt_cv_ld_force_load" = "yes"; then
+    _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+    m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes],
+                  [FC],  [_LT_TAGVAR(compiler_needs_object, $1)=yes])
+  else
+    _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+  fi
+  _LT_TAGVAR(link_all_deplibs, $1)=yes
+  _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined"
+  case $cc_basename in
+     ifort*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test "$_lt_dar_can_shared" = "yes"; then
+    output_verbose_link_cmd=func_echo_all
+    _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+    _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+    _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+    _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+    m4_if([$1], [CXX],
+[   if test "$lt_cv_apple_cc_single_mod" != "yes"; then
+      _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
+      _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
+    fi
+],[])
+  else
+  _LT_TAGVAR(ld_shlibs, $1)=no
+  fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX([TAGNAME])
+# ----------------------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+# Store the results from the different compilers for each TAGNAME.
+# Allow to override them for all tags through lt_cv_aix_libpath.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])],
+  [AC_LINK_IFELSE([AC_LANG_PROGRAM],[
+  lt_aix_libpath_sed='[
+      /Import File Strings/,/^$/ {
+         /^0/ {
+             s/^0  *\([^ ]*\) *$/\1/
+             p
+         }
+      }]'
+  _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+    _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi],[])
+  if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+    _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib"
+  fi
+  ])
+  aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])
+fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[m4_divert_text([M4SH-INIT], [$1
+])])# _LT_SHELL_INIT
+
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Find how we can fake an echo command that does not interpret backslash.
+# In particular, with Autoconf 2.60 or later we add some code to the start
+# of the generated configure script which will find a shell with a builtin
+# printf (which we can use as an echo command).
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+AC_MSG_CHECKING([how to print strings])
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+   test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='printf %s\n'
+else
+  # Use this function as a fallback that always works.
+  func_fallback_echo ()
+  {
+    eval 'cat <<_LTECHO_EOF
+$[]1
+_LTECHO_EOF'
+  }
+  ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO "$*" 
+}
+
+case "$ECHO" in
+  printf*) AC_MSG_RESULT([printf]) ;;
+  print*) AC_MSG_RESULT([print -r]) ;;
+  *) AC_MSG_RESULT([cat]) ;;
+esac
+
+m4_ifdef([_AS_DETECT_SUGGESTED],
+[_AS_DETECT_SUGGESTED([
+  test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || (
+    ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+    ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+    ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+    PATH=/empty FPATH=/empty; export PATH FPATH
+    test "X`printf %s $ECHO`" = "X$ECHO" \
+      || test "X`print -r -- $ECHO`" = "X$ECHO" )])])
+
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_WITH_SYSROOT
+# ----------------
+AC_DEFUN([_LT_WITH_SYSROOT],
+[AC_MSG_CHECKING([for sysroot])
+AC_ARG_WITH([sysroot],
+[  --with-sysroot[=DIR] Search for dependent libraries within DIR
+                        (or the compiler's sysroot if not specified).],
+[], [with_sysroot=no])
+
+dnl lt_sysroot will always be passed unquoted.  We quote it here
+dnl in case the user passed a directory name.
+lt_sysroot=
+case ${with_sysroot} in #(
+ yes)
+   if test "$GCC" = yes; then
+     lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+   fi
+   ;; #(
+ /*)
+   lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+   ;; #(
+ no|'')
+   ;; #(
+ *)
+   AC_MSG_RESULT([${with_sysroot}])
+   AC_MSG_ERROR([The sysroot must be an absolute path.])
+   ;;
+esac
+
+ AC_MSG_RESULT([${lt_sysroot:-no}])
+_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl
+[dependent libraries, and in which our libraries should be installed.])])
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+  [AS_HELP_STRING([--disable-libtool-lock],
+    [avoid locking (might break parallel builds)])])
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.$ac_objext` in
+      *ELF-32*)
+       HPUX_IA64_MODE="32"
+       ;;
+      *ELF-64*)
+       HPUX_IA64_MODE="64"
+       ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    if test "$lt_cv_prog_gnu_ld" = yes; then
+      case `/usr/bin/file conftest.$ac_objext` in
+       *32-bit*)
+         LD="${LD-ld} -melf32bsmip"
+         ;;
+       *N32*)
+         LD="${LD-ld} -melf32bmipn32"
+         ;;
+       *64-bit*)
+         LD="${LD-ld} -melf64bmip"
+       ;;
+      esac
+    else
+      case `/usr/bin/file conftest.$ac_objext` in
+       *32-bit*)
+         LD="${LD-ld} -32"
+         ;;
+       *N32*)
+         LD="${LD-ld} -n32"
+         ;;
+       *64-bit*)
+         LD="${LD-ld} -64"
+         ;;
+      esac
+    fi
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.o` in
+      *32-bit*)
+       case $host in
+         x86_64-*kfreebsd*-gnu)
+           LD="${LD-ld} -m elf_i386_fbsd"
+           ;;
+         x86_64-*linux*)
+           LD="${LD-ld} -m elf_i386"
+           ;;
+         ppc64-*linux*|powerpc64-*linux*)
+           LD="${LD-ld} -m elf32ppclinux"
+           ;;
+         s390x-*linux*)
+           LD="${LD-ld} -m elf_s390"
+           ;;
+         sparc64-*linux*)
+           LD="${LD-ld} -m elf32_sparc"
+           ;;
+       esac
+       ;;
+      *64-bit*)
+       case $host in
+         x86_64-*kfreebsd*-gnu)
+           LD="${LD-ld} -m elf_x86_64_fbsd"
+           ;;
+         x86_64-*linux*)
+           LD="${LD-ld} -m elf_x86_64"
+           ;;
+         ppc*-*linux*|powerpc*-*linux*)
+           LD="${LD-ld} -m elf64ppc"
+           ;;
+         s390*-*linux*|s390*-*tpf*)
+           LD="${LD-ld} -m elf64_s390"
+           ;;
+         sparc*-*linux*)
+           LD="${LD-ld} -m elf64_sparc"
+           ;;
+       esac
+       ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+    [AC_LANG_PUSH(C)
+     AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+     AC_LANG_POP])
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+*-*solaris*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.o` in
+    *64-bit*)
+      case $lt_cv_prog_gnu_ld in
+      yes*)
+        case $host in
+        i?86-*-solaris*)
+          LD="${LD-ld} -m elf_x86_64"
+          ;;
+        sparc*-*-solaris*)
+          LD="${LD-ld} -m elf64_sparc"
+          ;;
+        esac
+        # GNU ld 2.21 introduced _sol2 emulations.  Use them if available.
+        if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+          LD="${LD-ld}_sol2"
+        fi
+        ;;
+      *)
+       if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+         LD="${LD-ld} -64"
+       fi
+       ;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+esac
+
+need_locks="$enable_libtool_lock"
+])# _LT_ENABLE_LOCK
+
+
+# _LT_PROG_AR
+# -----------
+m4_defun([_LT_PROG_AR],
+[AC_CHECK_TOOLS(AR, [ar], false)
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive])
+
+AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file],
+  [lt_cv_ar_at_file=no
+   AC_COMPILE_IFELSE([AC_LANG_PROGRAM],
+     [echo conftest.$ac_objext > conftest.lst
+      lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD'
+      AC_TRY_EVAL([lt_ar_try])
+      if test "$ac_status" -eq 0; then
+       # Ensure the archiver fails upon bogus file names.
+       rm -f conftest.$ac_objext libconftest.a
+       AC_TRY_EVAL([lt_ar_try])
+       if test "$ac_status" -ne 0; then
+          lt_cv_ar_at_file=@
+        fi
+      fi
+      rm -f conftest.* libconftest.a
+     ])
+  ])
+
+if test "x$lt_cv_ar_at_file" = xno; then
+  archiver_list_spec=
+else
+  archiver_list_spec=$lt_cv_ar_at_file
+fi
+_LT_DECL([], [archiver_list_spec], [1],
+  [How to feed a file listing to the archiver])
+])# _LT_PROG_AR
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[_LT_PROG_AR
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+    [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+    ;;
+  *)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+  darwin*)
+    lock_old_archive_extraction=yes ;;
+  *)
+    lock_old_archive_extraction=no ;;
+esac
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+    [Commands used to build an old-style archive])
+_LT_DECL([], [lock_old_archive_extraction], [0],
+    [Whether to use a lock for old archive extraction])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#              [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$3"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       $2=yes
+     fi
+   fi
+   $RM conftest*
+])
+
+if test x"[$]$2" = xyes; then
+    m4_if([$5], , :, [$5])
+else
+    m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#                  [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $3"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&AS_MESSAGE_LOG_FD
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         $2=yes
+       fi
+     else
+       $2=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+])
+
+if test x"[$]$2" = xyes; then
+    m4_if([$4], , :, [$4])
+else
+    m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+  i=0
+  teststring="ABCD"
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw* | cegcc*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  mint*)
+    # On MiNT this can take a long time and run out of memory.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+    # This has been around since 386BSD, at least.  Likely further.
+    if test -x /sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+    elif test -x /usr/sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+    else
+      lt_cv_sys_max_cmd_len=65536      # usable default for all BSDs
+    fi
+    # And add a safety zone
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    ;;
+
+  interix*)
+    # We know the value 262144 and hardcode it with a safety zone (like BSD)
+    lt_cv_sys_max_cmd_len=196608
+    ;;
+
+  os2*)
+    # The test takes a long time on OS/2.
+    lt_cv_sys_max_cmd_len=8192
+    ;;
+
+  osf*)
+    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+    # nice to cause kernel panics so lets avoid the loop below.
+    # First set a reasonable default.
+    lt_cv_sys_max_cmd_len=16384
+    #
+    if test -x /sbin/sysconfig; then
+      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+        *1*) lt_cv_sys_max_cmd_len=-1 ;;
+      esac
+    fi
+    ;;
+  sco3.2v5*)
+    lt_cv_sys_max_cmd_len=102400
+    ;;
+  sysv5* | sco5v6* | sysv4.2uw2*)
+    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+    if test -n "$kargmax"; then
+      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[        ]]//'`
+    else
+      lt_cv_sys_max_cmd_len=32768
+    fi
+    ;;
+  *)
+    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+    if test -n "$lt_cv_sys_max_cmd_len"; then
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    else
+      # Make teststring a little bigger before we do anything with it.
+      # a 1K string should be a reasonable start.
+      for i in 1 2 3 4 5 6 7 8 ; do
+        teststring=$teststring$teststring
+      done
+      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+      # If test is not a shell built-in, we'll probably end up computing a
+      # maximum length that is only half of the actual maximum length, but
+      # we can't tell.
+      while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \
+                = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+             test $i != 17 # 1/2 MB should be enough
+      do
+        i=`expr $i + 1`
+        teststring=$teststring$teststring
+      done
+      # Only check the string length outside the loop.
+      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+      teststring=
+      # Add a significant safety factor because C++ compilers can tack on
+      # massive amounts of additional arguments before passing them to the
+      # linker.  It appears as though 1/2 is a usable value.
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    fi
+    ;;
+  esac
+])
+if test -n $lt_cv_sys_max_cmd_len ; then
+  AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+  AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+    [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+#                      ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "$cross_compiling" = yes; then :
+  [$4]
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+[#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL          RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL                DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL                0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW           RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW         DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW       RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW     DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW     0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisbility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+         if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+       }
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}]
+_LT_EOF
+  if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) $1 ;;
+      x$lt_dlneed_uscore) $2 ;;
+      x$lt_dlunknown|x*) $3 ;;
+    esac
+  else :
+    # compilation failed
+    $3
+  fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32* | cegcc*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+    ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+    ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    AC_CHECK_LIB([dl], [dlopen],
+               [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ])
+    ;;
+
+  *)
+    AC_CHECK_FUNC([shl_load],
+         [lt_cv_dlopen="shl_load"],
+      [AC_CHECK_LIB([dld], [shl_load],
+           [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
+       [AC_CHECK_FUNC([dlopen],
+             [lt_cv_dlopen="dlopen"],
+         [AC_CHECK_LIB([dl], [dlopen],
+               [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+           [AC_CHECK_LIB([svld], [dlopen],
+                 [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+             [AC_CHECK_LIB([dld], [dld_link],
+                   [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
+             ])
+           ])
+         ])
+       ])
+      ])
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    AC_CACHE_CHECK([whether a program can dlopen itself],
+         lt_cv_dlopen_self, [dnl
+         _LT_TRY_DLOPEN_SELF(
+           lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+           lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+    ])
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+      AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+         lt_cv_dlopen_self_static, [dnl
+         _LT_TRY_DLOPEN_SELF(
+           lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+           lt_cv_dlopen_self_static=no,  lt_cv_dlopen_self_static=cross)
+      ])
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+        [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+        [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+        [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+     fi
+   fi
+   chmod u+w . 2>&AS_MESSAGE_LOG_FD
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+       [Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links="nottested"
+if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  AC_MSG_CHECKING([if we can lock with hard links])
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  AC_MSG_RESULT([$hard_links])
+  if test "$hard_links" = no; then
+    AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+         [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/",
+  [Define to the sub-directory in which libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+   test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+   test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then
+
+  # We can hardcode non-existent directories.
+  if test "$_LT_TAGVAR(hardcode_direct, $1)" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
+     test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then
+    # Linking always hardcodes the temporary library directory.
+    _LT_TAGVAR(hardcode_action, $1)=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    _LT_TAGVAR(hardcode_action, $1)=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test "$_LT_TAGVAR(hardcode_action, $1)" = relink ||
+   test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+    [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+  darwin*)
+    if test -n "$STRIP" ; then
+      striplib="$STRIP -x"
+      old_striplib="$STRIP -S"
+      AC_MSG_RESULT([yes])
+    else
+      AC_MSG_RESULT([no])
+    fi
+    ;;
+  *)
+    AC_MSG_RESULT([no])
+    ;;
+  esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_OBJDUMP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+       [], [
+if test "$GCC" = yes; then
+  case $host_os in
+    darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+    *) lt_awk_arg="/^libraries:/" ;;
+  esac
+  case $host_os in
+    mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;;
+    *) lt_sed_strip_eq="s,=/,/,g" ;;
+  esac
+  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+  case $lt_search_path_spec in
+  *\;*)
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+    ;;
+  *)
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+    ;;
+  esac
+  # Ok, now we have the path, separated by spaces, we can step through it
+  # and add multilib dir if necessary.
+  lt_tmp_lt_search_path_spec=
+  lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+  for lt_sys_path in $lt_search_path_spec; do
+    if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+    else
+      test -d "$lt_sys_path" && \
+       lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+    fi
+  done
+  lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+  lt_foo="";
+  lt_count=0;
+  for (lt_i = NF; lt_i > 0; lt_i--) {
+    if ($lt_i != "" && $lt_i != ".") {
+      if ($lt_i == "..") {
+        lt_count++;
+      } else {
+        if (lt_count == 0) {
+          lt_foo="/" $lt_i lt_foo;
+        } else {
+          lt_count--;
+        }
+      }
+    }
+  }
+  if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+  if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+  # AWK program above erroneously prepends '/' to C:/dos/paths
+  # for these hosts.
+  case $host_os in
+    mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+      $SED 's,/\([[A-Za-z]]:\),\1,g'` ;;
+  esac
+  sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix[[4-9]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[[01]] | aix4.[[01]].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+          echo ' yes '
+          echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+       :
+      else
+       can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[[45]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$cc_basename in
+  yes,*)
+    # gcc
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+m4_if([$1], [],[
+      sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"])
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    dynamic_linker='Win32 ld.exe'
+    ;;
+
+  *,cl*)
+    # Native MSVC
+    libname_spec='$name'
+    soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+    library_names_spec='${libname}.dll.lib'
+
+    case $build_os in
+    mingw*)
+      sys_lib_search_path_spec=
+      lt_save_ifs=$IFS
+      IFS=';'
+      for lt_path in $LIB
+      do
+        IFS=$lt_save_ifs
+        # Let DOS variable expansion print the short 8.3 style file name.
+        lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+        sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+      done
+      IFS=$lt_save_ifs
+      # Convert to MSYS style.
+      sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'`
+      ;;
+    cygwin*)
+      # Convert to unix form, then to dos form, then back to unix form
+      # but this time dos style (no spaces!) so that the unix form looks
+      # like /cygdrive/c/PROGRA~1:/cygdr...
+      sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+      sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+      sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      ;;
+    *)
+      sys_lib_search_path_spec="$LIB"
+      if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
+        # It is most probably a Windows format PATH.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      # FIXME: find the short name or the path components, as spaces are
+      # common. (e.g. "Program Files" -> "PROGRA~1")
+      ;;
+    esac
+
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+    dynamic_linker='Win32 link.exe'
+    ;;
+
+  *)
+    # Assume MSVC wrapper
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    dynamic_linker='Win32 ld.exe'
+    ;;
+  esac
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd* | dragonfly*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[[23]].*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2.*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+  freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+haiku*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  dynamic_linker="$host_os runtime_loader"
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+  postinstall_cmds='chmod 555 $lib'
+  # or fails outright, so override atomically:
+  install_override_mode=555
+  ;;
+
+interix[[3-9]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+       if test "$lt_cv_prog_gnu_ld" = yes; then
+               version_type=linux # correct to gnu/linux during the next big refactor
+       else
+               version_type=irix
+       fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+
+  # Some binutils ld are patched to set DT_RUNPATH
+  AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath],
+    [lt_cv_shlibpath_overrides_runpath=no
+    save_LDFLAGS=$LDFLAGS
+    save_libdir=$libdir
+    eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+        LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+      [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
+        [lt_cv_shlibpath_overrides_runpath=yes])])
+    LDFLAGS=$save_LDFLAGS
+    libdir=$save_libdir
+    ])
+  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[  ]*hwcap[        ]/d;s/[:,      ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec="/usr/lib"
+  need_lib_prefix=no
+  # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+  case $host_os in
+    openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+    *)                         need_version=no  ;;
+  esac
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[[89]] | openbsd2.[[89]].*)
+       shlibpath_overrides_runpath=no
+       ;;
+      *)
+       shlibpath_overrides_runpath=yes
+       ;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux # correct to gnu/linux during the next big refactor
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=freebsd-elf
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test "$with_gnu_ld" = yes; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+       ;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+  sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+  sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+    [Variables whose values should be saved in libtool wrapper scripts and
+    restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+    [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0],  [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+    [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+    [[List of archive names.  First name is the real one, the rest are links.
+    The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+    [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [install_override_mode], [1],
+    [Permission mode override for installation of shared libraries])
+_LT_DECL([], [postinstall_cmds], [2],
+    [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+    [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+    [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+    [[As "finish_cmds", except a single script fragment to be evaled but
+    not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+    [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+    [Compile-time system search path for libraries])
+_LT_DECL([], [sys_lib_dlsearch_path_spec], [2],
+    [Run-time system search path for libraries])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program which can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] |  ?:[\\/]*])
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word.  This closes a longstanding sh security hole.
+  ac_dummy="m4_if([$2], , $PATH, [$2])"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$1; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+      if test -n "$file_magic_test_file"; then
+       case $deplibs_check_method in
+       "file_magic "*)
+         file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+         MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+         if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+           $EGREP "$file_magic_regex" > /dev/null; then
+           :
+         else
+           cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+         fi ;;
+       esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  AC_MSG_RESULT($MAGIC_CMD)
+else
+  AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+        [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program which can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+  else
+    MAGIC_CMD=:
+  fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PROG_ECHO_BACKSLASH])dnl
+
+AC_ARG_WITH([gnu-ld],
+    [AS_HELP_STRING([--with-gnu-ld],
+       [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+    [test "$withval" = no || with_gnu_ld=yes],
+    [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by $CC])
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [[\\/]]* | ?:[[\\/]]*)
+      re_direlt='/[[^/]][[^/]]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+       ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+       test "$with_gnu_ld" != no && break
+       ;;
+      *)
+       test "$with_gnu_ld" != yes && break
+       ;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  AC_MSG_RESULT($LD)
+else
+  AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+#   -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+  lt_cv_ld_reload_flag,
+  [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    if test "$GCC" != yes; then
+      reload_cmds=false
+    fi
+    ;;
+  darwin*)
+    if test "$GCC" = yes; then
+      reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+    else
+      reload_cmds='$LD$reload_flag -o $output$reload_objs'
+    fi
+    ;;
+esac
+_LT_TAGDECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_TAGDECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+#  -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi[[45]]*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump',
+  # unless we find 'file', for example because we are cross-compiling.
+  # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin.
+  if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then
+    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+    lt_cv_file_magic_cmd='func_win32_libid'
+  else
+    # Keep this pattern in sync with the one in func_win32_libid.
+    lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+    lt_cv_file_magic_cmd='$OBJDUMP -f'
+  fi
+  ;;
+
+cegcc*)
+  # use the weaker test based on 'objdump'. See mingw*.
+  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | dragonfly*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+haiku*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case $host_cpu in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]']
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+interix[[3-9]]*)
+  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+  lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd* | netbsdelf*-gnu)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+*nto* | *qnx*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+openbsd*)
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+rdos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.3*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  pc)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+tpf*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+])
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+  case $host_os in
+  mingw* | pw32*)
+    if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+      want_nocaseglob=yes
+    else
+      file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"`
+    fi
+    ;;
+  esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+    [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+    [Command to use when deplibs_check_method = "file_magic"])
+_LT_DECL([], [file_magic_glob], [1],
+    [How to find potential files when deplibs_check_method = "file_magic"])
+_LT_DECL([], [want_nocaseglob], [1],
+    [Find potential files using nocaseglob when deplibs_check_method = "file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  lt_nm_to_check="${ac_tool_prefix}nm"
+  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+    lt_nm_to_check="$lt_nm_to_check nm"
+  fi
+  for lt_tmp_nm in $lt_nm_to_check; do
+    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+      IFS="$lt_save_ifs"
+      test -z "$ac_dir" && ac_dir=.
+      tmp_nm="$ac_dir/$lt_tmp_nm"
+      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+       # Check to see if the nm accepts a BSD-compat flag.
+       # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+       #   nm: unknown option "B" ignored
+       # Tru64's nm complains that /dev/null is an invalid object file
+       case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+       */dev/null* | *'Invalid file or object type'*)
+         lt_cv_path_NM="$tmp_nm -B"
+         break
+         ;;
+       *)
+         case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+         */dev/null*)
+           lt_cv_path_NM="$tmp_nm -p"
+           break
+           ;;
+         *)
+           lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+           continue # so that we can try to find one that supports BSD flags
+           ;;
+         esac
+         ;;
+       esac
+      fi
+    done
+    IFS="$lt_save_ifs"
+  done
+  : ${lt_cv_path_NM=no}
+fi])
+if test "$lt_cv_path_NM" != "no"; then
+  NM="$lt_cv_path_NM"
+else
+  # Didn't find any BSD compatible name lister, look for dumpbin.
+  if test -n "$DUMPBIN"; then :
+    # Let the user override the test.
+  else
+    AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :)
+    case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in
+    *COFF*)
+      DUMPBIN="$DUMPBIN -symbols"
+      ;;
+    *)
+      DUMPBIN=:
+      ;;
+    esac
+  fi
+  AC_SUBST([DUMPBIN])
+  if test "$DUMPBIN" != ":"; then
+    NM="$DUMPBIN"
+  fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+  [lt_cv_nm_interface="BSD nm"
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+  (eval "$ac_compile" 2>conftest.err)
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD)
+  cat conftest.out >&AS_MESSAGE_LOG_FD
+  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+    lt_cv_nm_interface="MS dumpbin"
+  fi
+  rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+# --------------------------------
+# how to determine the name of the shared library
+# associated with a specific link library.
+#  -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+m4_require([_LT_DECL_DLLTOOL])
+AC_CACHE_CHECK([how to associate runtime and link libraries],
+lt_cv_sharedlib_from_linklib_cmd,
+[lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+  # two different shell functions defined in ltmain.sh
+  # decide which to use based on capabilities of $DLLTOOL
+  case `$DLLTOOL --help 2>&1` in
+  *--identify-strict*)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+    ;;
+  *)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+    ;;
+  esac
+  ;;
+*)
+  # fallback: assume linklib IS sharedlib
+  lt_cv_sharedlib_from_linklib_cmd="$ECHO"
+  ;;
+esac
+])
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+_LT_DECL([], [sharedlib_from_linklib_cmd], [1],
+    [Command to associate shared and link libraries])
+])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+
+
+# _LT_PATH_MANIFEST_TOOL
+# ----------------------
+# locate the manifest tool
+m4_defun([_LT_PATH_MANIFEST_TOOL],
+[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :)
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool],
+  [lt_cv_path_mainfest_tool=no
+  echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD
+  $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+    lt_cv_path_mainfest_tool=yes
+  fi
+  rm -f conftest*])
+if test "x$lt_cv_path_mainfest_tool" != xyes; then
+  MANIFEST_TOOL=:
+fi
+_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl
+])# _LT_PATH_MANIFEST_TOOL
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*)
+  # These system don't have libm, or don't need it
+  ;;
+*-ncr-sysv4.3*)
+  AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+  AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+  ;;
+*)
+  AC_CHECK_LIB(m, cos, LIBM="-lm")
+  ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test "$GCC" = yes; then
+  case $cc_basename in
+  nvcc*)
+    _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;;
+  *)
+    _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;;
+  esac
+
+  _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+    lt_cv_prog_compiler_rtti_exceptions,
+    [-fno-rtti -fno-exceptions], [],
+    [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+       [Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[[BCDT]]'
+  ;;
+cygwin* | mingw* | pw32* | cegcc*)
+  symcode='[[ABCDGISTW]]'
+  ;;
+hpux*)
+  if test "$host_cpu" = ia64; then
+    symcode='[[ABCDEGRST]]'
+  fi
+  ;;
+irix* | nonstopux*)
+  symcode='[[BCDEGRST]]'
+  ;;
+osf*)
+  symcode='[[BCDEGQRST]]'
+  ;;
+solaris*)
+  symcode='[[BDRT]]'
+  ;;
+sco3.2v5*)
+  symcode='[[DT]]'
+  ;;
+sysv4.2uw2*)
+  symcode='[[DT]]'
+  ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+  symcode='[[ABDT]]'
+  ;;
+sysv4)
+  symcode='[[DFNSTU]]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/  {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/  {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+  symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+  # Write the raw and C identifiers.
+  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+    # Fake it for dumpbin and say T for any non-static function
+    # and D for any global variable.
+    # Also find C++ and __fastcall symbols from MSVC++,
+    # which start with @ or ?.
+    lt_cv_sys_global_symbol_pipe="$AWK ['"\
+"     {last_section=section; section=\$ 3};"\
+"     /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+"     \$ 0!~/External *\|/{next};"\
+"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+"     {if(hide[section]) next};"\
+"     {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+"     {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+"     s[1]~/^[@?]/{print s[1], s[1]; next};"\
+"     s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+"     ' prfx=^$ac_symprfx]"
+  else
+    lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[    ]]\($symcode$symcode*\)[[       ]][[    ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+  fi
+  lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+  if AC_TRY_EVAL(ac_compile); then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+       mv -f "$nlist"T "$nlist"
+      else
+       rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+       if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+         cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+/* DATA imports from DLLs on WIN32 con't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT@&t@_DLSYM_CONST
+#elif defined(__osf__)
+/* This system does not cope well with relocations in const data.  */
+# define LT@&t@_DLSYM_CONST
+#else
+# define LT@&t@_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+         # Now generate the symbol file.
+         eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+         cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols.  */
+LT@&t@_DLSYM_CONST struct {
+  const char *name;
+  void       *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+  { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+         $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+         cat <<\_LT_EOF >> conftest.$ac_ext
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+         # Now try linking the two files.
+         mv conftest.$ac_objext conftstm.$ac_objext
+         lt_globsym_save_LIBS=$LIBS
+         lt_globsym_save_CFLAGS=$CFLAGS
+         LIBS="conftstm.$ac_objext"
+         CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+         if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
+           pipe_works=yes
+         fi
+         LIBS=$lt_globsym_save_LIBS
+         CFLAGS=$lt_globsym_save_CFLAGS
+       else
+         echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+       fi
+      else
+       echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+    fi
+  else
+    echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+    cat conftest.$ac_ext >&5
+  fi
+  rm -rf conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  AC_MSG_RESULT(failed)
+else
+  AC_MSG_RESULT(ok)
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+  nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then
+  nm_file_list_spec='@'
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+    [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+    [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_c_name_address],
+    [lt_cv_sys_global_symbol_to_c_name_address], [1],
+    [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+    [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+    [Transform the output of nm in a C name address pair when lib prefix is needed])
+_LT_DECL([], [nm_file_list_spec], [1],
+    [Specify filename containing input files for $NM])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+m4_if([$1], [CXX], [
+  # C++ specific cases for pic, static, wl, etc.
+  if test "$GXX" = yes; then
+    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+    aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+       # AIX 5 now supports IA64 processor
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+    mingw* | cygwin* | os2* | pw32* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      m4_if([$1], [GCJ], [],
+       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+    *djgpp*)
+      # DJGPP does not support shared libraries at all
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+      ;;
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)=
+      ;;
+    interix[[3-9]]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       ;;
+      esac
+      ;;
+    *qnx* | *nto*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+    *)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+  else
+    case $host_os in
+      aix[[4-9]]*)
+       # All AIX code is PIC.
+       if test "$host_cpu" = ia64; then
+         # AIX 5 now supports IA64 processor
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+       else
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+       fi
+       ;;
+      chorus*)
+       case $cc_basename in
+       cxch68*)
+         # Green Hills C++ Compiler
+         # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+         ;;
+       esac
+       ;;
+      mingw* | cygwin* | os2* | pw32* | cegcc*)
+       # This hack is so that the source file can tell whether it is being
+       # built for inclusion in a dll (and should export symbols for example).
+       m4_if([$1], [GCJ], [],
+         [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+       ;;
+      dgux*)
+       case $cc_basename in
+         ec++*)
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           ;;
+         ghcx*)
+           # Green Hills C++ Compiler
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      freebsd* | dragonfly*)
+       # FreeBSD uses GNU C++
+       ;;
+      hpux9* | hpux10* | hpux11*)
+       case $cc_basename in
+         CC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+           if test "$host_cpu" != ia64; then
+             _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+           fi
+           ;;
+         aCC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+           case $host_cpu in
+           hppa*64*|ia64*)
+             # +Z the default
+             ;;
+           *)
+             _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+             ;;
+           esac
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      interix*)
+       # This is c89, which is MS Visual C++ (no shared libs)
+       # Anyone wants to do a port?
+       ;;
+      irix5* | irix6* | nonstopux*)
+       case $cc_basename in
+         CC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+           # CC pic flag -KPIC is the default.
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      linux* | k*bsd*-gnu | kopensolaris*-gnu)
+       case $cc_basename in
+         KCC*)
+           # KAI C++ Compiler
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+           ;;
+         ecpc* )
+           # old Intel C++ for x86_64 which still supported -KPIC.
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+           ;;
+         icpc* )
+           # Intel C++, used to be incompatible with GCC.
+           # ICC 10 doesn't accept -KPIC any more.
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+           ;;
+         pgCC* | pgcpp*)
+           # Portland Group C++ compiler
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           ;;
+         cxx*)
+           # Compaq C++
+           # Make sure the PIC flag is empty.  It appears that all Alpha
+           # Linux and Compaq Tru64 Unix objects are PIC.
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+           ;;
+         xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*)
+           # IBM XL 8.0, 9.0 on PPC and BlueGene
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+           ;;
+         *)
+           case `$CC -V 2>&1 | sed 5q` in
+           *Sun\ C*)
+             # Sun C++ 5.9
+             _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+             _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+             _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+             ;;
+           esac
+           ;;
+       esac
+       ;;
+      lynxos*)
+       ;;
+      m88k*)
+       ;;
+      mvs*)
+       case $cc_basename in
+         cxx*)
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      netbsd* | netbsdelf*-gnu)
+       ;;
+      *qnx* | *nto*)
+        # QNX uses GNU C++, but need to define -shared option too, otherwise
+        # it will coredump.
+        _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+        ;;
+      osf3* | osf4* | osf5*)
+       case $cc_basename in
+         KCC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+           ;;
+         RCC*)
+           # Rational C++ 2.4.1
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           ;;
+         cxx*)
+           # Digital/Compaq C++
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           # Make sure the PIC flag is empty.  It appears that all Alpha
+           # Linux and Compaq Tru64 Unix objects are PIC.
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      psos*)
+       ;;
+      solaris*)
+       case $cc_basename in
+         CC* | sunCC*)
+           # Sun C++ 4.2, 5.x and Centerline C++
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+           ;;
+         gcx*)
+           # Green Hills C++ Compiler
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      sunos4*)
+       case $cc_basename in
+         CC*)
+           # Sun C++ 4.x
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           ;;
+         lcc*)
+           # Lucid
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+       case $cc_basename in
+         CC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           ;;
+       esac
+       ;;
+      tandem*)
+       case $cc_basename in
+         NCC*)
+           # NonStop-UX NCC 3.20
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      vxworks*)
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+       ;;
+    esac
+  fi
+],
+[
+  if test "$GCC" = yes; then
+    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+       # AIX 5 now supports IA64 processor
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      m4_if([$1], [GCJ], [],
+       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)=
+      ;;
+
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+       # +Z the default
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       ;;
+      esac
+      ;;
+
+    interix[[3-9]]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      enable_shared=no
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+
+    *)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+
+    case $cc_basename in
+    nvcc*) # Cuda Compiler Driver 2.2
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker '
+      if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+        _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)"
+      fi
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      if test "$host_cpu" = ia64; then
+       # AIX 5 now supports IA64 processor
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      else
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      m4_if([$1], [GCJ], [],
+       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case $host_cpu in
+      hppa*64*|ia64*)
+       # +Z the default
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+       ;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC (with -KPIC) is the default.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    linux* | k*bsd*-gnu | kopensolaris*-gnu)
+      case $cc_basename in
+      # old Intel for x86_64 which still supported -KPIC.
+      ecc*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      # icc used to be incompatible with GCC.
+      # ICC 10 doesn't accept -KPIC any more.
+      icc* | ifort*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      # Lahey Fortran 8.1.
+      lf95*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
+       ;;
+      nagfor*)
+       # NAG Fortran compiler
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+       ;;
+      pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+        # Portland Group compilers (*not* the Pentium gcc compiler,
+       # which looks to be a dead project)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+        ;;
+      ccc*)
+        _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+        # All Alpha code is PIC.
+        _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+        ;;
+      xl* | bgxl* | bgf* | mpixl*)
+       # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+       ;;
+      *)
+       case `$CC -V 2>&1 | sed 5q` in
+       *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*)
+         # Sun Fortran 8.3 passes all unrecognized flags to the linker
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+         ;;
+       *Sun\ F* | *Sun*Fortran*)
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+         ;;
+       *Sun\ C*)
+         # Sun C 5.9
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+         ;;
+        *Intel*\ [[CF]]*Compiler*)
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+         ;;
+       *Portland\ Group*)
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+         ;;
+       esac
+       ;;
+      esac
+      ;;
+
+    newsos6)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # All OSF/1 code is PIC.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    rdos*)
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    solaris*)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      case $cc_basename in
+      f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+      esac
+      ;;
+
+    sunos4*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    unicos*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+
+    uts4*)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *)
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+    esac
+  fi
+])
+case $host_os in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+    ;;
+  *)
+    _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+    ;;
+esac
+
+AC_CACHE_CHECK([for $compiler option to produce PIC],
+  [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)],
+  [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+  _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
+    [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+    [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+    [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+     "" | " "*) ;;
+     *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+     esac],
+    [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+     _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+       [Additional compiler flags for building library objects])
+
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+       [How to pass a linker flag through the compiler])
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+  _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+  $lt_tmp_static_flag,
+  [],
+  [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+       [Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+  case $host_os in
+  aix[[4-9]]*)
+    # If we're using GNU nm, then we don't want the "-C" option.
+    # -C means demangle to AIX nm, but means don't demangle with GNU nm
+    # Also, AIX nm treats weak defined symbols like other global defined
+    # symbols, whereas GNU nm marks them as "W".
+    if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+    else
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+    fi
+    ;;
+  pw32*)
+    _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
+    ;;
+  cygwin* | mingw* | cegcc*)
+    case $cc_basename in
+    cl*)
+      _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+      ;;
+    *)
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+      _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+      ;;
+    esac
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    _LT_TAGVAR(link_all_deplibs, $1)=no
+    ;;
+  *)
+    _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+    ;;
+  esac
+], [
+  runpath_var=
+  _LT_TAGVAR(allow_undefined_flag, $1)=
+  _LT_TAGVAR(always_export_symbols, $1)=no
+  _LT_TAGVAR(archive_cmds, $1)=
+  _LT_TAGVAR(archive_expsym_cmds, $1)=
+  _LT_TAGVAR(compiler_needs_object, $1)=no
+  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+  _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  _LT_TAGVAR(hardcode_automatic, $1)=no
+  _LT_TAGVAR(hardcode_direct, $1)=no
+  _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+  _LT_TAGVAR(hardcode_libdir_separator, $1)=
+  _LT_TAGVAR(hardcode_minus_L, $1)=no
+  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  _LT_TAGVAR(inherit_rpath, $1)=no
+  _LT_TAGVAR(link_all_deplibs, $1)=unknown
+  _LT_TAGVAR(module_cmds, $1)=
+  _LT_TAGVAR(module_expsym_cmds, $1)=
+  _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+  _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+  _LT_TAGVAR(thread_safe_flag_spec, $1)=
+  _LT_TAGVAR(whole_archive_flag_spec, $1)=
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  _LT_TAGVAR(include_expsyms, $1)=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    _LT_TAGVAR(link_all_deplibs, $1)=no
+    ;;
+  esac
+
+  _LT_TAGVAR(ld_shlibs, $1)=yes
+
+  # On some targets, GNU ld is compatible enough with the native linker
+  # that we're better off using the native interface for both.
+  lt_use_gnu_ld_interface=no
+  if test "$with_gnu_ld" = yes; then
+    case $host_os in
+      aix*)
+       # The AIX port of GNU ld has always aspired to compatibility
+       # with the native linker.  However, as the warning in the GNU ld
+       # block says, versions before 2.19.5* couldn't really create working
+       # shared libraries, regardless of the interface used.
+       case `$LD -v 2>&1` in
+         *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+         *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;;
+         *\ \(GNU\ Binutils\)\ [[3-9]]*) ;;
+         *)
+           lt_use_gnu_ld_interface=yes
+           ;;
+       esac
+       ;;
+      *)
+       lt_use_gnu_ld_interface=yes
+       ;;
+    esac
+  fi
+
+  if test "$lt_use_gnu_ld_interface" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # Set some defaults for GNU ld with shared library support. These
+    # are reset later if shared libraries are not supported. Putting them
+    # here allows them to be overridden if necessary.
+    runpath_var=LD_RUN_PATH
+    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+    _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+      _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+    else
+      _LT_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+    supports_anon_versioning=no
+    case `$LD -v 2>&1` in
+      *GNU\ gold*) supports_anon_versioning=yes ;;
+      *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+      *\ 2.11.*) ;; # other 2.11 versions
+      *) supports_anon_versioning=yes ;;
+    esac
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix[[3-9]]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            _LT_TAGVAR(archive_expsym_cmds, $1)=''
+        ;;
+      m68k)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes
+        ;;
+      esac
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+       # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+       # support --undefined.  This deserves some investigation.  FIXME
+       _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+      # as there is no search path for DLLs.
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols'
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(always_export_symbols, $1)=no
+      _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+      _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+
+      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+       # If the export-symbols file already is a .def file (1st line
+       # is EXPORTS), use it as is; otherwise, prepend...
+       _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+         cp $export_symbols $output_objdir/$soname.def;
+       else
+         echo EXPORTS > $output_objdir/$soname.def;
+         cat $export_symbols >> $output_objdir/$soname.def;
+       fi~
+       $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    haiku*)
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    interix[[3-9]]*)
+      _LT_TAGVAR(hardcode_direct, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+      # Instead, shared libraries are loaded at an image base (0x10000000 by
+      # default) and relocated if they conflict, which is a slow very memory
+      # consuming and fragmenting process.  To avoid this, we pick a random,
+      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      ;;
+
+    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+      tmp_diet=no
+      if test "$host_os" = linux-dietlibc; then
+       case $cc_basename in
+         diet\ *) tmp_diet=yes;;       # linux-dietlibc with static linking (!diet-dyn)
+       esac
+      fi
+      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+        && test "$tmp_diet" = no
+      then
+       tmp_addflag=' $pic_flag'
+       tmp_sharedflag='-shared'
+       case $cc_basename,$host_cpu in
+        pgcc*)                         # Portland Group C compiler
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+         tmp_addflag=' $pic_flag'
+         ;;
+       pgf77* | pgf90* | pgf95* | pgfortran*)
+                                       # Portland Group f77 and f90 compilers
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+         tmp_addflag=' $pic_flag -Mnomain' ;;
+       ecc*,ia64* | icc*,ia64*)        # Intel C compiler on ia64
+         tmp_addflag=' -i_dynamic' ;;
+       efc*,ia64* | ifort*,ia64*)      # Intel Fortran compiler on ia64
+         tmp_addflag=' -i_dynamic -nofor_main' ;;
+       ifc* | ifort*)                  # Intel Fortran compiler
+         tmp_addflag=' -nofor_main' ;;
+       lf95*)                          # Lahey Fortran 8.1
+         _LT_TAGVAR(whole_archive_flag_spec, $1)=
+         tmp_sharedflag='--shared' ;;
+       xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+         tmp_sharedflag='-qmkshrobj'
+         tmp_addflag= ;;
+       nvcc*)  # Cuda Compiler Driver 2.2
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+         _LT_TAGVAR(compiler_needs_object, $1)=yes
+         ;;
+       esac
+       case `$CC -V 2>&1 | sed 5q` in
+       *Sun\ C*)                       # Sun C 5.9
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+         _LT_TAGVAR(compiler_needs_object, $1)=yes
+         tmp_sharedflag='-G' ;;
+       *Sun\ F*)                       # Sun Fortran 8.3
+         tmp_sharedflag='-G' ;;
+       esac
+       _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+        if test "x$supports_anon_versioning" = xyes; then
+          _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+           cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+           echo "local: *; };" >> $output_objdir/$libname.ver~
+           $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+        fi
+
+       case $cc_basename in
+       xlf* | bgf* | bgxlf* | mpixlf*)
+         # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+         _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+         if test "x$supports_anon_versioning" = xyes; then
+           _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+             cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+             echo "local: *; };" >> $output_objdir/$libname.ver~
+             $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+         fi
+         ;;
+       esac
+      else
+        _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+       wlarc=
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris*)
+      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+       ;;
+       *)
+         # For security reasons, it is highly recommended that you always
+         # use absolute paths for naming shared libraries, and exclude the
+         # DT_RUNPATH tag from executables and libraries.  But doing so
+         # requires that you compile everything twice, which is a pain.
+         if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+         else
+           _LT_TAGVAR(ld_shlibs, $1)=no
+         fi
+       ;;
+      esac
+      ;;
+
+    sunos4*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+    esac
+
+    if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then
+      runpath_var=
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+      _LT_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(always_export_symbols, $1)=yes
+      _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+       # Neither direct hardcoding nor static linking is supported with a
+       # broken collect2.
+       _LT_TAGVAR(hardcode_direct, $1)=unsupported
+      fi
+      ;;
+
+    aix[[4-9]]*)
+      if test "$host_cpu" = ia64; then
+       # On IA64, the linker does run time linking by default, so we don't
+       # have to do anything special.
+       aix_use_runtimelinking=no
+       exp_sym_flag='-Bexport'
+       no_entry_flag=""
+      else
+       # If we're using GNU nm, then we don't want the "-C" option.
+       # -C means demangle to AIX nm, but means don't demangle with GNU nm
+       # Also, AIX nm treats weak defined symbols like other global
+       # defined symbols, whereas GNU nm marks them as "W".
+       if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+         _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+       else
+         _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+       fi
+       aix_use_runtimelinking=no
+
+       # Test if we are trying to use run time linking or normal
+       # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+       # need to do runtime linking.
+       case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+         for ld_flag in $LDFLAGS; do
+         if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+           aix_use_runtimelinking=yes
+           break
+         fi
+         done
+         ;;
+       esac
+
+       exp_sym_flag='-bexport'
+       no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      _LT_TAGVAR(archive_cmds, $1)=''
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+      if test "$GCC" = yes; then
+       case $host_os in aix4.[[012]]|aix4.[[012]].*)
+       # We only want to do this on AIX 4.2 and lower, the check
+       # below for broken collect2 doesn't work under 4.3+
+         collect2name=`${CC} -print-prog-name=collect2`
+         if test -f "$collect2name" &&
+          strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+         then
+         # We have reworked collect2
+         :
+         else
+         # We have old collect2
+         _LT_TAGVAR(hardcode_direct, $1)=unsupported
+         # It fails to find uninstalled libraries when the uninstalled
+         # path is not listed in the libpath.  Setting hardcode_minus_L
+         # to unsupported forces relinking
+         _LT_TAGVAR(hardcode_minus_L, $1)=yes
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+         _LT_TAGVAR(hardcode_libdir_separator, $1)=
+         fi
+         ;;
+       esac
+       shared_flag='-shared'
+       if test "$aix_use_runtimelinking" = yes; then
+         shared_flag="$shared_flag "'${wl}-G'
+       fi
+       _LT_TAGVAR(link_all_deplibs, $1)=no
+      else
+       # not using gcc
+       if test "$host_cpu" = ia64; then
+       # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+       # chokes on -Wl,-G. The following line is correct:
+         shared_flag='-G'
+       else
+         if test "$aix_use_runtimelinking" = yes; then
+           shared_flag='${wl}-G'
+         else
+           shared_flag='${wl}-bM:SRE'
+         fi
+       fi
+      fi
+
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      _LT_TAGVAR(always_export_symbols, $1)=yes
+      if test "$aix_use_runtimelinking" = yes; then
+       # Warning - without using the other runtime loading flags (-brtl),
+       # -berok will link without error, but may produce a broken library.
+       _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        _LT_SYS_MODULE_PATH_AIX([$1])
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+      else
+       if test "$host_cpu" = ia64; then
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+         _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+         _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+       else
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        _LT_SYS_MODULE_PATH_AIX([$1])
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+         # Warning - without using the other run time loading flags,
+         # -berok will link without error, but may produce a broken library.
+         _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+         _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+         if test "$with_gnu_ld" = yes; then
+           # We only use this code for GNU lds that support --whole-archive.
+           _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+         else
+           # Exported symbols can be pulled into shared objects from archives
+           _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+         fi
+         _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+         # This is similar to how AIX traditionally builds its shared libraries.
+         _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+       fi
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            _LT_TAGVAR(archive_expsym_cmds, $1)=''
+        ;;
+      m68k)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes
+        ;;
+      esac
+      ;;
+
+    bsdi[[45]]*)
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      case $cc_basename in
+      cl*)
+       # Native MSVC
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+       _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+       _LT_TAGVAR(always_export_symbols, $1)=yes
+       _LT_TAGVAR(file_list_spec, $1)='@'
+       # Tell ltmain to make .lib files, not .a files.
+       libext=lib
+       # Tell ltmain to make .dll files, not .so files.
+       shrext_cmds=".dll"
+       # FIXME: Setting linknames here is a bad hack.
+       _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
+       _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+           sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
+         else
+           sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
+         fi~
+         $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+         linknames='
+       # The linker will not automatically build a static lib if we build a DLL.
+       # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+       _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+       _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+       _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
+       # Don't use ranlib
+       _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+       _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+         lt_tool_outputfile="@TOOL_OUTPUT@"~
+         case $lt_outputfile in
+           *.exe|*.EXE) ;;
+           *)
+             lt_outputfile="$lt_outputfile.exe"
+             lt_tool_outputfile="$lt_tool_outputfile.exe"
+             ;;
+         esac~
+         if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
+           $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+           $RM "$lt_outputfile.manifest";
+         fi'
+       ;;
+      *)
+       # Assume MSVC wrapper
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+       _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+       # Tell ltmain to make .lib files, not .a files.
+       libext=lib
+       # Tell ltmain to make .dll files, not .so files.
+       shrext_cmds=".dll"
+       # FIXME: Setting linknames here is a bad hack.
+       _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+       # The linker will automatically build a .lib file if we build a DLL.
+       _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+       # FIXME: Should let the user specify the lib program.
+       _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+       _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+       ;;
+      esac
+      ;;
+
+    darwin* | rhapsody*)
+      _LT_DARWIN_LINKER_FEATURES($1)
+      ;;
+
+    dgux*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2.*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | dragonfly*)
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+      ;;
+
+    hpux10*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      if test "$with_gnu_ld" = no; then
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+       _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+       _LT_TAGVAR(hardcode_direct, $1)=yes
+       _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+       # hardcode_minus_L: Not really in the search PATH,
+       # but as the default location of the library.
+       _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      fi
+      ;;
+
+    hpux11*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+       case $host_cpu in
+       hppa*64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       ia64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       esac
+      else
+       case $host_cpu in
+       hppa*64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       ia64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+       m4_if($1, [], [
+         # Older versions of the 11.00 compiler do not understand -b yet
+         # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+         _LT_LINKER_OPTION([if $CC understands -b],
+           _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b],
+           [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'],
+           [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])],
+         [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'])
+         ;;
+       esac
+      fi
+      if test "$with_gnu_ld" = no; then
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+       _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+       case $host_cpu in
+       hppa*64*|ia64*)
+         _LT_TAGVAR(hardcode_direct, $1)=no
+         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+         ;;
+       *)
+         _LT_TAGVAR(hardcode_direct, $1)=yes
+         _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+         _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+
+         # hardcode_minus_L: Not really in the search PATH,
+         # but as the default location of the library.
+         _LT_TAGVAR(hardcode_minus_L, $1)=yes
+         ;;
+       esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+       # Try to use the -exported_symbol ld option, if it does not
+       # work, assume that -exports_file does not work either and
+       # implicitly export all symbols.
+       # This should be the same for all languages, so no per-tag cache variable.
+       AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol],
+         [lt_cv_irix_exported_symbol],
+         [save_LDFLAGS="$LDFLAGS"
+          LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+          AC_LINK_IFELSE(
+            [AC_LANG_SOURCE(
+               [AC_LANG_CASE([C], [[int foo (void) { return 0; }]],
+                             [C++], [[int foo (void) { return 0; }]],
+                             [Fortran 77], [[
+      subroutine foo
+      end]],
+                             [Fortran], [[
+      subroutine foo
+      end]])])],
+             [lt_cv_irix_exported_symbol=yes],
+             [lt_cv_irix_exported_symbol=no])
+           LDFLAGS="$save_LDFLAGS"])
+       if test "$lt_cv_irix_exported_symbol" = yes; then
+          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+       fi
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(inherit_rpath, $1)=yes
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    newsos6)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *nto* | *qnx*)
+      ;;
+
+    openbsd*)
+      if test -f /usr/libexec/ld.so; then
+       _LT_TAGVAR(hardcode_direct, $1)=yes
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+       if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+         _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+       else
+         case $host_os in
+          openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
+            _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+            ;;
+          *)
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+            ;;
+         esac
+       fi
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    os2*)
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+       _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    osf4* | osf5*)     # as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+      else
+       _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+       $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+       # Both c and cxx compiler support -rpath directly
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    solaris*)
+      _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+      if test "$GCC" = yes; then
+       wlarc='${wl}'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+      else
+       case `$CC -V 2>&1` in
+       *"Compilers 5.0"*)
+         wlarc=''
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+         ;;
+       *)
+         wlarc='${wl}'
+         _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+         ;;
+       esac
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      case $host_os in
+      solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+      *)
+       # The compiler driver will combine and reorder linker options,
+       # but understands `-z linker_flag'.  GCC discards it without `$wl',
+       # but is careful enough not to reorder.
+       # Supported since Solaris 2.6 (maybe 2.5.1?)
+       if test "$GCC" = yes; then
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+       else
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+       fi
+       ;;
+      esac
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+       # Use $CC to link under sequent, because it throws in some extra .o
+       # files that make .init and .fini sections work.
+       _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+       sni)
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+       ;;
+       siemens)
+         ## LD is ld it makes a PLAMLIB
+         ## CC just makes a GrossModule.
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+         _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+         _LT_TAGVAR(hardcode_direct, $1)=no
+        ;;
+       motorola)
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+       ;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4.3*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       runpath_var=LD_RUN_PATH
+       hardcode_runpath_var=yes
+       _LT_TAGVAR(ld_shlibs, $1)=yes
+      fi
+      ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6*)
+      # Note: We can NOT use -z defs as we might desire, because we do not
+      # link with -lc, and that would cause any symbols used from libc to
+      # always be unresolved, which means just about no library would
+      # ever link correctly.  If we're not using GNU ld we use -z text
+      # though, which does catch some bad symbols but isn't as heavy-handed
+      # as -z defs.
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    uts4*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      _LT_TAGVAR(ld_shlibs, $1)=no
+      ;;
+    esac
+
+    if test x$host_vendor = xsni; then
+      case $host in
+      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym'
+       ;;
+      esac
+    fi
+  fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+    [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+  # Assume -lc should be added
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $_LT_TAGVAR(archive_cmds, $1) in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      AC_CACHE_CHECK([whether -lc should be explicitly linked in],
+       [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1),
+       [$RM conftest*
+       echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+       if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+         soname=conftest
+         lib=conftest
+         libobjs=conftest.$ac_objext
+         deplibs=
+         wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+         pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+         compiler_flags=-v
+         linker_flags=-v
+         verstring=
+         output_objdir=.
+         libname=conftest
+         lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+         _LT_TAGVAR(allow_undefined_flag, $1)=
+         if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
+         then
+           lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+         else
+           lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+         fi
+         _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+       else
+         cat conftest.err 1>&5
+       fi
+       $RM conftest*
+       ])
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+    [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+    [enable_shared_with_static_runtimes], [0],
+    [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+    [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+    [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+    [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+    [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+    [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+    [Commands used to build a loadable module if different from building
+    a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+    [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+    [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+    [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+    [Flag to hardcode $libdir into a binary during linking.
+    This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+    [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+    [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+    DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+    [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+    DIR into the resulting binary and the resulting library dependency is
+    "absolute", i.e impossible to change by setting ${shlibpath_var} if the
+    library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+    [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+    into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+    [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+    into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+    [Set to "yes" if building a shared library automatically hardcodes DIR
+    into the library and all subsequent libraries and executables linked
+    against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+    [Set to yes if linker adds runtime paths of dependent libraries
+    to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+    [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [always_export_symbols], [0],
+    [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+    [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+    [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+    [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+    [Commands necessary for linking programs (against libraries) with templates])
+_LT_TAGDECL([], [postlink_cmds], [2],
+    [Commands necessary for finishing linking programs])
+_LT_TAGDECL([], [file_list_spec], [1],
+    [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl    [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined.  These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC="$CC"
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_SYS_DYNAMIC_LINKER($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+  LT_SYS_DLOPEN_SELF
+  _LT_CMD_STRIPLIB
+
+  # Report which library types will actually be built
+  AC_MSG_CHECKING([if libtool supports shared libraries])
+  AC_MSG_RESULT([$can_build_shared])
+
+  AC_MSG_CHECKING([whether to build shared libraries])
+  test "$can_build_shared" = "no" && enable_shared=no
+
+  # On AIX, shared libraries and static libraries use the same namespace, and
+  # are all built from PIC.
+  case $host_os in
+  aix3*)
+    test "$enable_shared" = yes && enable_static=no
+    if test -n "$RANLIB"; then
+      archive_cmds="$archive_cmds~\$RANLIB \$lib"
+      postinstall_cmds='$RANLIB $lib'
+    fi
+    ;;
+
+  aix[[4-9]]*)
+    if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+      test "$enable_shared" = yes && enable_static=no
+    fi
+    ;;
+  esac
+  AC_MSG_RESULT([$enable_shared])
+
+  AC_MSG_CHECKING([whether to build static libraries])
+  # Make sure either enable_shared or enable_static is yes.
+  test "$enable_shared" = yes || enable_static=yes
+  AC_MSG_RESULT([$enable_static])
+
+  _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC="$lt_save_CC"
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined.  These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
+    ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
+    (test "X$CXX" != "Xg++"))) ; then
+  AC_PROG_CXXCPP
+else
+  _lt_caught_CXX_error=yes
+fi
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_caught_CXX_error" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="int some_variable = 0;"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC=$CC
+  lt_save_CFLAGS=$CFLAGS
+  lt_save_LD=$LD
+  lt_save_GCC=$GCC
+  GCC=$GXX
+  lt_save_with_gnu_ld=$with_gnu_ld
+  lt_save_path_LD=$lt_cv_path_LD
+  if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+    lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+  else
+    $as_unset lt_cv_prog_gnu_ld
+  fi
+  if test -n "${lt_cv_path_LDCXX+set}"; then
+    lt_cv_path_LD=$lt_cv_path_LDCXX
+  else
+    $as_unset lt_cv_path_LD
+  fi
+  test -z "${LDCXX+set}" || LD=$LDCXX
+  CC=${CXX-"c++"}
+  CFLAGS=$CXXFLAGS
+  compiler=$CC
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+
+  if test -n "$compiler"; then
+    # We don't want -fno-exception when compiling C++ code, so set the
+    # no_builtin_flag separately
+    if test "$GXX" = yes; then
+      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+    else
+      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+    fi
+
+    if test "$GXX" = yes; then
+      # Set up default GNU C++ configuration
+
+      LT_PATH_LD
+
+      # Check if GNU C++ uses GNU ld as the underlying linker, since the
+      # archiving commands below assume that GNU ld is being used.
+      if test "$with_gnu_ld" = yes; then
+        _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+        # If archive_cmds runs LD, not CC, wlarc should be empty
+        # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+        #     investigate it a little bit more. (MM)
+        wlarc='${wl}'
+
+        # ancient GNU ld didn't support --whole-archive et. al.
+        if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+         $GREP 'no-whole-archive' > /dev/null; then
+          _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+        else
+          _LT_TAGVAR(whole_archive_flag_spec, $1)=
+        fi
+      else
+        with_gnu_ld=no
+        wlarc=
+
+        # A generic and very simple default shared library creation
+        # command for GNU C++ for the case where it uses the native
+        # linker, instead of GNU ld.  If possible, this setting should
+        # overridden to take advantage of the native linker features on
+        # the platform it is being used on.
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+      fi
+
+      # Commands to make compiler produce verbose output that lists
+      # what "hidden" libraries, object files and flags are used when
+      # linking a shared library.
+      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+    else
+      GXX=no
+      with_gnu_ld=no
+      wlarc=
+    fi
+
+    # PORTME: fill in a description of your system's C++ link characteristics
+    AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+    _LT_TAGVAR(ld_shlibs, $1)=yes
+    case $host_os in
+      aix3*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+      aix[[4-9]]*)
+        if test "$host_cpu" = ia64; then
+          # On IA64, the linker does run time linking by default, so we don't
+          # have to do anything special.
+          aix_use_runtimelinking=no
+          exp_sym_flag='-Bexport'
+          no_entry_flag=""
+        else
+          aix_use_runtimelinking=no
+
+          # Test if we are trying to use run time linking or normal
+          # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+          # need to do runtime linking.
+          case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+           for ld_flag in $LDFLAGS; do
+             case $ld_flag in
+             *-brtl*)
+               aix_use_runtimelinking=yes
+               break
+               ;;
+             esac
+           done
+           ;;
+          esac
+
+          exp_sym_flag='-bexport'
+          no_entry_flag='-bnoentry'
+        fi
+
+        # When large executables or shared objects are built, AIX ld can
+        # have problems creating the table of contents.  If linking a library
+        # or program results in "error TOC overflow" add -mminimal-toc to
+        # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+        # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+        _LT_TAGVAR(archive_cmds, $1)=''
+        _LT_TAGVAR(hardcode_direct, $1)=yes
+        _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+        _LT_TAGVAR(link_all_deplibs, $1)=yes
+        _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+        if test "$GXX" = yes; then
+          case $host_os in aix4.[[012]]|aix4.[[012]].*)
+          # We only want to do this on AIX 4.2 and lower, the check
+          # below for broken collect2 doesn't work under 4.3+
+         collect2name=`${CC} -print-prog-name=collect2`
+         if test -f "$collect2name" &&
+            strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+         then
+           # We have reworked collect2
+           :
+         else
+           # We have old collect2
+           _LT_TAGVAR(hardcode_direct, $1)=unsupported
+           # It fails to find uninstalled libraries when the uninstalled
+           # path is not listed in the libpath.  Setting hardcode_minus_L
+           # to unsupported forces relinking
+           _LT_TAGVAR(hardcode_minus_L, $1)=yes
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=
+         fi
+          esac
+          shared_flag='-shared'
+         if test "$aix_use_runtimelinking" = yes; then
+           shared_flag="$shared_flag "'${wl}-G'
+         fi
+        else
+          # not using gcc
+          if test "$host_cpu" = ia64; then
+         # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+         # chokes on -Wl,-G. The following line is correct:
+         shared_flag='-G'
+          else
+           if test "$aix_use_runtimelinking" = yes; then
+             shared_flag='${wl}-G'
+           else
+             shared_flag='${wl}-bM:SRE'
+           fi
+          fi
+        fi
+
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+        # It seems that -bexpall does not export symbols beginning with
+        # underscore (_), so it is better to generate a list of symbols to
+       # export.
+        _LT_TAGVAR(always_export_symbols, $1)=yes
+        if test "$aix_use_runtimelinking" = yes; then
+          # Warning - without using the other runtime loading flags (-brtl),
+          # -berok will link without error, but may produce a broken library.
+          _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+          # Determine the default libpath from the value encoded in an empty
+          # executable.
+          _LT_SYS_MODULE_PATH_AIX([$1])
+          _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+
+          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+        else
+          if test "$host_cpu" = ia64; then
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+           _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+           _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+          else
+           # Determine the default libpath from the value encoded in an
+           # empty executable.
+           _LT_SYS_MODULE_PATH_AIX([$1])
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+           # Warning - without using the other run time loading flags,
+           # -berok will link without error, but may produce a broken library.
+           _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+           _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+           if test "$with_gnu_ld" = yes; then
+             # We only use this code for GNU lds that support --whole-archive.
+             _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+           else
+             # Exported symbols can be pulled into shared objects from archives
+             _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+           fi
+           _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+           # This is similar to how AIX traditionally builds its shared
+           # libraries.
+           _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+          fi
+        fi
+        ;;
+
+      beos*)
+       if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+         _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+         # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+         # support --undefined.  This deserves some investigation.  FIXME
+         _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       else
+         _LT_TAGVAR(ld_shlibs, $1)=no
+       fi
+       ;;
+
+      chorus*)
+        case $cc_basename in
+          *)
+         # FIXME: insert proper C++ library support
+         _LT_TAGVAR(ld_shlibs, $1)=no
+         ;;
+        esac
+        ;;
+
+      cygwin* | mingw* | pw32* | cegcc*)
+       case $GXX,$cc_basename in
+       ,cl* | no,cl*)
+         # Native MSVC
+         # hardcode_libdir_flag_spec is actually meaningless, as there is
+         # no search path for DLLs.
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+         _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+         _LT_TAGVAR(always_export_symbols, $1)=yes
+         _LT_TAGVAR(file_list_spec, $1)='@'
+         # Tell ltmain to make .lib files, not .a files.
+         libext=lib
+         # Tell ltmain to make .dll files, not .so files.
+         shrext_cmds=".dll"
+         # FIXME: Setting linknames here is a bad hack.
+         _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
+         _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+             $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
+           else
+             $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
+           fi~
+           $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+           linknames='
+         # The linker will not automatically build a static lib if we build a DLL.
+         # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+         _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+         # Don't use ranlib
+         _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+         _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+           lt_tool_outputfile="@TOOL_OUTPUT@"~
+           case $lt_outputfile in
+             *.exe|*.EXE) ;;
+             *)
+               lt_outputfile="$lt_outputfile.exe"
+               lt_tool_outputfile="$lt_tool_outputfile.exe"
+               ;;
+           esac~
+           func_to_tool_file "$lt_outputfile"~
+           if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
+             $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+             $RM "$lt_outputfile.manifest";
+           fi'
+         ;;
+       *)
+         # g++
+         # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+         # as there is no search path for DLLs.
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+         _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols'
+         _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+         _LT_TAGVAR(always_export_symbols, $1)=no
+         _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+         if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+           # If the export-symbols file already is a .def file (1st line
+           # is EXPORTS), use it as is; otherwise, prepend...
+           _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+             cp $export_symbols $output_objdir/$soname.def;
+           else
+             echo EXPORTS > $output_objdir/$soname.def;
+             cat $export_symbols >> $output_objdir/$soname.def;
+           fi~
+           $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+         else
+           _LT_TAGVAR(ld_shlibs, $1)=no
+         fi
+         ;;
+       esac
+       ;;
+      darwin* | rhapsody*)
+        _LT_DARWIN_LINKER_FEATURES($1)
+       ;;
+
+      dgux*)
+        case $cc_basename in
+          ec++*)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          ghcx*)
+           # Green Hills C++ Compiler
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+        esac
+        ;;
+
+      freebsd2.*)
+        # C++ shared libraries reported to be fairly broken before
+       # switch to ELF
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      freebsd-elf*)
+        _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+        ;;
+
+      freebsd* | dragonfly*)
+        # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+        # conventions
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+        ;;
+
+      gnu*)
+        ;;
+
+      haiku*)
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+        _LT_TAGVAR(link_all_deplibs, $1)=yes
+        ;;
+
+      hpux9*)
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+        _LT_TAGVAR(hardcode_direct, $1)=yes
+        _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+                                            # but as the default
+                                            # location of the library.
+
+        case $cc_basename in
+          CC*)
+            # FIXME: insert proper C++ library support
+            _LT_TAGVAR(ld_shlibs, $1)=no
+            ;;
+          aCC*)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+            # Commands to make compiler produce verbose output that lists
+            # what "hidden" libraries, object files and flags are used when
+            # linking a shared library.
+            #
+            # There doesn't appear to be a way to prevent this compiler from
+            # explicitly linking system object files so we need to strip them
+            # from the output so that they don't get included in the library
+            # dependencies.
+            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+            ;;
+          *)
+            if test "$GXX" = yes; then
+              _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+            else
+              # FIXME: insert proper C++ library support
+              _LT_TAGVAR(ld_shlibs, $1)=no
+            fi
+            ;;
+        esac
+        ;;
+
+      hpux10*|hpux11*)
+        if test $with_gnu_ld = no; then
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+         _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+          case $host_cpu in
+            hppa*64*|ia64*)
+              ;;
+            *)
+             _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+              ;;
+          esac
+        fi
+        case $host_cpu in
+          hppa*64*|ia64*)
+            _LT_TAGVAR(hardcode_direct, $1)=no
+            _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+            ;;
+          *)
+            _LT_TAGVAR(hardcode_direct, $1)=yes
+            _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+                                                # but as the default
+                                                # location of the library.
+            ;;
+        esac
+
+        case $cc_basename in
+          CC*)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          aCC*)
+           case $host_cpu in
+             hppa*64*)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+               ;;
+             ia64*)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+               ;;
+             *)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+               ;;
+           esac
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+           ;;
+          *)
+           if test "$GXX" = yes; then
+             if test $with_gnu_ld = no; then
+               case $host_cpu in
+                 hppa*64*)
+                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+                   ;;
+                 ia64*)
+                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+                   ;;
+                 *)
+                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+                   ;;
+               esac
+             fi
+           else
+             # FIXME: insert proper C++ library support
+             _LT_TAGVAR(ld_shlibs, $1)=no
+           fi
+           ;;
+        esac
+        ;;
+
+      interix[[3-9]]*)
+       _LT_TAGVAR(hardcode_direct, $1)=no
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+       # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+       # Instead, shared libraries are loaded at an image base (0x10000000 by
+       # default) and relocated if they conflict, which is a slow very memory
+       # consuming and fragmenting process.  To avoid this, we pick a random,
+       # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+       # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+       ;;
+      irix5* | irix6*)
+        case $cc_basename in
+          CC*)
+           # SGI C++
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+
+           # Archives containing C++ object files must be created using
+           # "CC -ar", where "CC" is the IRIX C++ compiler.  This is
+           # necessary to make sure instantiated templates are included
+           # in the archive.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+           ;;
+          *)
+           if test "$GXX" = yes; then
+             if test "$with_gnu_ld" = no; then
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+             else
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib'
+             fi
+           fi
+           _LT_TAGVAR(link_all_deplibs, $1)=yes
+           ;;
+        esac
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+        _LT_TAGVAR(inherit_rpath, $1)=yes
+        ;;
+
+      linux* | k*bsd*-gnu | kopensolaris*-gnu)
+        case $cc_basename in
+          KCC*)
+           # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+           # KCC will only create a shared library if the output file
+           # ends with ".so" (or ".sl" for HP-UX), so rename the library
+           # to its proper name (with version) after linking.
+           _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+           # Archives containing C++ object files must be created using
+           # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+           ;;
+         icpc* | ecpc* )
+           # Intel C++
+           with_gnu_ld=yes
+           # version 8.0 and above of icpc choke on multiply defined symbols
+           # if we add $predep_objects and $postdep_objects, however 7.1 and
+           # earlier do not add the objects themselves.
+           case `$CC -V 2>&1` in
+             *"Version 7."*)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+               ;;
+             *)  # Version 8.0 or newer
+               tmp_idyn=
+               case $host_cpu in
+                 ia64*) tmp_idyn=' -i_dynamic';;
+               esac
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+               ;;
+           esac
+           _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+           _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+           ;;
+          pgCC* | pgcpp*)
+            # Portland Group C++ compiler
+           case `$CC -V` in
+           *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*)
+             _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+               rm -rf $tpldir~
+               $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+               compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+             _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+               rm -rf $tpldir~
+               $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+               $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+               $RANLIB $oldlib'
+             _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+               rm -rf $tpldir~
+               $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+               $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+             _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+               rm -rf $tpldir~
+               $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+               $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+             ;;
+           *) # Version 6 and above use weak symbols
+             _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+             _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+             ;;
+           esac
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+           _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+            ;;
+         cxx*)
+           # Compaq C++
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname  -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+
+           runpath_var=LD_RUN_PATH
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
+           ;;
+         xl* | mpixl* | bgxl*)
+           # IBM XL 8.0 on PPC, with GNU ld
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+           _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+           if test "x$supports_anon_versioning" = xyes; then
+             _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+               cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+               echo "local: *; };" >> $output_objdir/$libname.ver~
+               $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+           fi
+           ;;
+         *)
+           case `$CC -V 2>&1 | sed 5q` in
+           *Sun\ C*)
+             # Sun C++ 5.9
+             _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+             _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+             _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
+             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+             _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+             _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+             # Not sure whether something based on
+             # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+             # would be better.
+             output_verbose_link_cmd='func_echo_all'
+
+             # Archives containing C++ object files must be created using
+             # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+             # necessary to make sure instantiated templates are included
+             # in the archive.
+             _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+             ;;
+           esac
+           ;;
+       esac
+       ;;
+
+      lynxos*)
+        # FIXME: insert proper C++ library support
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       ;;
+
+      m88k*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+       ;;
+
+      mvs*)
+        case $cc_basename in
+          cxx*)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+         *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+       esac
+       ;;
+
+      netbsd*)
+        if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+         _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable  -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+         wlarc=
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+         _LT_TAGVAR(hardcode_direct, $1)=yes
+         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       fi
+       # Workaround some broken pre-1.5 toolchains
+       output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+       ;;
+
+      *nto* | *qnx*)
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+       ;;
+
+      openbsd2*)
+        # C++ shared libraries are fairly broken
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       ;;
+
+      openbsd*)
+       if test -f /usr/libexec/ld.so; then
+         _LT_TAGVAR(hardcode_direct, $1)=yes
+         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+         _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+         if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+           _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+         fi
+         output_verbose_link_cmd=func_echo_all
+       else
+         _LT_TAGVAR(ld_shlibs, $1)=no
+       fi
+       ;;
+
+      osf3* | osf4* | osf5*)
+        case $cc_basename in
+          KCC*)
+           # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+           # KCC will only create a shared library if the output file
+           # ends with ".so" (or ".sl" for HP-UX), so rename the library
+           # to its proper name (with version) after linking.
+           _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+           # Archives containing C++ object files must be created using
+           # the KAI C++ compiler.
+           case $host in
+             osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
+             *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+           esac
+           ;;
+          RCC*)
+           # Rational C++ 2.4.1
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          cxx*)
+           case $host in
+             osf3*)
+               _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+               _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+               ;;
+             *)
+               _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+                 echo "-hidden">> $lib.exp~
+                 $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp  `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~
+                 $RM $lib.exp'
+               _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+               ;;
+           esac
+
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+           ;;
+         *)
+           if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+             _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+             case $host in
+               osf3*)
+                 _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+                 ;;
+               *)
+                 _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+                 ;;
+             esac
+
+             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+             _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+             # Commands to make compiler produce verbose output that lists
+             # what "hidden" libraries, object files and flags are used when
+             # linking a shared library.
+             output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+           else
+             # FIXME: insert proper C++ library support
+             _LT_TAGVAR(ld_shlibs, $1)=no
+           fi
+           ;;
+        esac
+        ;;
+
+      psos*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      sunos4*)
+        case $cc_basename in
+          CC*)
+           # Sun C++ 4.x
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          lcc*)
+           # Lucid
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+        esac
+        ;;
+
+      solaris*)
+        case $cc_basename in
+          CC* | sunCC*)
+           # Sun C++ 4.2, 5.x and Centerline C++
+            _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+           _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+           _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag}  -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+             $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+           _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+           case $host_os in
+             solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+             *)
+               # The compiler driver will combine and reorder linker options,
+               # but understands `-z linker_flag'.
+               # Supported since Solaris 2.6 (maybe 2.5.1?)
+               _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+               ;;
+           esac
+           _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+           output_verbose_link_cmd='func_echo_all'
+
+           # Archives containing C++ object files must be created using
+           # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+           # necessary to make sure instantiated templates are included
+           # in the archive.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+           ;;
+          gcx*)
+           # Green Hills C++ Compiler
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+
+           # The C++ compiler must be used to create the archive.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+           ;;
+          *)
+           # GNU C++ compiler with Solaris linker
+           if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+             _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
+             if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+                 $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+               # Commands to make compiler produce verbose output that lists
+               # what "hidden" libraries, object files and flags are used when
+               # linking a shared library.
+               output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+             else
+               # g++ 2.7 appears to require `-G' NOT `-shared' on this
+               # platform.
+               _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+                 $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+               # Commands to make compiler produce verbose output that lists
+               # what "hidden" libraries, object files and flags are used when
+               # linking a shared library.
+               output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+             fi
+
+             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
+             case $host_os in
+               solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+               *)
+                 _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+                 ;;
+             esac
+           fi
+           ;;
+        esac
+        ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+      _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+
+      case $cc_basename in
+        CC*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+      esac
+      ;;
+
+      sysv5* | sco3.2v5* | sco5v6*)
+       # Note: We can NOT use -z defs as we might desire, because we do not
+       # link with -lc, and that would cause any symbols used from libc to
+       # always be unresolved, which means just about no library would
+       # ever link correctly.  If we're not using GNU ld we use -z text
+       # though, which does catch some bad symbols but isn't as heavy-handed
+       # as -z defs.
+       _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+       _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+       _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+       _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+       _LT_TAGVAR(link_all_deplibs, $1)=yes
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+       runpath_var='LD_RUN_PATH'
+
+       case $cc_basename in
+          CC*)
+           _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~
+             '"$_LT_TAGVAR(old_archive_cmds, $1)"
+           _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~
+             '"$_LT_TAGVAR(reload_cmds, $1)"
+           ;;
+         *)
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+           ;;
+       esac
+      ;;
+
+      tandem*)
+        case $cc_basename in
+          NCC*)
+           # NonStop-UX NCC 3.20
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+        esac
+        ;;
+
+      vxworks*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      *)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+    esac
+
+    AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+    test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+    _LT_TAGVAR(GCC, $1)="$GXX"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_SYS_HIDDEN_LIBDEPS($1)
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+  LDCXX=$LD
+  LD=$lt_save_LD
+  GCC=$lt_save_GCC
+  with_gnu_ld=$lt_save_with_gnu_ld
+  lt_cv_path_LDCXX=$lt_cv_path_LD
+  lt_cv_path_LD=$lt_save_path_LD
+  lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+  lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test "$_lt_caught_CXX_error" != yes
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_FUNC_STRIPNAME_CNF
+# ----------------------
+# func_stripname_cnf prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+#
+# This function is identical to the (non-XSI) version of func_stripname,
+# except this one can be used by m4 code that may be executed by configure,
+# rather than the libtool script.
+m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl
+AC_REQUIRE([_LT_DECL_SED])
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])
+func_stripname_cnf ()
+{
+  case ${2} in
+  .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
+  *)  func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+  esac
+} # func_stripname_cnf
+])# _LT_FUNC_STRIPNAME_CNF
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library.  It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+  Foo (void) { a = 0; }
+private:
+  int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+      subroutine foo
+      implicit none
+      integer*4 a
+      a=0
+      return
+      end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+      subroutine foo
+      implicit none
+      integer a
+      a=0
+      return
+      end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+  private int a;
+  public void bar (void) {
+    a = 0;
+  }
+};
+_LT_EOF
+], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF
+package foo
+func foo() {
+}
+_LT_EOF
+])
+
+_lt_libdeps_save_CFLAGS=$CFLAGS
+case "$CC $CFLAGS " in #(
+*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
+*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
+*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
+esac
+
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+  # Parse the compiler output and extract the necessary
+  # objects, libraries and library flags.
+
+  # Sentinel used to keep track of whether or not we are before
+  # the conftest object file.
+  pre_test_object_deps_done=no
+
+  for p in `eval "$output_verbose_link_cmd"`; do
+    case ${prev}${p} in
+
+    -L* | -R* | -l*)
+       # Some compilers place space between "-{L,R}" and the path.
+       # Remove the space.
+       if test $p = "-L" ||
+          test $p = "-R"; then
+        prev=$p
+        continue
+       fi
+
+       # Expand the sysroot to ease extracting the directories later.
+       if test -z "$prev"; then
+         case $p in
+         -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;;
+         -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;;
+         -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;;
+         esac
+       fi
+       case $p in
+       =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;;
+       esac
+       if test "$pre_test_object_deps_done" = no; then
+        case ${prev} in
+        -L | -R)
+          # Internal compiler library paths should come after those
+          # provided the user.  The postdeps already come after the
+          # user supplied libs so there is no need to process them.
+          if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+            _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
+          else
+            _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
+          fi
+          ;;
+        # The "-l" case would never come before the object being
+        # linked, so don't bother handling this case.
+        esac
+       else
+        if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+          _LT_TAGVAR(postdeps, $1)="${prev}${p}"
+        else
+          _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}"
+        fi
+       fi
+       prev=
+       ;;
+
+    *.lto.$objext) ;; # Ignore GCC LTO objects
+    *.$objext)
+       # This assumes that the test object file only shows up
+       # once in the compiler output.
+       if test "$p" = "conftest.$objext"; then
+        pre_test_object_deps_done=yes
+        continue
+       fi
+
+       if test "$pre_test_object_deps_done" = no; then
+        if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+          _LT_TAGVAR(predep_objects, $1)="$p"
+        else
+          _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+        fi
+       else
+        if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+          _LT_TAGVAR(postdep_objects, $1)="$p"
+        else
+          _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+        fi
+       fi
+       ;;
+
+    *) ;; # Ignore the rest.
+
+    esac
+  done
+
+  # Clean up.
+  rm -f a.out a.exe
+else
+  echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+CFLAGS=$_lt_libdeps_save_CFLAGS
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+  # Interix 3.5 installs completely hosed .la files for C++, so rather than
+  # hack all around it, let's just trust "g++" to DTRT.
+  _LT_TAGVAR(predep_objects,$1)=
+  _LT_TAGVAR(postdep_objects,$1)=
+  _LT_TAGVAR(postdeps,$1)=
+  ;;
+
+linux*)
+  case `$CC -V 2>&1 | sed 5q` in
+  *Sun\ C*)
+    # Sun C++ 5.9
+
+    # The more standards-conforming stlport4 library is
+    # incompatible with the Cstd library. Avoid specifying
+    # it if it's in CXXFLAGS. Ignore libCrun as
+    # -library=stlport4 depends on it.
+    case " $CXX $CXXFLAGS " in
+    *" -library=stlport4 "*)
+      solaris_use_stlport4=yes
+      ;;
+    esac
+
+    if test "$solaris_use_stlport4" != yes; then
+      _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+    fi
+    ;;
+  esac
+  ;;
+
+solaris*)
+  case $cc_basename in
+  CC* | sunCC*)
+    # The more standards-conforming stlport4 library is
+    # incompatible with the Cstd library. Avoid specifying
+    # it if it's in CXXFLAGS. Ignore libCrun as
+    # -library=stlport4 depends on it.
+    case " $CXX $CXXFLAGS " in
+    *" -library=stlport4 "*)
+      solaris_use_stlport4=yes
+      ;;
+    esac
+
+    # Adding this requires a known-good setup of shared libraries for
+    # Sun compiler versions before 5.6, else PIC objects from an old
+    # archive will be linked into the output, leading to subtle bugs.
+    if test "$solaris_use_stlport4" != yes; then
+      _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+    fi
+    ;;
+  esac
+  ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+    [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+    [Dependencies to place before and after the objects being linked to
+    create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+    [The library search path used internally by the compiler when linking
+    a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_LANG_PUSH(Fortran 77)
+if test -z "$F77" || test "X$F77" = "Xno"; then
+  _lt_disable_F77=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_F77" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="\
+      subroutine t
+      return
+      end
+"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code="\
+      program t
+      end
+"
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC="$CC"
+  lt_save_GCC=$GCC
+  lt_save_CFLAGS=$CFLAGS
+  CC=${F77-"f77"}
+  CFLAGS=$FFLAGS
+  compiler=$CC
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+  GCC=$G77
+  if test -n "$compiler"; then
+    AC_MSG_CHECKING([if libtool supports shared libraries])
+    AC_MSG_RESULT([$can_build_shared])
+
+    AC_MSG_CHECKING([whether to build shared libraries])
+    test "$can_build_shared" = "no" && enable_shared=no
+
+    # On AIX, shared libraries and static libraries use the same namespace, and
+    # are all built from PIC.
+    case $host_os in
+      aix3*)
+        test "$enable_shared" = yes && enable_static=no
+        if test -n "$RANLIB"; then
+          archive_cmds="$archive_cmds~\$RANLIB \$lib"
+          postinstall_cmds='$RANLIB $lib'
+        fi
+        ;;
+      aix[[4-9]]*)
+       if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+         test "$enable_shared" = yes && enable_static=no
+       fi
+        ;;
+    esac
+    AC_MSG_RESULT([$enable_shared])
+
+    AC_MSG_CHECKING([whether to build static libraries])
+    # Make sure either enable_shared or enable_static is yes.
+    test "$enable_shared" = yes || enable_static=yes
+    AC_MSG_RESULT([$enable_static])
+
+    _LT_TAGVAR(GCC, $1)="$G77"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  GCC=$lt_save_GCC
+  CC="$lt_save_CC"
+  CFLAGS="$lt_save_CFLAGS"
+fi # test "$_lt_disable_F77" != yes
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_LANG_PUSH(Fortran)
+
+if test -z "$FC" || test "X$FC" = "Xno"; then
+  _lt_disable_FC=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_FC" != yes; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="\
+      subroutine t
+      return
+      end
+"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code="\
+      program t
+      end
+"
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC="$CC"
+  lt_save_GCC=$GCC
+  lt_save_CFLAGS=$CFLAGS
+  CC=${FC-"f95"}
+  CFLAGS=$FCFLAGS
+  compiler=$CC
+  GCC=$ac_cv_fc_compiler_gnu
+
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+
+  if test -n "$compiler"; then
+    AC_MSG_CHECKING([if libtool supports shared libraries])
+    AC_MSG_RESULT([$can_build_shared])
+
+    AC_MSG_CHECKING([whether to build shared libraries])
+    test "$can_build_shared" = "no" && enable_shared=no
+
+    # On AIX, shared libraries and static libraries use the same namespace, and
+    # are all built from PIC.
+    case $host_os in
+      aix3*)
+        test "$enable_shared" = yes && enable_static=no
+        if test -n "$RANLIB"; then
+          archive_cmds="$archive_cmds~\$RANLIB \$lib"
+          postinstall_cmds='$RANLIB $lib'
+        fi
+        ;;
+      aix[[4-9]]*)
+       if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+         test "$enable_shared" = yes && enable_static=no
+       fi
+        ;;
+    esac
+    AC_MSG_RESULT([$enable_shared])
+
+    AC_MSG_CHECKING([whether to build static libraries])
+    # Make sure either enable_shared or enable_static is yes.
+    test "$enable_shared" = yes || enable_static=yes
+    AC_MSG_RESULT([$enable_static])
+
+    _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu"
+    _LT_TAGVAR(LD, $1)="$LD"
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_SYS_HIDDEN_LIBDEPS($1)
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  GCC=$lt_save_GCC
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+fi # test "$_lt_disable_FC" != yes
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+CFLAGS=$GCJFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)="$LD"
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+
+  _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_GO_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Go compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_GO_CONFIG],
+[AC_REQUIRE([LT_PROG_GO])dnl
+AC_LANG_SAVE
+
+# Source file extension for Go test sources.
+ac_ext=go
+
+# Object file extension for compiled Go test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="package main; func main() { }"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='package main; func main() { }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GOC-"gccgo"}
+CFLAGS=$GOFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)="$LD"
+_LT_CC_BASENAME([$compiler])
+
+# Go did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+
+  _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GO_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="$lt_simple_compile_test_code"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+CFLAGS=
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+  :
+  _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+  [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+    [AC_CHECK_TOOL(GCJ, gcj,)
+      test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+      AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_GO
+# ----------
+AC_DEFUN([LT_PROG_GO],
+[AC_CHECK_TOOL(GOC, gccgo,)
+])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_OBJDUMP
+# --------------
+# If we don't have a new enough Autoconf to choose the best objdump
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_OBJDUMP],
+[AC_CHECK_TOOL(OBJDUMP, objdump, false)
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
+AC_SUBST([OBJDUMP])
+])
+
+# _LT_DECL_DLLTOOL
+# ----------------
+# Ensure DLLTOOL variable is set.
+m4_defun([_LT_DECL_DLLTOOL],
+[AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])
+AC_SUBST([DLLTOOL])
+])
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible.  Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+    [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_SED.  When it is available in   #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for lt_ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+        lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+      fi
+    done
+  done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+  test ! -f $lt_ac_sed && continue
+  cat /dev/null > conftest.in
+  lt_ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+  # Check for GNU sed and select it if it is found.
+  if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+    lt_cv_path_SED=$lt_ac_sed
+    break
+  fi
+  while true; do
+    cat conftest.in conftest.in >conftest.tmp
+    mv conftest.tmp conftest.in
+    cp conftest.in conftest.nl
+    echo >>conftest.nl
+    $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+    cmp -s conftest.out conftest.nl || break
+    # 10000 chars as input seems more than enough
+    test $lt_ac_count -gt 10 && break
+    lt_ac_count=`expr $lt_ac_count + 1`
+    if test $lt_ac_count -gt $lt_ac_max; then
+      lt_ac_max=$lt_ac_count
+      lt_cv_path_SED=$lt_ac_sed
+    fi
+  done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[AC_MSG_CHECKING([whether the shell understands some XSI constructs])
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+  test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \
+      = c,a/b,b/c, \
+    && eval 'test $(( 1 + 1 )) -eq 2 \
+    && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+  && xsi_shell=yes
+AC_MSG_RESULT([$xsi_shell])
+_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell'])
+
+AC_MSG_CHECKING([whether the shell understands "+="])
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \
+    >/dev/null 2>&1 \
+  && lt_shell_append=yes
+AC_MSG_RESULT([$lt_shell_append])
+_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append'])
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  lt_unset=unset
+else
+  lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+  lt_SP2NL='tr \040 \012'
+  lt_NL2SP='tr \015\012 \040\040'
+  ;;
+ *) # EBCDIC based system
+  lt_SP2NL='tr \100 \n'
+  lt_NL2SP='tr \r\n \100\100'
+  ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY)
+# ------------------------------------------------------
+# In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and
+# '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY.
+m4_defun([_LT_PROG_FUNCTION_REPLACE],
+[dnl {
+sed -e '/^$1 ()$/,/^} # $1 /c\
+$1 ()\
+{\
+m4_bpatsubsts([$2], [$], [\\], [^\([    ]\)], [\\\1])
+} # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+])
+
+
+# _LT_PROG_REPLACE_SHELLFNS
+# -------------------------
+# Replace existing portable implementations of several shell functions with
+# equivalent extended shell implementations where those features are available..
+m4_defun([_LT_PROG_REPLACE_SHELLFNS],
+[if test x"$xsi_shell" = xyes; then
+  _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl
+    case ${1} in
+      */*) func_dirname_result="${1%/*}${2}" ;;
+      *  ) func_dirname_result="${3}" ;;
+    esac])
+
+  _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl
+    func_basename_result="${1##*/}"])
+
+  _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl
+    case ${1} in
+      */*) func_dirname_result="${1%/*}${2}" ;;
+      *  ) func_dirname_result="${3}" ;;
+    esac
+    func_basename_result="${1##*/}"])
+
+  _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl
+    # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+    # positional parameters, so assign one to ordinary parameter first.
+    func_stripname_result=${3}
+    func_stripname_result=${func_stripname_result#"${1}"}
+    func_stripname_result=${func_stripname_result%"${2}"}])
+
+  _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl
+    func_split_long_opt_name=${1%%=*}
+    func_split_long_opt_arg=${1#*=}])
+
+  _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl
+    func_split_short_opt_arg=${1#??}
+    func_split_short_opt_name=${1%"$func_split_short_opt_arg"}])
+
+  _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl
+    case ${1} in
+      *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+      *)    func_lo2o_result=${1} ;;
+    esac])
+
+  _LT_PROG_FUNCTION_REPLACE([func_xform], [    func_xform_result=${1%.*}.lo])
+
+  _LT_PROG_FUNCTION_REPLACE([func_arith], [    func_arith_result=$(( $[*] ))])
+
+  _LT_PROG_FUNCTION_REPLACE([func_len], [    func_len_result=${#1}])
+fi
+
+if test x"$lt_shell_append" = xyes; then
+  _LT_PROG_FUNCTION_REPLACE([func_append], [    eval "${1}+=\\${2}"])
+
+  _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl
+    func_quote_for_eval "${2}"
+dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \
+    eval "${1}+=\\\\ \\$func_quote_for_eval_result"])
+
+  # Save a `func_append' function call where possible by direct use of '+='
+  sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+else
+  # Save a `func_append' function call even when '+=' is not available
+  sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+fi
+
+if test x"$_lt_function_replace_fail" = x":"; then
+  AC_MSG_WARN([Unable to substitute extended shell functions in $ofile])
+fi
+])
+
+# _LT_PATH_CONVERSION_FUNCTIONS
+# -----------------------------
+# Determine which file name conversion functions should be used by
+# func_to_host_file (and, implicitly, by func_to_host_path).  These are needed
+# for certain cross-compile configurations and native mingw.
+m4_defun([_LT_PATH_CONVERSION_FUNCTIONS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_MSG_CHECKING([how to convert $build file names to $host format])
+AC_CACHE_VAL(lt_cv_to_host_file_cmd,
+[case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+        ;;
+    esac
+    ;;
+  *-*-cygwin* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_noop
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+        ;;
+    esac
+    ;;
+  * ) # unhandled hosts (and "normal" native builds)
+    lt_cv_to_host_file_cmd=func_convert_file_noop
+    ;;
+esac
+])
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+AC_MSG_RESULT([$lt_cv_to_host_file_cmd])
+_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd],
+         [0], [convert $build file names to $host format])dnl
+
+AC_MSG_CHECKING([how to convert $build file names to toolchain format])
+AC_CACHE_VAL(lt_cv_to_tool_file_cmd,
+[#assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+        ;;
+    esac
+    ;;
+esac
+])
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+AC_MSG_RESULT([$lt_cv_to_tool_file_cmd])
+_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd],
+         [0], [convert $build files to toolchain format])dnl
+])# _LT_PATH_CONVERSION_FUNCTIONS
+
+# Helper functions for option handling.                    -*- Autoconf -*-
+#
+#   Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation,
+#   Inc.
+#   Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 7 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it.  Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+        _LT_MANGLE_DEFUN([$1], [$2]),
+    [m4_warning([Unknown $1 option `$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+           [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+                     [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME.  If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+    [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+  dnl
+  dnl Simply set some default values (i.e off) if boolean options were not
+  dnl specified:
+  _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+  ])
+  _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+  ])
+  dnl
+  dnl If no reference was made to various pairs of opposing options, then
+  dnl we run the default mode handler for the pair.  For example, if neither
+  dnl `shared' nor `disable-shared' was passed, we enable building of shared
+  dnl archives by default:
+  _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+  _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+  _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+  _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+                  [_LT_ENABLE_FAST_INSTALL])
+  ])
+])# _LT_SET_OPTIONS
+
+
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
+  AC_CHECK_TOOL(AS, as, false)
+  AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+  AC_CHECK_TOOL(OBJDUMP, objdump, false)
+  ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS],      [1], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the `shared' and
+# `disable-shared' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+    [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+       [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_shared=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+    _LT_DECL([build_libtool_libs], [enable_shared], [0],
+       [Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the `static' and
+# `disable-static' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+    [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+       [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_static=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+    _LT_DECL([build_old_libs], [enable_static], [0],
+       [Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the `fast-install'
+# and `disable-fast-install' LT_INIT options.
+# DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+    [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+    [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_fast_install=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+        [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the `pic-only' and `no-pic'
+# LT_INIT options.
+# MODE is either `yes' or `no'.  If omitted, it defaults to `both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+    [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
+       [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+    [lt_p=${PACKAGE-default}
+    case $withval in
+    yes|no) pic_mode=$withval ;;
+    *)
+      pic_mode=default
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for lt_pkg in $withval; do
+       IFS="$lt_save_ifs"
+       if test "X$lt_pkg" = "X$lt_p"; then
+         pic_mode=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac],
+    [pic_mode=default])
+
+test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+                [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+                [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+                [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+                [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+                [m4_define([_LTDL_TYPE], [convenience])])
+
+# ltsugar.m4 -- libtool m4 base layer.                         -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+       [$#], [2], [[$2]],
+       [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+       [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59 which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+       [$#], 1, [],
+       [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+          m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn.  Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+       [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+            [m4_foreach([_Lt_suffix],
+               ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+       [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+         [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+                [lt_append([$1], [$2], [$3])$4],
+                [$5])],
+         [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+       m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+    m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+       [$5],
+    [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+  [lt_join(m4_quote(m4_default([$4], [[, ]])),
+           lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+                     [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
+
+# ltversion.m4 -- version numbers                      -*- Autoconf -*-
+#
+#   Copyright (C) 2004 Free Software Foundation, Inc.
+#   Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# @configure_input@
+
+# serial 3337 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.4.2])
+m4_define([LT_PACKAGE_REVISION], [1.3337])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.4.2'
+macro_revision='1.3337'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
+
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions.    -*-Autoconf-*-
+#
+#   Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
+#   Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 5 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick.  It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else.  This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. 
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION],  [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP],             [AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH],        [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT],         [AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX],    [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN],           [AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR],             [AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL],    [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN],           [AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER],       [AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK],               [AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE],        [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF],    [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O],    [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR],         [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR],            [AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP],  [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC],             [AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU],            [AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG],    [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD],   [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP],          [AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED],            [AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME],           [AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE],  [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE],    [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL],          [AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP],          [AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN],                [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER],     [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG],          [AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL],   [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX],           [AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77],           [AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ],           [AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG],  [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG],      [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG],        [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG],    [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG],        [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG],    [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG],        [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG],    [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG],     [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG],         [AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C],       [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS],        [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
+m4_ifndef([_LT_AC_PROG_CXXCPP],                [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
+m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS],        [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
+m4_ifndef([_LT_PROG_ECHO_BACKSLASH],   [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_PROG_F77],              [AC_DEFUN([_LT_PROG_F77])])
+m4_ifndef([_LT_PROG_FC],               [AC_DEFUN([_LT_PROG_FC])])
+m4_ifndef([_LT_PROG_CXX],              [AC_DEFUN([_LT_PROG_CXX])])
+
+# pkg.m4 - Macros to locate and utilise pkg-config.            -*- Autoconf -*-
+# serial 1 (pkg-config-0.24)
+# 
+# Copyright Â© 2004 Scott James Remnant <scott@netsplit.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# PKG_PROG_PKG_CONFIG([MIN-VERSION])
+# ----------------------------------
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+       AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+       _pkg_min_version=m4_default([$1], [0.9.0])
+       AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+       if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+               AC_MSG_RESULT([yes])
+       else
+               AC_MSG_RESULT([no])
+               PKG_CONFIG=""
+       fi
+fi[]dnl
+])# PKG_PROG_PKG_CONFIG
+
+# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# Check to see whether a particular set of modules exists.  Similar
+# to PKG_CHECK_MODULES(), but does not set variables or print errors.
+#
+# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+# only at the first occurence in configure.ac, so if the first place
+# it's called might be skipped (such as if it is within an "if", you
+# have to call PKG_CHECK_EXISTS manually
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+    AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+  m4_default([$2], [:])
+m4_ifvaln([$3], [else
+  $3])dnl
+fi])
+
+# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+# ---------------------------------------------
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+    pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+    PKG_CHECK_EXISTS([$3],
+                     [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes ],
+                    [pkg_failed=yes])
+ else
+    pkg_failed=untried
+fi[]dnl
+])# _PKG_CONFIG
+
+# _PKG_SHORT_ERRORS_SUPPORTED
+# -----------------------------
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi[]dnl
+])# _PKG_SHORT_ERRORS_SUPPORTED
+
+
+# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+#
+#
+# Note that if there is a possibility the first call to
+# PKG_CHECK_MODULES might not happen, you should be sure to include an
+# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+#
+#
+# --------------------------------------------------------------
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+       AC_MSG_RESULT([no])
+        _PKG_SHORT_ERRORS_SUPPORTED
+        if test $_pkg_short_errors_supported = yes; then
+               $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
+        else 
+               $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+       m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])[]dnl
+        ])
+elif test $pkg_failed = untried; then
+       AC_MSG_RESULT([no])
+       m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
+        ])
+else
+       $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+       $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+        AC_MSG_RESULT([yes])
+       $3
+fi[]dnl
+])# PKG_CHECK_MODULES
+
+# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008, 2011 Free Software
+# Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 1
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.11'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version.  Point them to the right macro.
+m4_if([$1], [1.11.3], [],
+      [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too.  Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.11.3])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+  [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
+
+# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 1
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'.  In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory.  The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run.  This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+#    fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+#    fails if $ac_aux_dir is absolute,
+#    fails when called from a subdirectory in a VPATH build with
+#          a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir.  In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
+#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+#   MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH.  The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL                                            -*- Autoconf -*-
+
+# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 9
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
+       [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+  AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009,
+# 2010, 2011 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 12
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC,   [depcc="$CC"   am_compiler_list=],
+       [$1], CXX,  [depcc="$CXX"  am_compiler_list=],
+       [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+       [$1], UPC,  [depcc="$UPC"  am_compiler_list=],
+       [$1], GCJ,  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                   [depcc="$$1"   am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+               [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_$1_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+  fi
+  am__universal=false
+  m4_case([$1], [CC],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac],
+    [CXX],
+    [case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac])
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+      # Solaris 8's {/usr,}/bin/sh.
+      touch sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+       continue
+      else
+       break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok `-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_$1_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[  --disable-dependency-tracking  speeds up one-time build
+  --enable-dependency-tracking   do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+AC_SUBST([am__nodep])dnl
+_AM_SUBST_NOTMAKE([am__nodep])dnl
+])
+
+# Generate code to set up dependency tracking.              -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+#serial 5
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+  # Autoconf 2.62 quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  case $CONFIG_FILES in
+  *\'*) eval set x "$CONFIG_FILES" ;;
+  *)   set x $CONFIG_FILES ;;
+  esac
+  shift
+  for mf
+  do
+    # Strip MF so we end up with the name of the file.
+    mf=`echo "$mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile or not.
+    # We used to match only the files named `Makefile.in', but
+    # some people rename them; so instead we look at the file content.
+    # Grep'ing the first line is not enough: some people post-process
+    # each Makefile.in and add a new line on top of each file to say so.
+    # Grep'ing the whole file is not good either: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+      dirpart=`AS_DIRNAME("$mf")`
+    else
+      continue
+    fi
+    # Extract the definition of DEPDIR, am__include, and am__quote
+    # from the Makefile without running `make'.
+    DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+    test -z "$DEPDIR" && continue
+    am__include=`sed -n 's/^am__include = //p' < "$mf"`
+    test -z "am__include" && continue
+    am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+    # When using ansi2knr, U may be empty or an underscore; expand it
+    U=`sed -n 's/^U = //p' < "$mf"`
+    # Find all dependency output files, they are included files with
+    # $(DEPDIR) in their names.  We invoke sed twice because it is the
+    # simplest approach to changing $(DEPDIR) to its actual value in the
+    # expansion.
+    for file in `sed -n "
+      s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+        sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+      # Make sure the directory exists.
+      test -f "$dirpart/$file" && continue
+      fdir=`AS_DIRNAME(["$file"])`
+      AS_MKDIR_P([$dirpart/$fdir])
+      # echo "creating $dirpart/$file"
+      echo '# dummy' > "$dirpart/$file"
+    done
+  done
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled.  FIXME.  This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+     [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+     [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 8
+
+# AM_CONFIG_HEADER is obsolete.  It has been replaced by AC_CONFIG_HEADERS.
+AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)])
+
+# Do all the work for Automake.                             -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 16
+
+# This macro actually does too much.  Some checks are only needed if
+# your package does certain things.  But this isn't really a big deal.
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out.  PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition.  After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.62])dnl
+dnl Autoconf wants to disallow AM_ names.  We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+  [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+             [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+                            [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+                 [_AM_DEPENDENCIES(CC)],
+                 [define([AC_PROG_CC],
+                         defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+                 [_AM_DEPENDENCIES(CXX)],
+                 [define([AC_PROG_CXX],
+                         defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+                 [_AM_DEPENDENCIES(OBJC)],
+                 [define([AC_PROG_OBJC],
+                         defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
+dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
+dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen.  This macro
+dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+  [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+])
+
+dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion.  Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated.  The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001, 2003, 2005, 2008, 2011 Free Software Foundation,
+# Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 1
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\    *)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+AC_SUBST(install_sh)])
+
+# Copyright (C) 2003, 2005  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot.  For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_PROG_LEX
+# -----------
+# Autoconf leaves LEX=: if lex or flex can't be found.  Change that to a
+# "missing" invocation, for better error output.
+AC_DEFUN([AM_PROG_LEX],
+[AC_PREREQ(2.50)dnl
+AC_REQUIRE([AM_MISSING_HAS_RUN])dnl
+AC_REQUIRE([AC_PROG_LEX])dnl
+if test "$LEX" = :; then
+  LEX=${am_missing_run}flex
+fi])
+
+# Add --enable-maintainer-mode option to configure.         -*- Autoconf -*-
+# From Jim Meyering
+
+# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008,
+# 2011 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_MAINTAINER_MODE([DEFAULT-MODE])
+# ----------------------------------
+# Control maintainer-specific portions of Makefiles.
+# Default is to disable them, unless `enable' is passed literally.
+# For symmetry, `disable' may be passed as well.  Anyway, the user
+# can override the default with the --enable/--disable switch.
+AC_DEFUN([AM_MAINTAINER_MODE],
+[m4_case(m4_default([$1], [disable]),
+       [enable], [m4_define([am_maintainer_other], [disable])],
+       [disable], [m4_define([am_maintainer_other], [enable])],
+       [m4_define([am_maintainer_other], [enable])
+        m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
+AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
+  dnl maintainer-mode's default is 'disable' unless 'enable' is passed
+  AC_ARG_ENABLE([maintainer-mode],
+[  --][am_maintainer_other][-maintainer-mode  am_maintainer_other make rules and dependencies not useful
+                         (and sometimes confusing) to the casual installer],
+      [USE_MAINTAINER_MODE=$enableval],
+      [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
+  AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+  AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+  MAINT=$MAINTAINER_MODE_TRUE
+  AC_SUBST([MAINT])dnl
+]
+)
+
+AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
+
+# Check to see how 'make' treats includes.                 -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2009  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+       @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+  am__include=include
+  am__quote=
+  _am_result=GNU
+  ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   case `$am_make -s -f confmf 2> /dev/null` in #(
+   *the\ am__doit\ target*)
+     am__include=.include
+     am__quote="\""
+     _am_result=BSD
+     ;;
+   esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 6
+
+# AM_PROG_CC_C_O
+# --------------
+# Like AC_PROG_CC_C_O, but changed for automake.
+AC_DEFUN([AM_PROG_CC_C_O],
+[AC_REQUIRE([AC_PROG_CC_C_O])dnl
+AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+# FIXME: we rely on the cache variable name because
+# there is no other way.
+set dummy $CC
+am_cc=`echo $[2] | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']`
+eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
+if test "$am_t" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+dnl Make sure AC_PROG_CC is never called again, or it will override our
+dnl setting of CC.
+m4_define([AC_PROG_CC],
+          [m4_fatal([AC_PROG_CC cannot be called after AM_PROG_CC_C_O])])
+])
+
+# Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
+
+# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 6
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\    *)
+    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+  *)
+    MISSING="\${SHELL} $am_aux_dir/missing" ;;
+  esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+# Copyright (C) 2003, 2004, 2005, 2006, 2011 Free Software Foundation,
+# Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 1
+
+# AM_PROG_MKDIR_P
+# ---------------
+# Check for `mkdir -p'.
+AC_DEFUN([AM_PROG_MKDIR_P],
+[AC_PREREQ([2.60])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+dnl Automake 1.8 to 1.9.6 used to define mkdir_p.  We now use MKDIR_P,
+dnl while keeping a definition of mkdir_p for backward compatibility.
+dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
+dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
+dnl Makefile.ins that do not define MKDIR_P, so we do our own
+dnl adjustment using top_builddir (which is defined more often than
+dnl MKDIR_P).
+AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
+case $mkdir_p in
+  [[\\/$]]* | ?:[[\\/]]*) ;;
+  */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+])
+
+# Helper functions for option handling.                     -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2008, 2010 Free Software
+# Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# --------------------
+# Set option NAME.  Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Check to make sure that the build environment is sane.    -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[[\\\"\#\$\&\'\`$am_lf]]*)
+    AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+  *[[\\\"\#\$\&\'\`$am_lf\ \   ]]*)
+    AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+   if test "$[*]" = "X"; then
+      # -L didn't work.
+      set X `ls -t "$srcdir/configure" conftest.file`
+   fi
+   rm -f conftest.file
+   if test "$[*]" != "X $srcdir/configure conftest.file" \
+      && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+alias in your environment])
+   fi
+
+   test "$[2]" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+# Copyright (C) 2009, 2011  Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# AM_SILENT_RULES([DEFAULT])
+# --------------------------
+# Enable less verbose build rules; with the default set to DEFAULT
+# (`yes' being less verbose, `no' or empty being verbose).
+AC_DEFUN([AM_SILENT_RULES],
+[AC_ARG_ENABLE([silent-rules],
+[  --enable-silent-rules          less verbose build output (undo: `make V=1')
+  --disable-silent-rules         verbose build output (undo: `make V=0')])
+case $enable_silent_rules in
+yes) AM_DEFAULT_VERBOSITY=0;;
+no)  AM_DEFAULT_VERBOSITY=1;;
+*)   AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+dnl
+dnl A few `make' implementations (e.g., NonStop OS and NextStep)
+dnl do not support nested variable expansions.
+dnl See automake bug#9928 and bug#10237.
+am_make=${MAKE-make}
+AC_CACHE_CHECK([whether $am_make supports nested variables],
+   [am_cv_make_support_nested_variables],
+   [if AS_ECHO([['TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+       @$(TRUE)
+.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi])
+if test $am_cv_make_support_nested_variables = yes; then
+  dnl Using `$V' instead of `$(V)' breaks IRIX make.
+  AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AC_SUBST([AM_V])dnl
+AM_SUBST_NOTMAKE([AM_V])dnl
+AC_SUBST([AM_DEFAULT_V])dnl
+AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+
+# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 1
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries.  This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'.  However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+  AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006, 2008, 2010 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 3
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# --------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball.                            -*- Autoconf -*-
+
+# Copyright (C) 2004, 2005, 2012 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of `v7', `ustar', or `pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+#     tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+#     $(am__untar) < result.tar
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AC_SUBST([AMTAR], ['$${TAR-tar}'])
+m4_if([$1], [v7],
+     [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
+     [m4_case([$1], [ustar],, [pax],,
+              [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+  case $_am_tool in
+  gnutar)
+    for _am_tar in tar gnutar gtar;
+    do
+      AM_RUN_LOG([$_am_tar --version]) && break
+    done
+    am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+    am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+    am__untar="$_am_tar -xf -"
+    ;;
+  plaintar)
+    # Must skip GNU tar: if it does not support --format= it doesn't create
+    # ustar tarball either.
+    (tar --version) >/dev/null 2>&1 && continue
+    am__tar='tar chf - "$$tardir"'
+    am__tar_='tar chf - "$tardir"'
+    am__untar='tar xf -'
+    ;;
+  pax)
+    am__tar='pax -L -x $1 -w "$$tardir"'
+    am__tar_='pax -L -x $1 -w "$tardir"'
+    am__untar='pax -r'
+    ;;
+  cpio)
+    am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+    am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+    am__untar='cpio -i -H $1 -d'
+    ;;
+  none)
+    am__tar=false
+    am__tar_=false
+    am__untar=false
+    ;;
+  esac
+
+  # If the value was cached, stop now.  We just wanted to have am__tar
+  # and am__untar set.
+  test -n "${am_cv_prog_tar_$1}" && break
+
+  # tar/untar a dummy directory, and stop if the command works
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  echo GrepMe > conftest.dir/file
+  AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+  rm -rf conftest.dir
+  if test -s conftest.tar; then
+    AM_RUN_LOG([$am__untar <conftest.tar])
+    grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+  fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
+m4_include([acinclude.m4])
diff --git a/alert/main.c b/alert/main.c
new file mode 100644 (file)
index 0000000..ec4ab6d
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <glib.h>
+#include <errno.h>
+
+#include "plugin.h"
+#include "hcid.h"
+#include "log.h"
+#include "server.h"
+
+static int alert_init(void)
+{
+       if (!main_opts.gatt_enabled) {
+               DBG("GATT is disabled");
+               return -ENOTSUP;
+       }
+
+       return alert_server_init();
+}
+
+static void alert_exit(void)
+{
+       if (!main_opts.gatt_enabled)
+               return;
+
+       alert_server_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(alert, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                       alert_init, alert_exit)
diff --git a/alert/server.c b/alert/server.c
new file mode 100644 (file)
index 0000000..d91b156
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "server.h"
+
+int alert_server_init(void)
+{
+       return 0;
+}
+
+void alert_server_exit(void)
+{
+}
diff --git a/alert/server.h b/alert/server.h
new file mode 100644 (file)
index 0000000..e59bdb1
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int alert_server_init(void);
+void alert_server_exit(void);
diff --git a/attrib/att-database.h b/attrib/att-database.h
new file mode 100644 (file)
index 0000000..3e854aa
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/* Requirements for read/write operations */
+enum {
+       ATT_NONE,               /* No restrictions */
+       ATT_AUTHENTICATION,     /* Authentication required */
+       ATT_AUTHORIZATION,      /* Authorization required */
+       ATT_NOT_PERMITTED,      /* Operation not permitted */
+};
+
+struct attribute {
+       uint16_t handle;
+       bt_uuid_t uuid;
+       int read_reqs;
+       int write_reqs;
+       uint8_t (*read_cb)(struct attribute *a, struct btd_device *device,
+                                                       gpointer user_data);
+       uint8_t (*write_cb)(struct attribute *a, struct btd_device *device,
+                                                       gpointer user_data);
+       gpointer cb_user_data;
+       int len;
+       uint8_t *data;
+};
diff --git a/attrib/att.c b/attrib/att.c
new file mode 100644 (file)
index 0000000..c8e2e1d
--- /dev/null
@@ -0,0 +1,975 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+
+#include "att.h"
+
+const char *att_ecode2str(uint8_t status)
+{
+       switch (status)  {
+       case ATT_ECODE_INVALID_HANDLE:
+               return "Invalid handle";
+       case ATT_ECODE_READ_NOT_PERM:
+               return "Attribute can't be read";
+       case ATT_ECODE_WRITE_NOT_PERM:
+               return "Attribute can't be written";
+       case ATT_ECODE_INVALID_PDU:
+               return "Attribute PDU was invalid";
+       case ATT_ECODE_AUTHENTICATION:
+               return "Attribute requires authentication before read/write";
+       case ATT_ECODE_REQ_NOT_SUPP:
+               return "Server doesn't support the request received";
+       case ATT_ECODE_INVALID_OFFSET:
+               return "Offset past the end of the attribute";
+       case ATT_ECODE_AUTHORIZATION:
+               return "Attribute requires authorization before read/write";
+       case ATT_ECODE_PREP_QUEUE_FULL:
+               return "Too many prepare writes have been queued";
+       case ATT_ECODE_ATTR_NOT_FOUND:
+               return "No attribute found within the given range";
+       case ATT_ECODE_ATTR_NOT_LONG:
+               return "Attribute can't be read/written using Read Blob Req";
+       case ATT_ECODE_INSUFF_ENCR_KEY_SIZE:
+               return "Encryption Key Size is insufficient";
+       case ATT_ECODE_INVAL_ATTR_VALUE_LEN:
+               return "Attribute value length is invalid";
+       case ATT_ECODE_UNLIKELY:
+               return "Request attribute has encountered an unlikely error";
+       case ATT_ECODE_INSUFF_ENC:
+               return "Encryption required before read/write";
+       case ATT_ECODE_UNSUPP_GRP_TYPE:
+               return "Attribute type is not a supported grouping attribute";
+       case ATT_ECODE_INSUFF_RESOURCES:
+               return "Insufficient Resources to complete the request";
+       case ATT_ECODE_IO:
+               return "Internal application error: I/O";
+       case ATT_ECODE_TIMEOUT:
+               return "A timeout occured";
+       case ATT_ECODE_ABORTED:
+               return "The operation was aborted";
+       default:
+               return "Unexpected error code";
+       }
+}
+
+void att_data_list_free(struct att_data_list *list)
+{
+       if (list == NULL)
+               return;
+
+       if (list->data) {
+               int i;
+               for (i = 0; i < list->num; i++)
+                       g_free(list->data[i]);
+       }
+
+       g_free(list->data);
+       g_free(list);
+}
+
+struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len)
+{
+       struct att_data_list *list;
+       int i;
+
+       list = g_new0(struct att_data_list, 1);
+       list->len = len;
+       list->num = num;
+
+       list->data = g_malloc0(sizeof(uint8_t *) * num);
+
+       for (i = 0; i < num; i++)
+               list->data[i] = g_malloc0(sizeof(uint8_t) * len);
+
+       return list;
+}
+
+uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+                                                       uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
+       uint16_t length;
+
+       if (!uuid)
+               return 0;
+
+       if (uuid->type == BT_UUID16)
+               length = 2;
+       else if (uuid->type == BT_UUID128)
+               length = 16;
+       else
+               return 0;
+
+       if (len < min_len + length)
+               return 0;
+
+       pdu[0] = ATT_OP_READ_BY_GROUP_REQ;
+       att_put_u16(start, &pdu[1]);
+       att_put_u16(end, &pdu[3]);
+
+       att_put_uuid(*uuid, &pdu[5]);
+
+       return min_len + length;
+}
+
+uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start,
+                                               uint16_t *end, bt_uuid_t *uuid)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (start == NULL || end == NULL || uuid == NULL)
+               return 0;
+
+       if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ)
+               return 0;
+
+       if (len < min_len + 2)
+               return 0;
+
+       *start = att_get_u16(&pdu[1]);
+       *end = att_get_u16(&pdu[3]);
+       if (len == min_len + 2)
+               *uuid = att_get_uuid16(&pdu[5]);
+       else
+               *uuid = att_get_uuid128(&pdu[5]);
+
+       return len;
+}
+
+uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu,
+                                                               int len)
+{
+       int i;
+       uint16_t w;
+       uint8_t *ptr;
+
+       if (list == NULL)
+               return 0;
+
+       if (len < list->len + 2)
+               return 0;
+
+       pdu[0] = ATT_OP_READ_BY_GROUP_RESP;
+       pdu[1] = list->len;
+
+       ptr = &pdu[2];
+
+       for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
+               memcpy(ptr, list->data[i], list->len);
+               ptr += list->len;
+               w += list->len;
+       }
+
+       return w;
+}
+
+struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, int len)
+{
+       struct att_data_list *list;
+       const uint8_t *ptr;
+       uint16_t elen, num;
+       int i;
+
+       if (pdu[0] != ATT_OP_READ_BY_GROUP_RESP)
+               return NULL;
+
+       elen = pdu[1];
+       num = (len - 2) / elen;
+       list = att_data_list_alloc(num, elen);
+
+       ptr = &pdu[2];
+
+       for (i = 0; i < num; i++) {
+               memcpy(list->data[i], ptr, list->len);
+               ptr += list->len;
+       }
+
+       return list;
+}
+
+uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+                       const uint8_t *value, int vlen, uint8_t *pdu, int len)
+{
+       uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end) +
+                                                       sizeof(uint16_t);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (!uuid)
+               return 0;
+
+       if (uuid->type != BT_UUID16)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (vlen > len - min_len)
+               vlen = len - min_len;
+
+       pdu[0] = ATT_OP_FIND_BY_TYPE_REQ;
+       att_put_u16(start, &pdu[1]);
+       att_put_u16(end, &pdu[3]);
+       att_put_uuid16(*uuid, &pdu[5]);
+
+       if (vlen > 0) {
+               memcpy(&pdu[7], value, vlen);
+               return min_len + vlen;
+       }
+
+       return min_len;
+}
+
+uint16_t dec_find_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
+               uint16_t *end, bt_uuid_t *uuid, uint8_t *value, int *vlen)
+{
+       int valuelen;
+       uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) +
+                                               sizeof(*end) + sizeof(uint16_t);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (pdu[0] != ATT_OP_FIND_BY_TYPE_REQ)
+               return 0;
+
+       /* First requested handle number */
+       if (start)
+               *start = att_get_u16(&pdu[1]);
+
+       /* Last requested handle number */
+       if (end)
+               *end = att_get_u16(&pdu[3]);
+
+       /* Always UUID16 */
+       if (uuid)
+               *uuid = att_get_uuid16(&pdu[5]);
+
+       valuelen = len - min_len;
+
+       /* Attribute value to find */
+       if (valuelen > 0 && value)
+               memcpy(value, pdu + min_len, valuelen);
+
+       if (vlen)
+               *vlen = valuelen;
+
+       return len;
+}
+
+uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, int len)
+{
+       GSList *l;
+       uint16_t offset;
+
+       if (pdu == NULL || len < 5)
+               return 0;
+
+       pdu[0] = ATT_OP_FIND_BY_TYPE_RESP;
+
+       for (l = matches, offset = 1; l && len >= (offset + 4);
+                                       l = l->next, offset += 4) {
+               struct att_range *range = l->data;
+
+               att_put_u16(range->start, &pdu[offset]);
+               att_put_u16(range->end, &pdu[offset + 2]);
+       }
+
+       return offset;
+}
+
+GSList *dec_find_by_type_resp(const uint8_t *pdu, int len)
+{
+       struct att_range *range;
+       GSList *matches;
+       int offset;
+
+       if (pdu == NULL || len < 5)
+               return NULL;
+
+       if (pdu[0] != ATT_OP_FIND_BY_TYPE_RESP)
+               return NULL;
+
+       for (offset = 1, matches = NULL; len >= (offset + 4); offset += 4) {
+               range = g_new0(struct att_range, 1);
+               range->start = att_get_u16(&pdu[offset]);
+               range->end = att_get_u16(&pdu[offset + 2]);
+
+               matches = g_slist_append(matches, range);
+       }
+
+       return matches;
+}
+
+uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+                                                       uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
+       uint16_t length;
+
+       if (!uuid)
+               return 0;
+
+       if (uuid->type == BT_UUID16)
+               length = 2;
+       else if (uuid->type == BT_UUID128)
+               length = 16;
+       else
+               return 0;
+
+       if (len < min_len + length)
+               return 0;
+
+       pdu[0] = ATT_OP_READ_BY_TYPE_REQ;
+       att_put_u16(start, &pdu[1]);
+       att_put_u16(end, &pdu[3]);
+
+       att_put_uuid(*uuid, &pdu[5]);
+
+       return min_len + length;
+}
+
+uint16_t dec_read_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
+                                               uint16_t *end, bt_uuid_t *uuid)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (start == NULL || end == NULL || uuid == NULL)
+               return 0;
+
+       if (len < min_len + 2)
+               return 0;
+
+       if (pdu[0] != ATT_OP_READ_BY_TYPE_REQ)
+               return 0;
+
+       *start = att_get_u16(&pdu[1]);
+       *end = att_get_u16(&pdu[3]);
+
+       if (len == min_len + 2)
+               *uuid = att_get_uuid16(&pdu[5]);
+       else
+               *uuid = att_get_uuid128(&pdu[5]);
+
+       return len;
+}
+
+uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu, int len)
+{
+       uint8_t *ptr;
+       int i, w, l;
+
+       if (list == NULL)
+               return 0;
+
+       if (pdu == NULL)
+               return 0;
+
+       l = MIN(len - 2, list->len);
+
+       pdu[0] = ATT_OP_READ_BY_TYPE_RESP;
+       pdu[1] = l;
+       ptr = &pdu[2];
+
+       for (i = 0, w = 2; i < list->num && w + l <= len; i++) {
+               memcpy(ptr, list->data[i], l);
+               ptr += l;
+               w += l;
+       }
+
+       return w;
+}
+
+struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, int len)
+{
+       struct att_data_list *list;
+       const uint8_t *ptr;
+       uint16_t elen, num;
+       int i;
+
+       if (pdu[0] != ATT_OP_READ_BY_TYPE_RESP)
+               return NULL;
+
+       elen = pdu[1];
+       num = (len - 2) / elen;
+       list = att_data_list_alloc(num, elen);
+
+       ptr = &pdu[2];
+
+       for (i = 0; i < num; i++) {
+               memcpy(list->data[i], ptr, list->len);
+               ptr += list->len;
+       }
+
+       return list;
+}
+
+uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, int vlen,
+                                                       uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (vlen > len - min_len)
+               vlen = len - min_len;
+
+       pdu[0] = ATT_OP_WRITE_CMD;
+       att_put_u16(handle, &pdu[1]);
+
+       if (vlen > 0) {
+               memcpy(&pdu[3], value, vlen);
+               return min_len + vlen;
+       }
+
+       return min_len;
+}
+
+uint16_t dec_write_cmd(const uint8_t *pdu, int len, uint16_t *handle,
+                                               uint8_t *value, int *vlen)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (value == NULL || vlen == NULL || handle == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (pdu[0] != ATT_OP_WRITE_CMD)
+               return 0;
+
+       *handle = att_get_u16(&pdu[1]);
+       memcpy(value, pdu + min_len, len - min_len);
+       *vlen = len - min_len;
+
+       return len;
+}
+
+uint16_t enc_write_req(uint16_t handle, const uint8_t *value, int vlen,
+                                                       uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (vlen > len - min_len)
+               vlen = len - min_len;
+
+       pdu[0] = ATT_OP_WRITE_REQ;
+       att_put_u16(handle, &pdu[1]);
+
+       if (vlen > 0) {
+               memcpy(&pdu[3], value, vlen);
+               return min_len + vlen;
+       }
+
+       return min_len;
+}
+
+uint16_t dec_write_req(const uint8_t *pdu, int len, uint16_t *handle,
+                                               uint8_t *value, int *vlen)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (value == NULL || vlen == NULL || handle == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (pdu[0] != ATT_OP_WRITE_REQ)
+               return 0;
+
+       *handle = att_get_u16(&pdu[1]);
+       *vlen = len - min_len;
+       if (*vlen > 0)
+               memcpy(value, pdu + min_len, *vlen);
+
+       return len;
+}
+
+uint16_t enc_write_resp(uint8_t *pdu, int len)
+{
+       if (pdu == NULL)
+               return 0;
+
+       pdu[0] = ATT_OP_WRITE_RESP;
+
+       return sizeof(pdu[0]);
+}
+
+uint16_t dec_write_resp(const uint8_t *pdu, int len)
+{
+       if (pdu == NULL)
+               return 0;
+
+       if (pdu[0] != ATT_OP_WRITE_RESP)
+               return 0;
+
+       return len;
+}
+
+uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       pdu[0] = ATT_OP_READ_REQ;
+       att_put_u16(handle, &pdu[1]);
+
+       return min_len;
+}
+
+uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu,
+                                                                       int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) +
+                                                       sizeof(offset);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       pdu[0] = ATT_OP_READ_BLOB_REQ;
+       att_put_u16(handle, &pdu[1]);
+       att_put_u16(offset, &pdu[3]);
+
+       return min_len;
+}
+
+uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (handle == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (pdu[0] != ATT_OP_READ_REQ)
+               return 0;
+
+       *handle = att_get_u16(&pdu[1]);
+
+       return min_len;
+}
+
+uint16_t dec_read_blob_req(const uint8_t *pdu, int len, uint16_t *handle,
+                                                       uint16_t *offset)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) +
+                                                       sizeof(*offset);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (handle == NULL)
+               return 0;
+
+       if (offset == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (pdu[0] != ATT_OP_READ_BLOB_REQ)
+               return 0;
+
+       *handle = att_get_u16(&pdu[1]);
+       *offset = att_get_u16(&pdu[3]);
+
+       return min_len;
+}
+
+uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len)
+{
+       if (pdu == NULL)
+               return 0;
+
+       /* If the attribute value length is longer than the allowed PDU size,
+        * send only the octets that fit on the PDU. The remaining octets can
+        * be requested using the Read Blob Request. */
+       if (vlen > len - 1)
+               vlen = len - 1;
+
+       pdu[0] = ATT_OP_READ_RESP;
+
+       memcpy(pdu + 1, value, vlen);
+
+       return vlen + 1;
+}
+
+uint16_t enc_read_blob_resp(uint8_t *value, int vlen, uint16_t offset,
+                                                       uint8_t *pdu, int len)
+{
+       if (pdu == NULL)
+               return 0;
+
+       vlen -= offset;
+       if (vlen > len - 1)
+               vlen = len - 1;
+
+       pdu[0] = ATT_OP_READ_BLOB_RESP;
+
+       memcpy(pdu + 1, &value[offset], vlen);
+
+       return vlen + 1;
+}
+
+uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen)
+{
+       if (pdu == NULL)
+               return 0;
+
+       if (value == NULL || vlen == NULL)
+               return 0;
+
+       if (pdu[0] != ATT_OP_READ_RESP)
+               return 0;
+
+       memcpy(value, pdu + 1, len - 1);
+
+       *vlen = len - 1;
+
+       return len;
+}
+
+uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
+                                                       uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(opcode) +
+                                               sizeof(handle) + sizeof(status);
+       uint16_t u16;
+
+       if (len < min_len)
+               return 0;
+
+       u16 = htobs(handle);
+       pdu[0] = ATT_OP_ERROR;
+       pdu[1] = opcode;
+       memcpy(&pdu[2], &u16, sizeof(u16));
+       pdu[4] = status;
+
+       return min_len;
+}
+
+uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       pdu[0] = ATT_OP_FIND_INFO_REQ;
+       att_put_u16(start, &pdu[1]);
+       att_put_u16(end, &pdu[3]);
+
+       return min_len;
+}
+
+uint16_t dec_find_info_req(const uint8_t *pdu, int len, uint16_t *start,
+                                                               uint16_t *end)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (start == NULL || end == NULL)
+               return 0;
+
+       if (pdu[0] != ATT_OP_FIND_INFO_REQ)
+               return 0;
+
+       *start = att_get_u16(&pdu[1]);
+       *end = att_get_u16(&pdu[3]);
+
+       return min_len;
+}
+
+uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list,
+                                                       uint8_t *pdu, int len)
+{
+       uint8_t *ptr;
+       int i, w;
+
+       if (pdu == NULL)
+               return 0;
+
+       if (list == NULL)
+               return 0;
+
+       if (len < list->len + 2)
+               return 0;
+
+       pdu[0] = ATT_OP_FIND_INFO_RESP;
+       pdu[1] = format;
+       ptr = (void *) &pdu[2];
+
+       for (i = 0, w = 2; i < list->num && w + list->len <= len; i++) {
+               memcpy(ptr, list->data[i], list->len);
+               ptr += list->len;
+               w += list->len;
+       }
+
+       return w;
+}
+
+struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len,
+                                                       uint8_t *format)
+{
+       struct att_data_list *list;
+       uint8_t *ptr;
+       uint16_t elen, num;
+       int i;
+
+       if (pdu == NULL)
+               return 0;
+
+       if (format == NULL)
+               return 0;
+
+       if (pdu[0] != ATT_OP_FIND_INFO_RESP)
+               return 0;
+
+       *format = pdu[1];
+       elen = sizeof(pdu[0]) + sizeof(*format);
+       if (*format == 0x01)
+               elen += 2;
+       else if (*format == 0x02)
+               elen += 16;
+
+       num = (len - 2) / elen;
+
+       ptr = (void *) &pdu[2];
+
+       list = att_data_list_alloc(num, elen);
+
+       for (i = 0; i < num; i++) {
+               memcpy(list->data[i], ptr, list->len);
+               ptr += list->len;
+       }
+
+       return list;
+}
+
+uint16_t enc_notification(uint16_t handle, uint8_t *value, int vlen,
+                                               uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < (vlen + min_len))
+               return 0;
+
+       pdu[0] = ATT_OP_HANDLE_NOTIFY;
+       att_put_u16(handle, &pdu[1]);
+       memcpy(&pdu[3], value, vlen);
+
+       return vlen + min_len;
+}
+
+uint16_t enc_indication(uint16_t handle, uint8_t *value, int vlen,
+                                               uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < (vlen + min_len))
+               return 0;
+
+       pdu[0] = ATT_OP_HANDLE_IND;
+       att_put_u16(handle, &pdu[1]);
+       memcpy(&pdu[3], value, vlen);
+
+       return vlen + min_len;
+}
+
+uint16_t dec_indication(const uint8_t *pdu, int len, uint16_t *handle,
+                                               uint8_t *value, int vlen)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+       uint16_t dlen;
+
+       if (pdu == NULL)
+               return 0;
+
+       if (pdu[0] != ATT_OP_HANDLE_IND)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       dlen = MIN(len - min_len, vlen);
+
+       if (handle)
+               *handle = att_get_u16(&pdu[1]);
+
+       memcpy(value, &pdu[3], dlen);
+
+       return dlen;
+}
+
+uint16_t enc_confirmation(uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       pdu[0] = ATT_OP_HANDLE_CNF;
+
+       return min_len;
+}
+
+uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       pdu[0] = ATT_OP_MTU_REQ;
+       att_put_u16(mtu, &pdu[1]);
+
+       return min_len;
+}
+
+uint16_t dec_mtu_req(const uint8_t *pdu, int len, uint16_t *mtu)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (mtu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (pdu[0] != ATT_OP_MTU_REQ)
+               return 0;
+
+       *mtu = att_get_u16(&pdu[1]);
+
+       return min_len;
+}
+
+uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, int len)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       pdu[0] = ATT_OP_MTU_RESP;
+       att_put_u16(mtu, &pdu[1]);
+
+       return min_len;
+}
+
+uint16_t dec_mtu_resp(const uint8_t *pdu, int len, uint16_t *mtu)
+{
+       const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu);
+
+       if (pdu == NULL)
+               return 0;
+
+       if (mtu == NULL)
+               return 0;
+
+       if (len < min_len)
+               return 0;
+
+       if (pdu[0] != ATT_OP_MTU_RESP)
+               return 0;
+
+       *mtu = att_get_u16(&pdu[1]);
+
+       return min_len;
+}
diff --git a/attrib/att.h b/attrib/att.h
new file mode 100644 (file)
index 0000000..144513f
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/* Attribute Protocol Opcodes */
+#define ATT_OP_ERROR                   0x01
+#define ATT_OP_MTU_REQ                 0x02
+#define ATT_OP_MTU_RESP                        0x03
+#define ATT_OP_FIND_INFO_REQ           0x04
+#define ATT_OP_FIND_INFO_RESP          0x05
+#define ATT_OP_FIND_BY_TYPE_REQ                0x06
+#define ATT_OP_FIND_BY_TYPE_RESP       0x07
+#define ATT_OP_READ_BY_TYPE_REQ                0x08
+#define ATT_OP_READ_BY_TYPE_RESP       0x09
+#define ATT_OP_READ_REQ                        0x0A
+#define ATT_OP_READ_RESP               0x0B
+#define ATT_OP_READ_BLOB_REQ           0x0C
+#define ATT_OP_READ_BLOB_RESP          0x0D
+#define ATT_OP_READ_MULTI_REQ          0x0E
+#define ATT_OP_READ_MULTI_RESP         0x0F
+#define ATT_OP_READ_BY_GROUP_REQ       0x10
+#define ATT_OP_READ_BY_GROUP_RESP      0x11
+#define ATT_OP_WRITE_REQ               0x12
+#define ATT_OP_WRITE_RESP              0x13
+#define ATT_OP_WRITE_CMD               0x52
+#define ATT_OP_PREP_WRITE_REQ          0x16
+#define ATT_OP_PREP_WRITE_RESP         0x17
+#define ATT_OP_EXEC_WRITE_REQ          0x18
+#define ATT_OP_EXEC_WRITE_RESP         0x19
+#define ATT_OP_HANDLE_NOTIFY           0x1B
+#define ATT_OP_HANDLE_IND              0x1D
+#define ATT_OP_HANDLE_CNF              0x1E
+#define ATT_OP_SIGNED_WRITE_CMD                0xD2
+
+/* Error codes for Error response PDU */
+#define ATT_ECODE_INVALID_HANDLE               0x01
+#define ATT_ECODE_READ_NOT_PERM                        0x02
+#define ATT_ECODE_WRITE_NOT_PERM               0x03
+#define ATT_ECODE_INVALID_PDU                  0x04
+#define ATT_ECODE_AUTHENTICATION               0x05
+#define ATT_ECODE_REQ_NOT_SUPP                 0x06
+#define ATT_ECODE_INVALID_OFFSET               0x07
+#define ATT_ECODE_AUTHORIZATION                        0x08
+#define ATT_ECODE_PREP_QUEUE_FULL              0x09
+#define ATT_ECODE_ATTR_NOT_FOUND               0x0A
+#define ATT_ECODE_ATTR_NOT_LONG                        0x0B
+#define ATT_ECODE_INSUFF_ENCR_KEY_SIZE         0x0C
+#define ATT_ECODE_INVAL_ATTR_VALUE_LEN         0x0D
+#define ATT_ECODE_UNLIKELY                     0x0E
+#define ATT_ECODE_INSUFF_ENC                   0x0F
+#define ATT_ECODE_UNSUPP_GRP_TYPE              0x10
+#define ATT_ECODE_INSUFF_RESOURCES             0x11
+/* Application error */
+#define ATT_ECODE_IO                           0x80
+#define ATT_ECODE_TIMEOUT                      0x81
+#define ATT_ECODE_ABORTED                      0x82
+
+/* Characteristic Property bit field */
+#define ATT_CHAR_PROPER_BROADCAST              0x01
+#define ATT_CHAR_PROPER_READ                   0x02
+#define ATT_CHAR_PROPER_WRITE_WITHOUT_RESP     0x04
+#define ATT_CHAR_PROPER_WRITE                  0x08
+#define ATT_CHAR_PROPER_NOTIFY                 0x10
+#define ATT_CHAR_PROPER_INDICATE               0x20
+#define ATT_CHAR_PROPER_AUTH                   0x40
+#define ATT_CHAR_PROPER_EXT_PROPER             0x80
+
+#define ATT_MAX_MTU                            256
+#define ATT_DEFAULT_L2CAP_MTU                  48
+#define ATT_DEFAULT_LE_MTU                     23
+
+#define ATT_CID                                        4
+#define ATT_PSM                                        31
+
+struct att_data_list {
+       uint16_t num;
+       uint16_t len;
+       uint8_t **data;
+};
+
+struct att_range {
+       uint16_t start;
+       uint16_t end;
+};
+
+/* These functions do byte conversion */
+static inline uint8_t att_get_u8(const void *ptr)
+{
+       const uint8_t *u8_ptr = ptr;
+       return bt_get_unaligned(u8_ptr);
+}
+
+static inline uint16_t att_get_u16(const void *ptr)
+{
+       const uint16_t *u16_ptr = ptr;
+       return btohs(bt_get_unaligned(u16_ptr));
+}
+
+static inline uint32_t att_get_u32(const void *ptr)
+{
+       const uint32_t *u32_ptr = ptr;
+       return btohl(bt_get_unaligned(u32_ptr));
+}
+
+static inline uint128_t att_get_u128(const void *ptr)
+{
+       const uint128_t *u128_ptr = ptr;
+       uint128_t dst;
+
+       btoh128(u128_ptr, &dst);
+
+       return dst;
+}
+
+static inline void att_put_u8(uint8_t src, void *dst)
+{
+       bt_put_unaligned(src, (uint8_t *) dst);
+}
+
+static inline void att_put_u16(uint16_t src, void *dst)
+{
+       bt_put_unaligned(htobs(src), (uint16_t *) dst);
+}
+
+static inline void att_put_u32(uint32_t src, void *dst)
+{
+       bt_put_unaligned(htobl(src), (uint32_t *) dst);
+}
+
+static inline void att_put_u128(uint128_t src, void *dst)
+{
+       uint128_t *d128 = dst;
+
+       htob128(&src, d128);
+}
+
+static inline void att_put_uuid16(bt_uuid_t src, void *dst)
+{
+       att_put_u16(src.value.u16, dst);
+}
+
+static inline void att_put_uuid128(bt_uuid_t src, void *dst)
+{
+       att_put_u128(src.value.u128, dst);
+}
+
+static inline void att_put_uuid(bt_uuid_t src, void *dst)
+{
+       if (src.type == BT_UUID16)
+               att_put_uuid16(src, dst);
+       else
+               att_put_uuid128(src, dst);
+}
+
+static inline bt_uuid_t att_get_uuid16(const void *ptr)
+{
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, att_get_u16(ptr));
+
+       return uuid;
+}
+
+static inline bt_uuid_t att_get_uuid128(const void *ptr)
+{
+       bt_uuid_t uuid;
+       uint128_t value;
+
+       value  = att_get_u128(ptr);
+       bt_uuid128_create(&uuid, value);
+
+       return uuid;
+}
+
+struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len);
+void att_data_list_free(struct att_data_list *list);
+
+const char *att_ecode2str(uint8_t status);
+uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+                                                       uint8_t *pdu, int len);
+uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start,
+                                               uint16_t *end, bt_uuid_t *uuid);
+uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu, int len);
+uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+                       const uint8_t *value, int vlen, uint8_t *pdu, int len);
+uint16_t dec_find_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
+               uint16_t *end, bt_uuid_t *uuid, uint8_t *value, int *vlen);
+uint16_t enc_find_by_type_resp(GSList *ranges, uint8_t *pdu, int len);
+GSList *dec_find_by_type_resp(const uint8_t *pdu, int len);
+struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, int len);
+uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
+                                                       uint8_t *pdu, int len);
+uint16_t dec_read_by_type_req(const uint8_t *pdu, int len, uint16_t *start,
+                                               uint16_t *end, bt_uuid_t *uuid);
+uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu,
+                                                               int len);
+uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, int vlen,
+                                                       uint8_t *pdu, int len);
+uint16_t dec_write_cmd(const uint8_t *pdu, int len, uint16_t *handle,
+                                               uint8_t *value, int *vlen);
+struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, int len);
+uint16_t enc_write_req(uint16_t handle, const uint8_t *value, int vlen,
+                                                       uint8_t *pdu, int len);
+uint16_t dec_write_req(const uint8_t *pdu, int len, uint16_t *handle,
+                                               uint8_t *value, int *vlen);
+uint16_t enc_write_resp(uint8_t *pdu, int len);
+uint16_t dec_write_resp(const uint8_t *pdu, int len);
+uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len);
+uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu,
+                                                               int len);
+uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle);
+uint16_t dec_read_blob_req(const uint8_t *pdu, int len, uint16_t *handle,
+                                                       uint16_t *offset);
+uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len);
+uint16_t enc_read_blob_resp(uint8_t *value, int vlen, uint16_t offset,
+                                                       uint8_t *pdu, int len);
+uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen);
+uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
+                                                       uint8_t *pdu, int len);
+uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, int len);
+uint16_t dec_find_info_req(const uint8_t *pdu, int len, uint16_t *start,
+                                                               uint16_t *end);
+uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list,
+                                                       uint8_t *pdu, int len);
+struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len,
+                                                       uint8_t *format);
+uint16_t enc_notification(uint16_t handle, uint8_t *value, int vlen,
+                                               uint8_t *pdu, int len);
+uint16_t enc_indication(uint16_t handle, uint8_t *value, int vlen,
+                                               uint8_t *pdu, int len);
+uint16_t dec_indication(const uint8_t *pdu, int len, uint16_t *handle,
+                                               uint8_t *value, int vlen);
+uint16_t enc_confirmation(uint8_t *pdu, int len);
+
+uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len);
+uint16_t dec_mtu_req(const uint8_t *pdu, int len, uint16_t *mtu);
+uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, int len);
+uint16_t dec_mtu_resp(const uint8_t *pdu, int len, uint16_t *mtu);
diff --git a/attrib/client.c b/attrib/client.c
new file mode 100644 (file)
index 0000000..8d119df
--- /dev/null
@@ -0,0 +1,1144 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "log.h"
+#include "gdbus.h"
+#include "error.h"
+#include "dbus-common.h"
+#include "btio.h"
+#include "storage.h"
+
+#include "att.h"
+#include "gattrib.h"
+#include "attio.h"
+#include "gatt.h"
+#include "client.h"
+
+#define CHAR_INTERFACE "org.bluez.Characteristic"
+
+struct format {
+       guint8 format;
+       guint8 exponent;
+       guint16 unit;
+       guint8 namespace;
+       guint16 desc;
+} __attribute__ ((packed));
+
+struct query {
+       DBusMessage *msg;
+       GSList *list;
+};
+
+struct gatt_service {
+       struct btd_device *dev;
+       struct gatt_primary *prim;
+       DBusConnection *conn;
+       GAttrib *attrib;
+       guint attioid;
+       int psm;
+       char *path;
+       GSList *chars;
+       GSList *offline_chars;
+       GSList *watchers;
+       struct query *query;
+};
+
+struct characteristic {
+       struct gatt_service *gatt;
+       char *path;
+       uint16_t handle;
+       uint16_t end;
+       uint8_t perm;
+       char type[MAX_LEN_UUID_STR + 1];
+       char *name;
+       char *desc;
+       struct format *format;
+       uint8_t *value;
+       size_t vlen;
+};
+
+struct query_data {
+       struct gatt_service *gatt;
+       struct characteristic *chr;
+       uint16_t handle;
+};
+
+struct watcher {
+       guint id;
+       char *name;
+       char *path;
+       struct gatt_service *gatt;
+};
+
+static GSList *gatt_services = NULL;
+
+static void characteristic_free(void *user_data)
+{
+       struct characteristic *chr = user_data;
+
+       g_free(chr->path);
+       g_free(chr->desc);
+       g_free(chr->format);
+       g_free(chr->value);
+       g_free(chr->name);
+       g_free(chr);
+}
+
+static void watcher_free(void *user_data)
+{
+       struct watcher *watcher = user_data;
+
+       g_free(watcher->path);
+       g_free(watcher->name);
+       g_free(watcher);
+}
+
+static void gatt_service_free(struct gatt_service *gatt)
+{
+       g_slist_free_full(gatt->watchers, watcher_free);
+       g_slist_free_full(gatt->chars, characteristic_free);
+       g_slist_free(gatt->offline_chars);
+       g_free(gatt->path);
+       btd_device_unref(gatt->dev);
+       dbus_connection_unref(gatt->conn);
+       g_free(gatt);
+}
+
+static void remove_attio(struct gatt_service *gatt)
+{
+       if (gatt->offline_chars || gatt->watchers || gatt->query)
+               return;
+
+       if (gatt->attioid) {
+               btd_device_remove_attio_callback(gatt->dev, gatt->attioid);
+               gatt->attioid = 0;
+       }
+
+       if (gatt->attrib) {
+               g_attrib_unref(gatt->attrib);
+               gatt->attrib = NULL;
+       }
+}
+
+static void gatt_get_address(struct gatt_service *gatt, bdaddr_t *sba,
+                                       bdaddr_t *dba, uint8_t *bdaddr_type)
+{
+       struct btd_device *device = gatt->dev;
+       struct btd_adapter *adapter;
+
+       adapter = device_get_adapter(device);
+       adapter_get_address(adapter, sba);
+       device_get_address(device, dba, bdaddr_type);
+}
+
+static int characteristic_handle_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct characteristic *chr = a;
+       uint16_t handle = GPOINTER_TO_UINT(b);
+
+       return chr->handle - handle;
+}
+
+static int watcher_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct watcher *watcher = a;
+       const struct watcher *match = b;
+       int ret;
+
+       ret = g_strcmp0(watcher->name, match->name);
+       if (ret != 0)
+               return ret;
+
+       return g_strcmp0(watcher->path, match->path);
+}
+
+static void append_char_dict(DBusMessageIter *iter, struct characteristic *chr)
+{
+       DBusMessageIter dict;
+       const char *name = "";
+       char *uuid;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       uuid = g_strdup(chr->type);
+       dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
+       g_free(uuid);
+
+       /* FIXME: Translate UUID to name. */
+       dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &name);
+
+       if (chr->desc)
+               dict_append_entry(&dict, "Description", DBUS_TYPE_STRING,
+                                                               &chr->desc);
+
+       if (chr->value)
+               dict_append_array(&dict, "Value", DBUS_TYPE_BYTE, &chr->value,
+                                                               chr->vlen);
+
+       /* FIXME: Missing Format, Value and Representation */
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void watcher_exit(DBusConnection *conn, void *user_data)
+{
+       struct watcher *watcher = user_data;
+       struct gatt_service *gatt = watcher->gatt;
+
+       DBG("%s watcher %s exited", gatt->path, watcher->name);
+
+       gatt->watchers = g_slist_remove(gatt->watchers, watcher);
+       g_dbus_remove_watch(gatt->conn, watcher->id);
+       remove_attio(gatt);
+}
+
+static int characteristic_set_value(struct characteristic *chr,
+                                       const uint8_t *value, size_t vlen)
+{
+       chr->value = g_try_realloc(chr->value, vlen);
+       if (chr->value == NULL)
+               return -ENOMEM;
+
+       memcpy(chr->value, value, vlen);
+       chr->vlen = vlen;
+
+       return 0;
+}
+
+static void update_watchers(gpointer data, gpointer user_data)
+{
+       struct watcher *w = data;
+       struct characteristic *chr = user_data;
+       DBusConnection *conn = w->gatt->conn;
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(w->name, w->path,
+                               "org.bluez.Watcher", "ValueChanged");
+       if (msg == NULL)
+               return;
+
+       dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &chr->path,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                       &chr->value, chr->vlen, DBUS_TYPE_INVALID);
+
+       dbus_message_set_no_reply(msg, TRUE);
+       g_dbus_send_message(conn, msg);
+}
+
+static void events_handler(const uint8_t *pdu, uint16_t len,
+                                                       gpointer user_data)
+{
+       struct gatt_service *gatt = user_data;
+       struct characteristic *chr;
+       GSList *l;
+       uint8_t opdu[ATT_MAX_MTU];
+       guint handle;
+       uint16_t olen;
+
+       if (len < 3) {
+               DBG("Malformed notification/indication packet (opcode 0x%02x)",
+                                                                       pdu[0]);
+               return;
+       }
+
+       handle = att_get_u16(&pdu[1]);
+
+       l = g_slist_find_custom(gatt->chars, GUINT_TO_POINTER(handle),
+                                               characteristic_handle_cmp);
+       if (!l)
+               return;
+
+       chr = l->data;
+
+       if (chr == NULL) {
+               DBG("Attribute handle 0x%02x not found", handle);
+               return;
+       }
+
+       switch (pdu[0]) {
+       case ATT_OP_HANDLE_IND:
+               olen = enc_confirmation(opdu, sizeof(opdu));
+               g_attrib_send(gatt->attrib, 0, opdu[0], opdu, olen,
+                                               NULL, NULL, NULL);
+       case ATT_OP_HANDLE_NOTIFY:
+               if (characteristic_set_value(chr, &pdu[3], len - 3) < 0)
+                       DBG("Can't change Characteristic 0x%02x", handle);
+
+               g_slist_foreach(gatt->watchers, update_watchers, chr);
+               break;
+       }
+}
+
+static void offline_char_written(gpointer user_data)
+{
+       struct characteristic *chr = user_data;
+       struct gatt_service *gatt = chr->gatt;
+
+       gatt->offline_chars = g_slist_remove(gatt->offline_chars, chr);
+
+       remove_attio(gatt);
+}
+
+static void offline_char_write(gpointer data, gpointer user_data)
+{
+       struct characteristic *chr = data;
+       GAttrib *attrib = user_data;
+
+       gatt_write_cmd(attrib, chr->handle, chr->value, chr->vlen,
+                                               offline_char_written, chr);
+}
+
+static void char_discovered_cb(GSList *characteristics, guint8 status,
+                                                       gpointer user_data);
+
+static void attio_connected(GAttrib *attrib, gpointer user_data)
+{
+       struct gatt_service *gatt = user_data;
+
+       gatt->attrib = g_attrib_ref(attrib);
+
+       g_attrib_register(gatt->attrib, ATT_OP_HANDLE_NOTIFY,
+                                       events_handler, gatt, NULL);
+       g_attrib_register(gatt->attrib, ATT_OP_HANDLE_IND,
+                                       events_handler, gatt, NULL);
+
+       g_slist_foreach(gatt->offline_chars, offline_char_write, attrib);
+
+       if (gatt->query) {
+               struct gatt_primary *prim = gatt->prim;
+               struct query_data *qchr;
+
+               qchr = g_slist_nth_data(gatt->query->list, 0);
+               gatt_discover_char(gatt->attrib, prim->range.start,
+                                               prim->range.end, NULL,
+                                               char_discovered_cb, qchr);
+       }
+}
+
+static void attio_disconnected(gpointer user_data)
+{
+       struct gatt_service *gatt = user_data;
+
+       if (gatt->query && gatt->query->msg) {
+               DBusMessage *reply;
+
+               reply = btd_error_failed(gatt->query->msg,
+                                       "ATT IO channel was disconnected");
+               g_dbus_send_message(gatt->conn, reply);
+               dbus_message_unref(gatt->query->msg);
+       }
+
+       if (gatt->query) {
+               g_slist_free_full(gatt->query->list, g_free);
+               gatt->query = NULL;
+       }
+
+       if (gatt->attrib) {
+               g_attrib_unref(gatt->attrib);
+               gatt->attrib = NULL;
+       }
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct gatt_service *gatt = data;
+       struct watcher *watcher;
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = g_new0(struct watcher, 1);
+       watcher->name = g_strdup(sender);
+       watcher->gatt = gatt;
+       watcher->path = g_strdup(path);
+       watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
+                                                       watcher, watcher_free);
+
+       if (gatt->attioid == 0)
+               gatt->attioid = btd_device_add_attio_callback(gatt->dev,
+                                                       attio_connected,
+                                                       attio_disconnected,
+                                                       gatt);
+
+       gatt->watchers = g_slist_append(gatt->watchers, watcher);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct gatt_service *gatt = data;
+       struct watcher *watcher, *match;
+       GSList *l;
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       match = g_new0(struct watcher, 1);
+       match->name = g_strdup(sender);
+       match->path = g_strdup(path);
+       l = g_slist_find_custom(gatt->watchers, match, watcher_cmp);
+       watcher_free(match);
+       if (!l)
+               return btd_error_not_authorized(msg);
+
+       watcher = l->data;
+       gatt->watchers = g_slist_remove(gatt->watchers, watcher);
+       g_dbus_remove_watch(conn, watcher->id);
+       remove_attio(gatt);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_value(DBusConnection *conn, DBusMessage *msg,
+                       DBusMessageIter *iter, struct characteristic *chr)
+{
+       struct gatt_service *gatt = chr->gatt;
+       DBusMessageIter sub;
+       uint8_t *value;
+       int len;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
+                       dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(iter, &sub);
+
+       dbus_message_iter_get_fixed_array(&sub, &value, &len);
+
+       characteristic_set_value(chr, value, len);
+
+       if (gatt->attioid == 0)
+               gatt->attioid = btd_device_add_attio_callback(gatt->dev,
+                                                       attio_connected,
+                                                       attio_disconnected,
+                                                       gatt);
+
+       if (gatt->attrib)
+               gatt_write_cmd(gatt->attrib, chr->handle, value, len,
+                                                               NULL, NULL);
+       else
+               gatt->offline_chars = g_slist_append(gatt->offline_chars, chr);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct characteristic *chr = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       append_char_dict(&iter, chr);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct characteristic *chr = data;
+       DBusMessageIter iter;
+       DBusMessageIter sub;
+       const char *property;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return btd_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &sub);
+
+       if (g_str_equal("Value", property))
+               return set_value(conn, msg, &sub, chr);
+
+       return btd_error_invalid_args(msg);
+}
+
+static const GDBusMethodTable char_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+                       set_property) },
+       { }
+};
+
+static char *characteristic_list_to_string(GSList *chars)
+{
+       GString *characteristics;
+       GSList *l;
+
+       characteristics = g_string_new(NULL);
+
+       for (l = chars; l; l = l->next) {
+               struct characteristic *chr = l->data;
+               char chr_str[64];
+
+               memset(chr_str, 0, sizeof(chr_str));
+
+               snprintf(chr_str, sizeof(chr_str), "%04X#%02X#%04X#%s ",
+                               chr->handle, chr->perm, chr->end, chr->type);
+
+               characteristics = g_string_append(characteristics, chr_str);
+       }
+
+       return g_string_free(characteristics, FALSE);
+}
+
+static void store_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+                                       uint8_t bdaddr_type, uint16_t start,
+                                                               GSList *chars)
+{
+       char *characteristics;
+
+       characteristics = characteristic_list_to_string(chars);
+
+       write_device_characteristics(sba, dba, bdaddr_type, start,
+                                                       characteristics);
+
+       g_free(characteristics);
+}
+
+static void register_characteristic(gpointer data, gpointer user_data)
+{
+       struct characteristic *chr = data;
+       DBusConnection *conn = chr->gatt->conn;
+       const char *gatt_path = user_data;
+
+       chr->path = g_strdup_printf("%s/characteristic%04x", gatt_path,
+                                                               chr->handle);
+
+       g_dbus_register_interface(conn, chr->path, CHAR_INTERFACE,
+                                       char_methods, NULL, NULL, chr, NULL);
+
+       DBG("Registered: %s", chr->path);
+}
+
+static GSList *string_to_characteristic_list(struct gatt_service *gatt,
+                                                       const char *str)
+{
+       GSList *l = NULL;
+       char **chars;
+       int i;
+
+       if (str == NULL)
+               return NULL;
+
+       chars = g_strsplit(str, " ", 0);
+       if (chars == NULL)
+               return NULL;
+
+       for (i = 0; chars[i]; i++) {
+               struct characteristic *chr;
+               int ret;
+
+               chr = g_new0(struct characteristic, 1);
+
+               ret = sscanf(chars[i], "%04hX#%02hhX#%04hX#%s", &chr->handle,
+                               &chr->perm, &chr->end, chr->type);
+               if (ret < 4) {
+                       g_free(chr);
+                       continue;
+               }
+
+               chr->gatt = gatt;
+               l = g_slist_append(l, chr);
+       }
+
+       g_strfreev(chars);
+
+       return l;
+}
+
+static GSList *load_characteristics(struct gatt_service *gatt, uint16_t start)
+{
+       GSList *chrs_list;
+       bdaddr_t sba, dba;
+       uint8_t bdaddr_type;
+       char *str;
+
+       gatt_get_address(gatt, &sba, &dba, &bdaddr_type);
+
+       str = read_device_characteristics(&sba, &dba, bdaddr_type, start);
+       if (str == NULL)
+               return NULL;
+
+       chrs_list = string_to_characteristic_list(gatt, str);
+
+       free(str);
+
+       return chrs_list;
+}
+
+static void store_attribute(struct gatt_service *gatt, uint16_t handle,
+                               uint16_t type, uint8_t *value, gsize len)
+{
+       struct btd_device *device = gatt->dev;
+       bdaddr_t sba, dba;
+       uint8_t bdaddr_type;
+       bt_uuid_t uuid;
+       char *str, *tmp;
+       guint i;
+
+       str = g_malloc0(MAX_LEN_UUID_STR + len * 2 + 1);
+
+       bt_uuid16_create(&uuid, type);
+       bt_uuid_to_string(&uuid, str, MAX_LEN_UUID_STR);
+
+       str[MAX_LEN_UUID_STR - 1] = '#';
+
+       for (i = 0, tmp = str + MAX_LEN_UUID_STR; i < len; i++, tmp += 2)
+               sprintf(tmp, "%02X", value[i]);
+
+       gatt_get_address(gatt, &sba, &dba, NULL);
+
+       bdaddr_type = device_get_addr_type(device);
+
+       write_device_attribute(&sba, &dba, bdaddr_type, handle, str);
+
+       g_free(str);
+}
+
+static void query_list_append(struct gatt_service *gatt, struct query_data *data)
+{
+       struct query *query = gatt->query;
+
+       query->list = g_slist_append(query->list, data);
+}
+
+static void query_list_remove(struct gatt_service *gatt, struct query_data *data)
+{
+       struct query *query = gatt->query;
+
+       query->list = g_slist_remove(query->list, data);
+       if (query->list != NULL)
+               return;
+
+       g_free(query);
+       gatt->query = NULL;
+
+       remove_attio(gatt);
+}
+
+static void update_char_desc(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct query_data *current = user_data;
+       struct gatt_service *gatt = current->gatt;
+       struct characteristic *chr = current->chr;
+
+       if (status == 0) {
+
+               g_free(chr->desc);
+
+               chr->desc = g_malloc(len);
+               memcpy(chr->desc, pdu + 1, len - 1);
+               chr->desc[len - 1] = '\0';
+
+               store_attribute(gatt, current->handle,
+                               GATT_CHARAC_USER_DESC_UUID,
+                               (void *) chr->desc, len);
+       } else if (status == ATT_ECODE_INSUFF_ENC) {
+               GIOChannel *io = g_attrib_get_channel(gatt->attrib);
+               BtIOSecLevel level = BT_IO_SEC_HIGH;
+
+               bt_io_get(io, BT_IO_L2CAP, NULL,
+                               BT_IO_OPT_SEC_LEVEL, &level,
+                               BT_IO_OPT_INVALID);
+
+               if (level < BT_IO_SEC_HIGH)
+                       level++;
+
+               if (bt_io_set(io, BT_IO_L2CAP, NULL,
+                               BT_IO_OPT_SEC_LEVEL, level,
+                               BT_IO_OPT_INVALID)) {
+                       gatt_read_char(gatt->attrib, current->handle, 0,
+                                       update_char_desc, current);
+                       return;
+               }
+       }
+
+       query_list_remove(gatt, current);
+       g_free(current);
+}
+
+static void update_char_format(guint8 status, const guint8 *pdu, guint16 len,
+                                                               gpointer user_data)
+{
+       struct query_data *current = user_data;
+       struct gatt_service *gatt = current->gatt;
+       struct characteristic *chr = current->chr;
+
+       if (status != 0)
+               goto done;
+
+       if (len < 8)
+               goto done;
+
+       g_free(chr->format);
+
+       chr->format = g_new0(struct format, 1);
+       memcpy(chr->format, pdu + 1, 7);
+
+       store_attribute(gatt, current->handle, GATT_CHARAC_FMT_UUID,
+                               (void *) chr->format, sizeof(*chr->format));
+
+done:
+       query_list_remove(gatt, current);
+       g_free(current);
+}
+
+static void update_char_value(guint8 status, const guint8 *pdu,
+                                       guint16 len, gpointer user_data)
+{
+       struct query_data *current = user_data;
+       struct gatt_service *gatt = current->gatt;
+       struct characteristic *chr = current->chr;
+
+       if (status == 0)
+               characteristic_set_value(chr, pdu + 1, len - 1);
+       else if (status == ATT_ECODE_INSUFF_ENC) {
+               GIOChannel *io = g_attrib_get_channel(gatt->attrib);
+
+               if (bt_io_set(io, BT_IO_L2CAP, NULL,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
+                               BT_IO_OPT_INVALID)) {
+                       gatt_read_char(gatt->attrib, chr->handle, 0,
+                                       update_char_value, current);
+                       return;
+               }
+       }
+
+       query_list_remove(gatt, current);
+       g_free(current);
+}
+
+static int uuid_desc16_cmp(bt_uuid_t *uuid, guint16 desc)
+{
+       bt_uuid_t u16;
+
+       bt_uuid16_create(&u16, desc);
+
+       return bt_uuid_cmp(uuid, &u16);
+}
+
+static void descriptor_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       struct query_data *current = user_data;
+       struct gatt_service *gatt = current->gatt;
+       struct att_data_list *list;
+       guint8 format;
+       int i;
+
+       if (status != 0)
+               goto done;
+
+       DBG("Find Information Response received");
+
+       list = dec_find_info_resp(pdu, plen, &format);
+       if (list == NULL)
+               goto done;
+
+       for (i = 0; i < list->num; i++) {
+               guint16 handle;
+               bt_uuid_t uuid;
+               uint8_t *info = list->data[i];
+               struct query_data *qfmt;
+
+               handle = att_get_u16(info);
+
+               if (format == 0x01) {
+                       uuid = att_get_uuid16(&info[2]);
+               } else {
+                       /* Currently, only "user description" and "presentation
+                        * format" descriptors are used, and both have 16-bit
+                        * UUIDs. Therefore there is no need to support format
+                        * 0x02 yet. */
+                       continue;
+               }
+               qfmt = g_new0(struct query_data, 1);
+               qfmt->gatt = current->gatt;
+               qfmt->chr = current->chr;
+               qfmt->handle = handle;
+
+               if (uuid_desc16_cmp(&uuid, GATT_CHARAC_USER_DESC_UUID) == 0) {
+                       query_list_append(gatt, qfmt);
+                       gatt_read_char(gatt->attrib, handle, 0, update_char_desc,
+                                                                       qfmt);
+               } else if (uuid_desc16_cmp(&uuid, GATT_CHARAC_FMT_UUID) == 0) {
+                       query_list_append(gatt, qfmt);
+                       gatt_read_char(gatt->attrib, handle, 0,
+                                               update_char_format, qfmt);
+               } else
+                       g_free(qfmt);
+       }
+
+       att_data_list_free(list);
+done:
+       query_list_remove(gatt, current);
+       g_free(current);
+}
+
+static void update_all_chars(gpointer data, gpointer user_data)
+{
+       struct query_data *qdesc, *qvalue;
+       struct characteristic *chr = data;
+       struct gatt_service *gatt = user_data;
+
+       qdesc = g_new0(struct query_data, 1);
+       qdesc->gatt = gatt;
+       qdesc->chr = chr;
+
+       query_list_append(gatt, qdesc);
+
+       gatt_find_info(gatt->attrib, chr->handle + 1, chr->end, descriptor_cb,
+                                                                       qdesc);
+
+       qvalue = g_new0(struct query_data, 1);
+       qvalue->gatt = gatt;
+       qvalue->chr = chr;
+
+       query_list_append(gatt, qvalue);
+
+       gatt_read_char(gatt->attrib, chr->handle, 0, update_char_value, qvalue);
+}
+
+static DBusMessage *create_discover_char_reply(DBusMessage *msg, GSList *chars)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, array_iter;
+       GSList *l;
+
+       reply = dbus_message_new_method_return(msg);
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
+
+       for (l = chars; l; l = l->next) {
+               struct characteristic *chr = l->data;
+
+               dbus_message_iter_append_basic(&array_iter,
+                                       DBUS_TYPE_OBJECT_PATH, &chr->path);
+       }
+
+       dbus_message_iter_close_container(&iter, &array_iter);
+
+       return reply;
+}
+
+static void char_discovered_cb(GSList *characteristics, guint8 status,
+                                                       gpointer user_data)
+{
+       DBusMessage *reply;
+       struct query_data *current = user_data;
+       struct gatt_service *gatt = current->gatt;
+       struct gatt_primary *prim = gatt->prim;
+       uint16_t *previous_end = NULL;
+       GSList *l;
+       bdaddr_t sba, dba;
+       uint8_t bdaddr_type;
+
+       if (status != 0) {
+               const char *str = att_ecode2str(status);
+
+               DBG("Discover all characteristics failed: %s", str);
+               reply = btd_error_failed(gatt->query->msg, str);
+               goto fail;
+       }
+
+       for (l = characteristics; l; l = l->next) {
+               struct gatt_char *current_chr = l->data;
+               struct characteristic *chr;
+               guint handle = current_chr->value_handle;
+               GSList *lchr;
+
+               lchr = g_slist_find_custom(gatt->chars,
+                       GUINT_TO_POINTER(handle), characteristic_handle_cmp);
+               if (lchr)
+                       continue;
+
+               chr = g_new0(struct characteristic, 1);
+               chr->gatt = gatt;
+               chr->perm = current_chr->properties;
+               chr->handle = current_chr->value_handle;
+               strncpy(chr->type, current_chr->uuid, sizeof(chr->type));
+
+               if (previous_end)
+                       *previous_end = current_chr->handle;
+
+               previous_end = &chr->end;
+
+               gatt->chars = g_slist_append(gatt->chars, chr);
+               register_characteristic(chr, gatt->path);
+       }
+
+       if (previous_end)
+               *previous_end = prim->range.end;
+
+       gatt_get_address(gatt, &sba, &dba, &bdaddr_type);
+       store_characteristics(&sba, &dba, bdaddr_type, prim->range.start,
+                                                               gatt->chars);
+
+       g_slist_foreach(gatt->chars, update_all_chars, gatt);
+
+       reply = create_discover_char_reply(gatt->query->msg, gatt->chars);
+
+fail:
+       dbus_message_unref(gatt->query->msg);
+       gatt->query->msg = NULL;
+
+       g_dbus_send_message(gatt->conn, reply);
+       query_list_remove(gatt, current);
+       g_free(current);
+}
+
+static DBusMessage *discover_char(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct gatt_service *gatt = data;
+       struct query *query;
+       struct query_data *qchr;
+
+       if (gatt->query)
+               return btd_error_busy(msg);
+
+       query = g_new0(struct query, 1);
+
+       qchr = g_new0(struct query_data, 1);
+       qchr->gatt = gatt;
+
+       query->msg = dbus_message_ref(msg);
+
+       if (gatt->attioid == 0) {
+               gatt->attioid = btd_device_add_attio_callback(gatt->dev,
+                                                       attio_connected,
+                                                       attio_disconnected,
+                                                       gatt);
+       } else if (gatt->attrib) {
+               struct gatt_primary *prim = gatt->prim;
+               gatt_discover_char(gatt->attrib, prim->range.start,
+                                               prim->range.end, NULL,
+                                               char_discovered_cb, qchr);
+       }
+
+       gatt->query = query;
+
+       query_list_append(gatt, qchr);
+
+       return NULL;
+}
+
+static DBusMessage *prim_get_properties(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct gatt_service *gatt = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       GSList *l;
+       char **chars;
+       const char *uuid;
+       int i;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       chars = g_new0(char *, g_slist_length(gatt->chars) + 1);
+
+       for (i = 0, l = gatt->chars; l; l = l->next, i++) {
+               struct characteristic *chr = l->data;
+               chars[i] = chr->path;
+       }
+
+       dict_append_array(&dict, "Characteristics", DBUS_TYPE_OBJECT_PATH,
+                                                               &chars, i);
+       uuid = gatt->prim->uuid;
+       dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
+
+       g_free(chars);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static const GDBusMethodTable prim_methods[] = {
+       { GDBUS_ASYNC_METHOD("DiscoverCharacteristics",
+                       NULL, GDBUS_ARGS({ "characteristics", "ao" }),
+                       discover_char) },
+       { GDBUS_METHOD("RegisterCharacteristicsWatcher",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       register_watcher) },
+       { GDBUS_METHOD("UnregisterCharacteristicsWatcher",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       unregister_watcher) },
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       prim_get_properties) },
+       { }
+};
+
+static struct gatt_service *primary_register(DBusConnection *conn,
+                                               struct btd_device *device,
+                                               struct gatt_primary *prim,
+                                               int psm)
+{
+       struct gatt_service *gatt;
+       const char *device_path;
+
+       device_path = device_get_path(device);
+
+       gatt = g_new0(struct gatt_service, 1);
+       gatt->dev = btd_device_ref(device);
+       gatt->prim = prim;
+       gatt->psm = psm;
+       gatt->conn = dbus_connection_ref(conn);
+       gatt->path = g_strdup_printf("%s/service%04x", device_path,
+                                                               prim->range.start);
+
+       g_dbus_register_interface(gatt->conn, gatt->path,
+                                       CHAR_INTERFACE, prim_methods,
+                                       NULL, NULL, gatt, NULL);
+       gatt->chars = load_characteristics(gatt, prim->range.start);
+       g_slist_foreach(gatt->chars, register_characteristic, gatt->path);
+
+       return gatt;
+}
+
+GSList *attrib_client_register(DBusConnection *connection,
+                                       struct btd_device *device, int psm,
+                                       GAttrib *attrib, GSList *primaries)
+{
+       GSList *l, *services;
+
+       for (l = primaries, services = NULL; l; l = l->next) {
+               struct gatt_primary *prim = l->data;
+               struct gatt_service *gatt;
+
+               gatt = primary_register(connection, device, prim, psm);
+
+               DBG("Registered: %s", gatt->path);
+
+               services = g_slist_append(services, g_strdup(gatt->path));
+               gatt_services = g_slist_append(gatt_services, gatt);
+
+       }
+
+       return services;
+}
+
+static void primary_unregister(struct gatt_service *gatt)
+{
+       GSList *l;
+
+       for (l = gatt->chars; l; l = l->next) {
+               struct characteristic *chr = l->data;
+               g_dbus_unregister_interface(gatt->conn, chr->path,
+                                                       CHAR_INTERFACE);
+       }
+
+       g_dbus_unregister_interface(gatt->conn, gatt->path, CHAR_INTERFACE);
+
+       remove_attio(gatt);
+}
+
+static int path_cmp(gconstpointer data, gconstpointer user_data)
+{
+       const char *path = data;
+       const char *gatt_path = user_data;
+
+       return g_strcmp0(path, gatt_path);
+}
+
+void attrib_client_unregister(GSList *services)
+{
+       GSList *l, *left;
+
+       for (l = gatt_services, left = NULL; l; l = l->next) {
+               struct gatt_service *gatt = l->data;
+
+               if (!g_slist_find_custom(services, gatt->path, path_cmp)) {
+                       left = g_slist_append(left, gatt);
+                       continue;
+               }
+
+               primary_unregister(gatt);
+               gatt_service_free(gatt);
+       }
+
+       g_slist_free(gatt_services);
+       gatt_services = left;
+}
diff --git a/attrib/client.h b/attrib/client.h
new file mode 100644 (file)
index 0000000..948f030
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+GSList *attrib_client_register(DBusConnection *connection,
+                                       struct btd_device *device, int psm,
+                                       GAttrib *attrib, GSList *primaries);
+void attrib_client_unregister(GSList *services);
diff --git a/attrib/gatt-service.c b/attrib/gatt-service.c
new file mode 100644 (file)
index 0000000..a9de98c
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+#include <adapter.h>
+
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
+#include "attrib-server.h"
+#include "gatt-service.h"
+#include "log.h"
+
+struct gatt_info {
+       bt_uuid_t uuid;
+       uint8_t props;
+       int authentication;
+       int authorization;
+       GSList *callbacks;
+       unsigned int num_attrs;
+       uint16_t *value_handle;
+       uint16_t *ccc_handle;
+};
+
+struct attrib_cb {
+       attrib_event_t event;
+       void *fn;
+       void *user_data;
+};
+
+static GSList *parse_opts(gatt_option opt1, va_list args)
+{
+       gatt_option opt = opt1;
+       struct gatt_info *info;
+       struct attrib_cb *cb;
+       GSList *l = NULL;
+
+       info = g_new0(struct gatt_info, 1);
+       l = g_slist_append(l, info);
+
+       while (opt != GATT_OPT_INVALID) {
+               switch (opt) {
+               case GATT_OPT_CHR_UUID:
+                       bt_uuid16_create(&info->uuid, va_arg(args, int));
+                       /* characteristic declaration and value */
+                       info->num_attrs += 2;
+                       break;
+               case GATT_OPT_CHR_PROPS:
+                       info->props = va_arg(args, int);
+
+                       if (info->props & (ATT_CHAR_PROPER_NOTIFY |
+                                               ATT_CHAR_PROPER_INDICATE))
+                               /* client characteristic configuration */
+                               info->num_attrs += 1;
+
+                       /* TODO: "Extended Properties" property requires a
+                        * descriptor, but it is not supported yet. */
+                       break;
+               case GATT_OPT_CHR_VALUE_CB:
+                       cb = g_new0(struct attrib_cb, 1);
+                       cb->event = va_arg(args, attrib_event_t);
+                       cb->fn = va_arg(args, void *);
+                       cb->user_data = va_arg(args, void *);
+                       info->callbacks = g_slist_append(info->callbacks, cb);
+                       break;
+               case GATT_OPT_CHR_VALUE_GET_HANDLE:
+                       info->value_handle = va_arg(args, void *);
+                       break;
+               case GATT_OPT_CCC_GET_HANDLE:
+                       info->ccc_handle = va_arg(args, void *);
+                       break;
+               case GATT_OPT_CHR_AUTHENTICATION:
+                       info->authentication = va_arg(args, gatt_option);
+                       break;
+               case GATT_OPT_CHR_AUTHORIZATION:
+                       info->authorization = va_arg(args, gatt_option);
+                       break;
+               default:
+                       error("Invalid option: %d", opt);
+               }
+
+               opt = va_arg(args, gatt_option);
+               if (opt == GATT_OPT_CHR_UUID) {
+                       info = g_new0(struct gatt_info, 1);
+                       l = g_slist_append(l, info);
+               }
+       }
+
+       return l;
+}
+
+static struct attribute *add_service_declaration(struct btd_adapter *adapter,
+                               uint16_t handle, uint16_t svc, bt_uuid_t *uuid)
+{
+       bt_uuid_t bt_uuid;
+       uint8_t atval[16];
+       int len;
+
+       if (uuid->type == BT_UUID16) {
+               att_put_u16(uuid->value.u16, &atval[0]);
+               len = 2;
+       } else if (uuid->type == BT_UUID128) {
+               att_put_u128(uuid->value.u128, &atval[0]);
+               len = 16;
+       } else
+               return NULL;
+
+       bt_uuid16_create(&bt_uuid, svc);
+
+       return attrib_db_add(adapter, handle, &bt_uuid, ATT_NONE,
+                                               ATT_NOT_PERMITTED, atval, len);
+}
+
+static int att_read_reqs(int authorization, int authentication, uint8_t props)
+{
+       if (authorization == GATT_CHR_VALUE_READ ||
+                               authorization == GATT_CHR_VALUE_BOTH)
+               return ATT_AUTHORIZATION;
+       else if (authentication == GATT_CHR_VALUE_READ ||
+                               authentication == GATT_CHR_VALUE_BOTH)
+               return ATT_AUTHENTICATION;
+       else if (!(props & ATT_CHAR_PROPER_READ))
+               return ATT_NOT_PERMITTED;
+
+       return ATT_NONE;
+}
+
+static int att_write_reqs(int authorization, int authentication, uint8_t props)
+{
+       if (authorization == GATT_CHR_VALUE_WRITE ||
+                               authorization == GATT_CHR_VALUE_BOTH)
+               return ATT_AUTHORIZATION;
+       else if (authentication == GATT_CHR_VALUE_WRITE ||
+                               authentication == GATT_CHR_VALUE_BOTH)
+               return ATT_AUTHENTICATION;
+       else if (!(props & (ATT_CHAR_PROPER_WRITE |
+                                       ATT_CHAR_PROPER_WRITE_WITHOUT_RESP)))
+               return ATT_NOT_PERMITTED;
+
+       return ATT_NONE;
+}
+
+static gint find_callback(gconstpointer a, gconstpointer b)
+{
+       const struct attrib_cb *cb = a;
+       unsigned int event = GPOINTER_TO_UINT(b);
+
+       return cb->event - event;
+}
+
+static gboolean add_characteristic(struct btd_adapter *adapter,
+                               uint16_t *handle, struct gatt_info *info)
+{
+       int read_reqs, write_reqs;
+       uint16_t h = *handle;
+       struct attribute *a;
+       bt_uuid_t bt_uuid;
+       uint8_t atval[5];
+       GSList *l;
+
+       if (!info->uuid.value.u16 || !info->props) {
+               error("Characteristic UUID or properties are missing");
+               return FALSE;
+       }
+
+       read_reqs = att_read_reqs(info->authorization, info->authentication,
+                                                               info->props);
+       write_reqs = att_write_reqs(info->authorization, info->authentication,
+                                                               info->props);
+
+       /* TODO: static characteristic values are not supported, therefore a
+        * callback must be always provided if a read/write property is set */
+       if (read_reqs != ATT_NOT_PERMITTED) {
+               gpointer reqs = GUINT_TO_POINTER(ATTRIB_READ);
+
+               if (!g_slist_find_custom(info->callbacks, reqs,
+                                                       find_callback)) {
+                       error("Callback for read required");
+                       return FALSE;
+               }
+       }
+
+       if (write_reqs != ATT_NOT_PERMITTED) {
+               gpointer reqs = GUINT_TO_POINTER(ATTRIB_WRITE);
+
+               if (!g_slist_find_custom(info->callbacks, reqs,
+                                                       find_callback)) {
+                       error("Callback for write required");
+                       return FALSE;
+               }
+       }
+
+       /* characteristic declaration */
+       bt_uuid16_create(&bt_uuid, GATT_CHARAC_UUID);
+       atval[0] = info->props;
+       att_put_u16(h + 1, &atval[1]);
+       att_put_u16(info->uuid.value.u16, &atval[3]);
+       if (attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                               atval, sizeof(atval)) == NULL)
+               return FALSE;
+
+       /* characteristic value */
+       a = attrib_db_add(adapter, h++, &info->uuid, read_reqs, write_reqs,
+                                                               NULL, 0);
+       if (a == NULL)
+               return FALSE;
+
+       for (l = info->callbacks; l != NULL; l = l->next) {
+               struct attrib_cb *cb = l->data;
+
+               switch (cb->event) {
+               case ATTRIB_READ:
+                       a->read_cb = cb->fn;
+                       break;
+               case ATTRIB_WRITE:
+                       a->write_cb = cb->fn;
+                       break;
+               }
+
+               a->cb_user_data = cb->user_data;
+       }
+
+       if (info->value_handle != NULL)
+               *info->value_handle = a->handle;
+
+       /* client characteristic configuration descriptor */
+       if (info->props & (ATT_CHAR_PROPER_NOTIFY | ATT_CHAR_PROPER_INDICATE)) {
+               uint8_t cfg_val[2];
+
+               bt_uuid16_create(&bt_uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+               cfg_val[0] = 0x00;
+               cfg_val[1] = 0x00;
+               a = attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE,
+                               ATT_AUTHENTICATION, cfg_val, sizeof(cfg_val));
+               if (a == NULL)
+                       return FALSE;
+
+               if (info->ccc_handle != NULL)
+                       *info->ccc_handle = a->handle;
+       }
+
+       *handle = h;
+
+       return TRUE;
+}
+
+static void free_gatt_info(void *data)
+{
+       struct gatt_info *info = data;
+
+       g_slist_free_full(info->callbacks, g_free);
+       g_free(info);
+}
+
+static void service_attr_del(struct btd_adapter *adapter, uint16_t start_handle,
+                                                       uint16_t end_handle)
+{
+       uint16_t handle;
+
+       for (handle = start_handle; handle <= end_handle; handle++)
+               if (attrib_db_del(adapter, handle) < 0)
+                       error("Can't delete handle 0x%04x", handle);
+}
+
+gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
+                               bt_uuid_t *svc_uuid, gatt_option opt1, ...)
+{
+       char uuidstr[MAX_LEN_UUID_STR];
+       uint16_t start_handle, h;
+       unsigned int size;
+       va_list args;
+       GSList *chrs, *l;
+
+       bt_uuid_to_string(svc_uuid, uuidstr, MAX_LEN_UUID_STR);
+
+       if (svc_uuid->type != BT_UUID16 && svc_uuid->type != BT_UUID128) {
+               error("Invalid service uuid: %s", uuidstr);
+               return FALSE;
+       }
+
+       va_start(args, opt1);
+       chrs = parse_opts(opt1, args);
+       va_end(args);
+
+       /* calculate how many attributes are necessary for this service */
+       for (l = chrs, size = 1; l != NULL; l = l->next) {
+               struct gatt_info *info = l->data;
+               size += info->num_attrs;
+       }
+
+       start_handle = attrib_db_find_avail(adapter, svc_uuid, size);
+       if (start_handle == 0) {
+               error("Not enough free handles to register service");
+               goto fail;
+       }
+
+       DBG("New service: handle 0x%04x, UUID %s, %d attributes",
+                                               start_handle, uuidstr, size);
+
+       /* service declaration */
+       h = start_handle;
+       if (add_service_declaration(adapter, h++, uuid, svc_uuid) == NULL)
+               goto fail;
+
+       for (l = chrs; l != NULL; l = l->next) {
+               struct gatt_info *info = l->data;
+
+               DBG("New characteristic: handle 0x%04x", h);
+               if (!add_characteristic(adapter, &h, info)) {
+                       service_attr_del(adapter, start_handle, h - 1);
+                       goto fail;
+               }
+       }
+
+       g_assert(size < USHRT_MAX);
+       g_assert(h - start_handle == (uint16_t) size);
+       g_slist_free_full(chrs, free_gatt_info);
+
+       return TRUE;
+
+fail:
+       g_slist_free_full(chrs, free_gatt_info);
+       return FALSE;
+}
diff --git a/attrib/gatt-service.h b/attrib/gatt-service.h
new file mode 100644 (file)
index 0000000..b810e2e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef enum {
+       GATT_OPT_INVALID = 0,
+       GATT_OPT_CHR_UUID,
+       GATT_OPT_CHR_PROPS,
+       GATT_OPT_CHR_VALUE_CB,
+       GATT_OPT_CHR_AUTHENTICATION,
+       GATT_OPT_CHR_AUTHORIZATION,
+
+       /* Get attribute handle for characteristic value */
+       GATT_OPT_CHR_VALUE_GET_HANDLE,
+
+       /* Get handle for ccc attribute */
+       GATT_OPT_CCC_GET_HANDLE,
+
+       /* arguments for authentication/authorization */
+       GATT_CHR_VALUE_READ,
+       GATT_CHR_VALUE_WRITE,
+       GATT_CHR_VALUE_BOTH,
+} gatt_option;
+
+typedef enum {
+       ATTRIB_READ,
+       ATTRIB_WRITE,
+} attrib_event_t;
+
+gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
+                                       bt_uuid_t *svc_uuid, gatt_option opt1, ...);
diff --git a/attrib/gatt.c b/attrib/gatt.c
new file mode 100644 (file)
index 0000000..6f9a11d
--- /dev/null
@@ -0,0 +1,655 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+
+struct discover_primary {
+       GAttrib *attrib;
+       bt_uuid_t uuid;
+       GSList *primaries;
+       gatt_cb_t cb;
+       void *user_data;
+};
+
+struct discover_char {
+       GAttrib *attrib;
+       bt_uuid_t *uuid;
+       uint16_t end;
+       GSList *characteristics;
+       gatt_cb_t cb;
+       void *user_data;
+};
+
+static void discover_primary_free(struct discover_primary *dp)
+{
+       g_slist_free(dp->primaries);
+       g_attrib_unref(dp->attrib);
+       g_free(dp);
+}
+
+static void discover_char_free(struct discover_char *dc)
+{
+       g_slist_free_full(dc->characteristics, g_free);
+       g_attrib_unref(dc->attrib);
+       g_free(dc->uuid);
+       g_free(dc);
+}
+
+static guint16 encode_discover_primary(uint16_t start, uint16_t end,
+                               bt_uuid_t *uuid, uint8_t *pdu, size_t len)
+{
+       bt_uuid_t prim;
+       guint16 plen;
+
+       bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
+
+       if (uuid == NULL) {
+               /* Discover all primary services */
+               plen = enc_read_by_grp_req(start, end, &prim, pdu, len);
+       } else {
+               uint16_t u16;
+               uint128_t u128;
+               const void *value;
+               int vlen;
+
+               /* Discover primary service by service UUID */
+
+               if (uuid->type == BT_UUID16) {
+                       u16 = htobs(uuid->value.u16);
+                       value = &u16;
+                       vlen = sizeof(u16);
+               } else {
+                       htob128(&uuid->value.u128, &u128);
+                       value = &u128;
+                       vlen = sizeof(u128);
+               }
+
+               plen = enc_find_by_type_req(start, end, &prim, value, vlen,
+                                                               pdu, len);
+       }
+
+       return plen;
+}
+
+static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
+                                       guint16 iplen, gpointer user_data)
+
+{
+       struct discover_primary *dp = user_data;
+       GSList *ranges, *last;
+       struct att_range *range;
+       uint8_t *buf;
+       guint16 oplen;
+       int err = 0, buflen;
+
+       if (status) {
+               err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
+               goto done;
+       }
+
+       ranges = dec_find_by_type_resp(ipdu, iplen);
+       if (ranges == NULL)
+               goto done;
+
+       dp->primaries = g_slist_concat(dp->primaries, ranges);
+
+       last = g_slist_last(ranges);
+       range = last->data;
+
+       if (range->end == 0xffff)
+               goto done;
+
+       buf = g_attrib_get_buffer(dp->attrib, &buflen);
+       oplen = encode_discover_primary(range->end + 1, 0xffff, &dp->uuid,
+                                                               buf, buflen);
+
+       if (oplen == 0)
+               goto done;
+
+       g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_by_uuid_cb,
+                                                               dp, NULL);
+       return;
+
+done:
+       dp->cb(dp->primaries, err, dp->user_data);
+       discover_primary_free(dp);
+}
+
+static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
+                                                       gpointer user_data)
+{
+       struct discover_primary *dp = user_data;
+       struct att_data_list *list;
+       unsigned int i, err;
+       uint16_t start, end;
+
+       if (status) {
+               err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
+               goto done;
+       }
+
+       list = dec_read_by_grp_resp(ipdu, iplen);
+       if (list == NULL) {
+               err = ATT_ECODE_IO;
+               goto done;
+       }
+
+       for (i = 0, end = 0; i < list->num; i++) {
+               const uint8_t *data = list->data[i];
+               struct gatt_primary *primary;
+               bt_uuid_t uuid;
+
+               start = att_get_u16(&data[0]);
+               end = att_get_u16(&data[2]);
+
+               if (list->len == 6) {
+                       bt_uuid_t uuid16 = att_get_uuid16(&data[4]);
+                       bt_uuid_to_uuid128(&uuid16, &uuid);
+               } else if (list->len == 20) {
+                       uuid = att_get_uuid128(&data[4]);
+               } else {
+                       /* Skipping invalid data */
+                       continue;
+               }
+
+               primary = g_try_new0(struct gatt_primary, 1);
+               if (!primary) {
+                       err = ATT_ECODE_INSUFF_RESOURCES;
+                       goto done;
+               }
+               primary->range.start = start;
+               primary->range.end = end;
+               bt_uuid_to_string(&uuid, primary->uuid, sizeof(primary->uuid));
+               dp->primaries = g_slist_append(dp->primaries, primary);
+       }
+
+       att_data_list_free(list);
+       err = 0;
+
+       if (end != 0xffff) {
+               int buflen;
+               uint8_t *buf = g_attrib_get_buffer(dp->attrib, &buflen);
+               guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
+                                                               buf, buflen);
+
+               g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_all_cb,
+                                                               dp, NULL);
+
+               return;
+       }
+
+done:
+       dp->cb(dp->primaries, err, dp->user_data);
+       discover_primary_free(dp);
+}
+
+guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
+                                                       gpointer user_data)
+{
+       struct discover_primary *dp;
+       int buflen;
+       uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+       GAttribResultFunc cb;
+       guint16 plen;
+
+       plen = encode_discover_primary(0x0001, 0xffff, uuid, buf, buflen);
+       if (plen == 0)
+               return 0;
+
+       dp = g_try_new0(struct discover_primary, 1);
+       if (dp == NULL)
+               return 0;
+
+       dp->attrib = g_attrib_ref(attrib);
+       dp->cb = func;
+       dp->user_data = user_data;
+
+       if (uuid) {
+               dp->uuid = *uuid;
+               cb = primary_by_uuid_cb;
+       } else
+               cb = primary_all_cb;
+
+       return g_attrib_send(attrib, 0, buf[0], buf, plen, cb, dp, NULL);
+}
+
+static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
+                                                       gpointer user_data)
+{
+       struct discover_char *dc = user_data;
+       struct att_data_list *list;
+       unsigned int i, err;
+       int buflen;
+       uint8_t *buf;
+       guint16 oplen;
+       bt_uuid_t uuid;
+       uint16_t last = 0;
+
+       if (status) {
+               err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
+               goto done;
+       }
+
+       list = dec_read_by_type_resp(ipdu, iplen);
+       if (list == NULL) {
+               err = ATT_ECODE_IO;
+               goto done;
+       }
+
+       for (i = 0; i < list->num; i++) {
+               uint8_t *value = list->data[i];
+               struct gatt_char *chars;
+               bt_uuid_t uuid;
+
+               last = att_get_u16(value);
+
+               if (list->len == 7) {
+                       bt_uuid_t uuid16 = att_get_uuid16(&value[5]);
+                       bt_uuid_to_uuid128(&uuid16, &uuid);
+               } else
+                       uuid = att_get_uuid128(&value[5]);
+
+               chars = g_try_new0(struct gatt_char, 1);
+               if (!chars) {
+                       err = ATT_ECODE_INSUFF_RESOURCES;
+                       goto done;
+               }
+
+               if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid))
+                       break;
+
+               chars->handle = last;
+               chars->properties = value[2];
+               chars->value_handle = att_get_u16(&value[3]);
+               bt_uuid_to_string(&uuid, chars->uuid, sizeof(chars->uuid));
+               dc->characteristics = g_slist_append(dc->characteristics,
+                                                                       chars);
+       }
+
+       att_data_list_free(list);
+       err = 0;
+
+       if (last != 0 && (last + 1 < dc->end)) {
+               buf = g_attrib_get_buffer(dc->attrib, &buflen);
+
+               bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+
+               oplen = enc_read_by_type_req(last + 1, dc->end, &uuid, buf,
+                                                                       buflen);
+
+               if (oplen == 0)
+                       return;
+
+               g_attrib_send(dc->attrib, 0, buf[0], buf, oplen,
+                                               char_discovered_cb, dc, NULL);
+
+               return;
+       }
+
+done:
+       dc->cb(dc->characteristics, err, dc->user_data);
+       discover_char_free(dc);
+}
+
+guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
+                                               bt_uuid_t *uuid, gatt_cb_t func,
+                                               gpointer user_data)
+{
+       int buflen;
+       uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+       struct discover_char *dc;
+       bt_uuid_t type_uuid;
+       guint16 plen;
+
+       bt_uuid16_create(&type_uuid, GATT_CHARAC_UUID);
+
+       plen = enc_read_by_type_req(start, end, &type_uuid, buf, buflen);
+       if (plen == 0)
+               return 0;
+
+       dc = g_try_new0(struct discover_char, 1);
+       if (dc == NULL)
+               return 0;
+
+       dc->attrib = g_attrib_ref(attrib);
+       dc->cb = func;
+       dc->user_data = user_data;
+       dc->end = end;
+       dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
+
+       return g_attrib_send(attrib, 0, buf[0], buf, plen, char_discovered_cb,
+                                                               dc, NULL);
+}
+
+guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
+                                       bt_uuid_t *uuid, GAttribResultFunc func,
+                                       gpointer user_data)
+{
+       int buflen;
+       uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+       guint16 plen;
+
+       plen = enc_read_by_type_req(start, end, uuid, buf, buflen);
+       if (plen == 0)
+               return 0;
+
+       return g_attrib_send(attrib, 0, ATT_OP_READ_BY_TYPE_REQ,
+                                       buf, plen, func, user_data, NULL);
+}
+
+struct read_long_data {
+       GAttrib *attrib;
+       GAttribResultFunc func;
+       gpointer user_data;
+       guint8 *buffer;
+       guint16 size;
+       guint16 handle;
+       guint id;
+       gint ref;
+};
+
+static void read_long_destroy(gpointer user_data)
+{
+       struct read_long_data *long_read = user_data;
+
+       if (g_atomic_int_dec_and_test(&long_read->ref) == FALSE)
+               return;
+
+       if (long_read->buffer != NULL)
+               g_free(long_read->buffer);
+
+       g_free(long_read);
+}
+
+static void read_blob_helper(guint8 status, const guint8 *rpdu, guint16 rlen,
+                                                       gpointer user_data)
+{
+       struct read_long_data *long_read = user_data;
+       uint8_t *buf;
+       int buflen;
+       guint8 *tmp;
+       guint16 plen;
+       guint id;
+
+       if (status != 0 || rlen == 1) {
+               status = 0;
+               goto done;
+       }
+
+       tmp = g_try_realloc(long_read->buffer, long_read->size + rlen - 1);
+
+       if (tmp == NULL) {
+               status = ATT_ECODE_INSUFF_RESOURCES;
+               goto done;
+       }
+
+       memcpy(&tmp[long_read->size], &rpdu[1], rlen - 1);
+       long_read->buffer = tmp;
+       long_read->size += rlen - 1;
+
+       buf = g_attrib_get_buffer(long_read->attrib, &buflen);
+       if (rlen < buflen)
+               goto done;
+
+       plen = enc_read_blob_req(long_read->handle, long_read->size - 1,
+                                                               buf, buflen);
+       id = g_attrib_send(long_read->attrib, long_read->id,
+                               ATT_OP_READ_BLOB_REQ, buf, plen,
+                               read_blob_helper, long_read, read_long_destroy);
+
+       if (id != 0) {
+               g_atomic_int_inc(&long_read->ref);
+               return;
+       }
+
+       status = ATT_ECODE_IO;
+
+done:
+       long_read->func(status, long_read->buffer, long_read->size,
+                                                       long_read->user_data);
+}
+
+static void read_char_helper(guint8 status, const guint8 *rpdu,
+                                       guint16 rlen, gpointer user_data)
+{
+       struct read_long_data *long_read = user_data;
+       int buflen;
+       uint8_t *buf = g_attrib_get_buffer(long_read->attrib, &buflen);
+       guint16 plen;
+       guint id;
+
+       if (status != 0 || rlen < buflen)
+               goto done;
+
+       long_read->buffer = g_malloc(rlen);
+
+       if (long_read->buffer == NULL)
+               goto done;
+
+       memcpy(long_read->buffer, rpdu, rlen);
+       long_read->size = rlen;
+
+       plen = enc_read_blob_req(long_read->handle, rlen - 1, buf, buflen);
+       id = g_attrib_send(long_read->attrib, long_read->id,
+                       ATT_OP_READ_BLOB_REQ, buf, plen, read_blob_helper,
+                       long_read, read_long_destroy);
+
+       if (id != 0) {
+               g_atomic_int_inc(&long_read->ref);
+               return;
+       }
+
+       status = ATT_ECODE_IO;
+
+done:
+       long_read->func(status, rpdu, rlen, long_read->user_data);
+}
+
+guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset,
+                               GAttribResultFunc func, gpointer user_data)
+{
+       uint8_t *buf;
+       int buflen;
+       guint16 plen;
+       guint id;
+       struct read_long_data *long_read;
+
+       long_read = g_try_new0(struct read_long_data, 1);
+
+       if (long_read == NULL)
+               return 0;
+
+       long_read->attrib = attrib;
+       long_read->func = func;
+       long_read->user_data = user_data;
+       long_read->handle = handle;
+
+       buf = g_attrib_get_buffer(attrib, &buflen);
+       if (offset > 0) {
+               plen = enc_read_blob_req(long_read->handle, offset, buf,
+                                                                       buflen);
+               id = g_attrib_send(attrib, 0, ATT_OP_READ_BLOB_REQ, buf, plen,
+                               read_blob_helper, long_read, read_long_destroy);
+       } else {
+               plen = enc_read_req(handle, buf, buflen);
+               id = g_attrib_send(attrib, 0, ATT_OP_READ_REQ, buf, plen,
+                               read_char_helper, long_read, read_long_destroy);
+       }
+
+       if (id == 0)
+               g_free(long_read);
+       else {
+               g_atomic_int_inc(&long_read->ref);
+               long_read->id = id;
+       }
+
+       return id;
+}
+
+guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
+                       int vlen, GAttribResultFunc func, gpointer user_data)
+{
+       uint8_t *buf;
+       int buflen;
+       guint16 plen;
+
+       buf = g_attrib_get_buffer(attrib, &buflen);
+       if (func)
+               plen = enc_write_req(handle, value, vlen, buf, buflen);
+       else
+               plen = enc_write_cmd(handle, value, vlen, buf, buflen);
+
+       return g_attrib_send(attrib, 0, buf[0], buf, plen, func,
+                                                       user_data, NULL);
+}
+
+guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
+                                                       gpointer user_data)
+{
+       uint8_t *buf;
+       int buflen;
+       guint16 plen;
+
+       buf = g_attrib_get_buffer(attrib, &buflen);
+       plen = enc_mtu_req(mtu, buf, buflen);
+       return g_attrib_send(attrib, 0, ATT_OP_MTU_REQ, buf, plen, func,
+                                                       user_data, NULL);
+}
+
+guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
+                               GAttribResultFunc func, gpointer user_data)
+{
+       uint8_t *buf;
+       int buflen;
+       guint16 plen;
+
+       buf = g_attrib_get_buffer(attrib, &buflen);
+       plen = enc_find_info_req(start, end, buf, buflen);
+       if (plen == 0)
+               return 0;
+
+       return g_attrib_send(attrib, 0, ATT_OP_FIND_INFO_REQ, buf, plen, func,
+                                                       user_data, NULL);
+}
+
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
+                               GDestroyNotify notify, gpointer user_data)
+{
+       uint8_t *buf;
+       int buflen;
+       guint16 plen;
+
+       buf = g_attrib_get_buffer(attrib, &buflen);
+       plen = enc_write_cmd(handle, value, vlen, buf, buflen);
+       return g_attrib_send(attrib, 0, ATT_OP_WRITE_CMD, buf, plen, NULL,
+                                                       user_data, notify);
+}
+
+static sdp_data_t *proto_seq_find(sdp_list_t *proto_list)
+{
+       sdp_list_t *list;
+       uuid_t proto;
+
+       sdp_uuid16_create(&proto, ATT_UUID);
+
+       for (list = proto_list; list; list = list->next) {
+               sdp_list_t *p;
+               for (p = list->data; p; p = p->next) {
+                       sdp_data_t *seq = p->data;
+                       if (seq && seq->dtd == SDP_UUID16 &&
+                               sdp_uuid16_cmp(&proto, &seq->val.uuid) == 0)
+                               return seq->next;
+               }
+       }
+
+       return NULL;
+}
+
+static gboolean parse_proto_params(sdp_list_t *proto_list, uint16_t *psm,
+                                               uint16_t *start, uint16_t *end)
+{
+       sdp_data_t *seq1, *seq2;
+
+       if (psm)
+               *psm = sdp_get_proto_port(proto_list, L2CAP_UUID);
+
+       /* Getting start and end handle */
+       seq1 = proto_seq_find(proto_list);
+       if (!seq1 || seq1->dtd != SDP_UINT16)
+               return FALSE;
+
+       seq2 = seq1->next;
+       if (!seq2 || seq2->dtd != SDP_UINT16)
+               return FALSE;
+
+       if (start)
+               *start = seq1->val.uint16;
+
+       if (end)
+               *end = seq2->val.uint16;
+
+       return TRUE;
+}
+
+gboolean gatt_parse_record(const sdp_record_t *rec,
+                                       uuid_t *prim_uuid, uint16_t *psm,
+                                       uint16_t *start, uint16_t *end)
+{
+       sdp_list_t *list;
+       uuid_t uuid;
+       gboolean ret;
+
+       if (sdp_get_service_classes(rec, &list) < 0)
+               return FALSE;
+
+       memcpy(&uuid, list->data, sizeof(uuid));
+       sdp_list_free(list, free);
+
+       if (sdp_get_access_protos(rec, &list) < 0)
+               return FALSE;
+
+       ret = parse_proto_params(list, psm, start, end);
+
+       sdp_list_foreach(list, (sdp_list_func_t) sdp_list_free, NULL);
+       sdp_list_free(list, NULL);
+
+       /* FIXME: replace by bt_uuid_t after uuid_t/sdp code cleanup */
+       if (ret && prim_uuid)
+               memcpy(prim_uuid, &uuid, sizeof(uuid_t));
+
+       return ret;
+}
diff --git a/attrib/gatt.h b/attrib/gatt.h
new file mode 100644 (file)
index 0000000..9ffe58f
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <bluetooth/sdp.h>
+
+/* GATT Profile Attribute types */
+#define GATT_PRIM_SVC_UUID             0x2800
+#define GATT_SND_SVC_UUID              0x2801
+#define GATT_INCLUDE_UUID              0x2802
+#define GATT_CHARAC_UUID               0x2803
+
+/* GATT Characteristic Types */
+#define GATT_CHARAC_DEVICE_NAME                        0x2A00
+#define GATT_CHARAC_APPEARANCE                 0x2A01
+#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG       0x2A02
+#define GATT_CHARAC_RECONNECTION_ADDRESS       0x2A03
+#define GATT_CHARAC_PERIPHERAL_PREF_CONN       0x2A04
+#define GATT_CHARAC_SERVICE_CHANGED            0x2A05
+
+/* GATT Characteristic Descriptors */
+#define GATT_CHARAC_EXT_PROPER_UUID    0x2900
+#define GATT_CHARAC_USER_DESC_UUID     0x2901
+#define GATT_CLIENT_CHARAC_CFG_UUID    0x2902
+#define GATT_SERVER_CHARAC_CFG_UUID    0x2903
+#define GATT_CHARAC_FMT_UUID           0x2904
+#define GATT_CHARAC_AGREG_FMT_UUID     0x2905
+#define GATT_CHARAC_VALID_RANGE_UUID   0x2906
+
+/* Client Characteristic Configuration bit field */
+#define GATT_CLIENT_CHARAC_CFG_NOTIF_BIT       0x0001
+#define GATT_CLIENT_CHARAC_CFG_IND_BIT         0x0002
+
+typedef void (*gatt_cb_t) (GSList *l, guint8 status, gpointer user_data);
+
+struct gatt_primary {
+       char uuid[MAX_LEN_UUID_STR + 1];
+       struct att_range range;
+};
+
+struct gatt_char {
+       char uuid[MAX_LEN_UUID_STR + 1];
+       uint16_t handle;
+       uint8_t properties;
+       uint16_t value_handle;
+};
+
+guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
+                                                       gpointer user_data);
+
+guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
+                                       bt_uuid_t *uuid, gatt_cb_t func,
+                                       gpointer user_data);
+
+guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset,
+                               GAttribResultFunc func, gpointer user_data);
+
+guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
+                       int vlen, GAttribResultFunc func, gpointer user_data);
+
+guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
+                               GAttribResultFunc func, gpointer user_data);
+
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
+                               GDestroyNotify notify, gpointer user_data);
+
+guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
+                               bt_uuid_t *uuid, GAttribResultFunc func,
+                               gpointer user_data);
+
+guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
+                                                       gpointer user_data);
+
+gboolean gatt_parse_record(const sdp_record_t *rec,
+                                       uuid_t *prim_uuid, uint16_t *psm,
+                                       uint16_t *start, uint16_t *end);
diff --git a/attrib/gattrib.c b/attrib/gattrib.c
new file mode 100644 (file)
index 0000000..00f59d7
--- /dev/null
@@ -0,0 +1,726 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+
+#include <stdio.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include "log.h"
+#include "att.h"
+#include "btio.h"
+#include "gattrib.h"
+
+#define GATT_TIMEOUT 30
+
+struct _GAttrib {
+       GIOChannel *io;
+       gint refs;
+       uint8_t *buf;
+       int buflen;
+       guint read_watch;
+       guint write_watch;
+       guint timeout_watch;
+       GQueue *requests;
+       GQueue *responses;
+       GSList *events;
+       guint next_cmd_id;
+       GDestroyNotify destroy;
+       gpointer destroy_user_data;
+       gboolean stale;
+};
+
+struct command {
+       guint id;
+       guint8 opcode;
+       guint8 *pdu;
+       guint16 len;
+       guint8 expected;
+       gboolean sent;
+       GAttribResultFunc func;
+       gpointer user_data;
+       GDestroyNotify notify;
+};
+
+struct event {
+       guint id;
+       guint8 expected;
+       GAttribNotifyFunc func;
+       gpointer user_data;
+       GDestroyNotify notify;
+};
+
+static guint8 opcode2expected(guint8 opcode)
+{
+       switch (opcode) {
+       case ATT_OP_MTU_REQ:
+               return ATT_OP_MTU_RESP;
+
+       case ATT_OP_FIND_INFO_REQ:
+               return ATT_OP_FIND_INFO_RESP;
+
+       case ATT_OP_FIND_BY_TYPE_REQ:
+               return ATT_OP_FIND_BY_TYPE_RESP;
+
+       case ATT_OP_READ_BY_TYPE_REQ:
+               return ATT_OP_READ_BY_TYPE_RESP;
+
+       case ATT_OP_READ_REQ:
+               return ATT_OP_READ_RESP;
+
+       case ATT_OP_READ_BLOB_REQ:
+               return ATT_OP_READ_BLOB_RESP;
+
+       case ATT_OP_READ_MULTI_REQ:
+               return ATT_OP_READ_MULTI_RESP;
+
+       case ATT_OP_READ_BY_GROUP_REQ:
+               return ATT_OP_READ_BY_GROUP_RESP;
+
+       case ATT_OP_WRITE_REQ:
+               return ATT_OP_WRITE_RESP;
+
+       case ATT_OP_PREP_WRITE_REQ:
+               return ATT_OP_PREP_WRITE_RESP;
+
+       case ATT_OP_EXEC_WRITE_REQ:
+               return ATT_OP_EXEC_WRITE_RESP;
+
+       case ATT_OP_HANDLE_IND:
+               return ATT_OP_HANDLE_CNF;
+       }
+
+       return 0;
+}
+
+static gboolean is_response(guint8 opcode)
+{
+       switch (opcode) {
+       case ATT_OP_ERROR:
+       case ATT_OP_MTU_RESP:
+       case ATT_OP_FIND_INFO_RESP:
+       case ATT_OP_FIND_BY_TYPE_RESP:
+       case ATT_OP_READ_BY_TYPE_RESP:
+       case ATT_OP_READ_RESP:
+       case ATT_OP_READ_BLOB_RESP:
+       case ATT_OP_READ_MULTI_RESP:
+       case ATT_OP_READ_BY_GROUP_RESP:
+       case ATT_OP_WRITE_RESP:
+       case ATT_OP_PREP_WRITE_RESP:
+       case ATT_OP_EXEC_WRITE_RESP:
+       case ATT_OP_HANDLE_CNF:
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+GAttrib *g_attrib_ref(GAttrib *attrib)
+{
+       if (!attrib)
+               return NULL;
+
+       g_atomic_int_inc(&attrib->refs);
+
+       DBG("%p: ref=%d", attrib, attrib->refs);
+
+       return attrib;
+}
+
+static void command_destroy(struct command *cmd)
+{
+       if (cmd->notify)
+               cmd->notify(cmd->user_data);
+
+       g_free(cmd->pdu);
+       g_free(cmd);
+}
+
+static void event_destroy(struct event *evt)
+{
+       if (evt->notify)
+               evt->notify(evt->user_data);
+
+       g_free(evt);
+}
+
+static void attrib_destroy(GAttrib *attrib)
+{
+       GSList *l;
+       struct command *c;
+
+       while ((c = g_queue_pop_head(attrib->requests)))
+               command_destroy(c);
+
+       while ((c = g_queue_pop_head(attrib->responses)))
+               command_destroy(c);
+
+       g_queue_free(attrib->requests);
+       attrib->requests = NULL;
+
+       g_queue_free(attrib->responses);
+       attrib->responses = NULL;
+
+       for (l = attrib->events; l; l = l->next)
+               event_destroy(l->data);
+
+       g_slist_free(attrib->events);
+       attrib->events = NULL;
+
+       if (attrib->timeout_watch > 0)
+               g_source_remove(attrib->timeout_watch);
+
+       if (attrib->write_watch > 0)
+               g_source_remove(attrib->write_watch);
+
+       if (attrib->read_watch > 0)
+               g_source_remove(attrib->read_watch);
+
+       if (attrib->io)
+               g_io_channel_unref(attrib->io);
+
+       g_free(attrib->buf);
+
+       if (attrib->destroy)
+               attrib->destroy(attrib->destroy_user_data);
+
+       g_free(attrib);
+}
+
+void g_attrib_unref(GAttrib *attrib)
+{
+       gboolean ret;
+
+       if (!attrib)
+               return;
+
+       ret = g_atomic_int_dec_and_test(&attrib->refs);
+
+       DBG("%p: ref=%d", attrib, attrib->refs);
+
+       if (ret == FALSE)
+               return;
+
+       attrib_destroy(attrib);
+}
+
+GIOChannel *g_attrib_get_channel(GAttrib *attrib)
+{
+       if (!attrib)
+               return NULL;
+
+       return attrib->io;
+}
+
+gboolean g_attrib_set_destroy_function(GAttrib *attrib,
+               GDestroyNotify destroy, gpointer user_data)
+{
+       if (attrib == NULL)
+               return FALSE;
+
+       attrib->destroy = destroy;
+       attrib->destroy_user_data = user_data;
+
+       return TRUE;
+}
+
+static gboolean disconnect_timeout(gpointer data)
+{
+       struct _GAttrib *attrib = data;
+       struct command *c;
+
+       g_attrib_ref(attrib);
+
+       c = g_queue_pop_head(attrib->requests);
+       if (c == NULL)
+               goto done;
+
+       if (c->func)
+               c->func(ATT_ECODE_TIMEOUT, NULL, 0, c->user_data);
+
+       command_destroy(c);
+
+       while ((c = g_queue_pop_head(attrib->requests))) {
+               if (c->func)
+                       c->func(ATT_ECODE_ABORTED, NULL, 0, c->user_data);
+               command_destroy(c);
+       }
+
+done:
+       attrib->stale = TRUE;
+
+       g_attrib_unref(attrib);
+
+       return FALSE;
+}
+
+static gboolean can_write_data(GIOChannel *io, GIOCondition cond,
+                                                               gpointer data)
+{
+       struct _GAttrib *attrib = data;
+       struct command *cmd;
+       GError *gerr = NULL;
+       gsize len;
+       GIOStatus iostat;
+       GQueue *queue;
+
+       if (attrib->stale)
+               return FALSE;
+
+       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+               return FALSE;
+
+       queue = attrib->responses;
+       cmd = g_queue_peek_head(queue);
+       if (cmd == NULL) {
+               queue = attrib->requests;
+               cmd = g_queue_peek_head(queue);
+       }
+       if (cmd == NULL)
+               return FALSE;
+
+       /*
+        * Verify that we didn't already send this command. This can only
+        * happen with elementes from attrib->requests.
+        */
+       if (cmd->sent)
+               return FALSE;
+
+       iostat = g_io_channel_write_chars(io, (gchar *) cmd->pdu, cmd->len,
+                                                               &len, &gerr);
+       if (iostat != G_IO_STATUS_NORMAL)
+               return FALSE;
+
+       if (cmd->expected == 0) {
+               g_queue_pop_head(queue);
+               command_destroy(cmd);
+
+               return TRUE;
+       }
+
+       cmd->sent = TRUE;
+
+       if (attrib->timeout_watch == 0)
+               attrib->timeout_watch = g_timeout_add_seconds(GATT_TIMEOUT,
+                                               disconnect_timeout, attrib);
+
+       return FALSE;
+}
+
+static void destroy_sender(gpointer data)
+{
+       struct _GAttrib *attrib = data;
+
+       attrib->write_watch = 0;
+       g_attrib_unref(attrib);
+}
+
+static void wake_up_sender(struct _GAttrib *attrib)
+{
+       if (attrib->write_watch > 0)
+               return;
+
+       attrib = g_attrib_ref(attrib);
+       attrib->write_watch = g_io_add_watch_full(attrib->io,
+                               G_PRIORITY_DEFAULT, G_IO_OUT,
+                               can_write_data, attrib, destroy_sender);
+}
+
+static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+       struct _GAttrib *attrib = data;
+       struct command *cmd = NULL;
+       GSList *l;
+       uint8_t buf[512], status;
+       gsize len;
+       GIOStatus iostat;
+       gboolean norequests, noresponses;
+
+       if (attrib->stale)
+               return FALSE;
+
+       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+               attrib->read_watch = 0;
+               return FALSE;
+       }
+
+       memset(buf, 0, sizeof(buf));
+
+       iostat = g_io_channel_read_chars(io, (gchar *) buf, sizeof(buf),
+                                                               &len, NULL);
+       if (iostat != G_IO_STATUS_NORMAL) {
+               status = ATT_ECODE_IO;
+               goto done;
+       }
+
+       for (l = attrib->events; l; l = l->next) {
+               struct event *evt = l->data;
+
+               if (evt->expected == buf[0] ||
+                               evt->expected == GATTRIB_ALL_EVENTS ||
+                               (is_response(buf[0]) == FALSE &&
+                                               evt->expected == GATTRIB_ALL_REQS))
+                       evt->func(buf, len, evt->user_data);
+       }
+
+       if (is_response(buf[0]) == FALSE)
+               return TRUE;
+
+       if (attrib->timeout_watch > 0) {
+               g_source_remove(attrib->timeout_watch);
+               attrib->timeout_watch = 0;
+       }
+
+       cmd = g_queue_pop_head(attrib->requests);
+       if (cmd == NULL) {
+               /* Keep the watch if we have events to report */
+               return attrib->events != NULL;
+       }
+
+       if (buf[0] == ATT_OP_ERROR) {
+               status = buf[4];
+               goto done;
+       }
+
+       if (cmd->expected != buf[0]) {
+               status = ATT_ECODE_IO;
+               goto done;
+       }
+
+       status = 0;
+
+done:
+       norequests = attrib->requests == NULL ||
+                       g_queue_is_empty(attrib->requests);
+       noresponses = attrib->responses == NULL ||
+                       g_queue_is_empty(attrib->responses);
+
+       if (cmd) {
+               if (cmd->func)
+                       cmd->func(status, buf, len, cmd->user_data);
+
+               command_destroy(cmd);
+       }
+
+       if (!norequests || !noresponses)
+               wake_up_sender(attrib);
+
+       return TRUE;
+}
+
+GAttrib *g_attrib_new(GIOChannel *io)
+{
+       struct _GAttrib *attrib;
+       uint16_t imtu;
+       uint16_t att_mtu;
+       uint16_t cid;
+       GError *gerr = NULL;
+
+       g_io_channel_set_encoding(io, NULL, NULL);
+       g_io_channel_set_buffered(io, FALSE);
+
+       bt_io_get(io, BT_IO_L2CAP, &gerr,
+                       BT_IO_OPT_IMTU, &imtu,
+                       BT_IO_OPT_CID, &cid,
+                       BT_IO_OPT_INVALID);
+
+       if (gerr) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               return NULL;
+       }
+
+       attrib = g_try_new0(struct _GAttrib, 1);
+       if (attrib == NULL)
+               return NULL;
+
+       att_mtu = (cid == ATT_CID) ? ATT_DEFAULT_LE_MTU : imtu;
+
+       attrib->buf = g_malloc0(att_mtu);
+       attrib->buflen = att_mtu;
+
+       attrib->io = g_io_channel_ref(io);
+       attrib->requests = g_queue_new();
+       attrib->responses = g_queue_new();
+
+       attrib->read_watch = g_io_add_watch(attrib->io,
+                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                       received_data, attrib);
+
+       return g_attrib_ref(attrib);
+}
+
+guint g_attrib_send(GAttrib *attrib, guint id, guint8 opcode,
+                       const guint8 *pdu, guint16 len, GAttribResultFunc func,
+                       gpointer user_data, GDestroyNotify notify)
+{
+       struct command *c;
+       GQueue *queue;
+
+       if (attrib->stale)
+               return 0;
+
+       c = g_try_new0(struct command, 1);
+       if (c == NULL)
+               return 0;
+
+       c->opcode = opcode;
+       c->expected = opcode2expected(opcode);
+       c->pdu = g_malloc(len);
+       memcpy(c->pdu, pdu, len);
+       c->len = len;
+       c->func = func;
+       c->user_data = user_data;
+       c->notify = notify;
+
+       if (is_response(opcode))
+               queue = attrib->responses;
+       else
+               queue = attrib->requests;
+
+       if (id) {
+               c->id = id;
+               if (!is_response(opcode))
+                       g_queue_push_head(queue, c);
+               else
+                       /* Don't re-order responses even if an ID is given */
+                       g_queue_push_tail(queue, c);
+       } else {
+               c->id = ++attrib->next_cmd_id;
+               g_queue_push_tail(queue, c);
+       }
+
+       /*
+        * If a command was added to the queue and it was empty before, wake up
+        * the sender. If the sender was already woken up by the second queue,
+        * wake_up_sender will just return.
+        */
+       if (g_queue_get_length(queue) == 1)
+               wake_up_sender(attrib);
+
+       return c->id;
+}
+
+static gint command_cmp_by_id(gconstpointer a, gconstpointer b)
+{
+       const struct command *cmd = a;
+       guint id = GPOINTER_TO_UINT(b);
+
+       return cmd->id - id;
+}
+
+gboolean g_attrib_cancel(GAttrib *attrib, guint id)
+{
+       GList *l = NULL;
+       struct command *cmd;
+       GQueue *queue;
+
+       if (attrib == NULL)
+               return FALSE;
+
+       queue = attrib->requests;
+       if (queue)
+               l = g_queue_find_custom(queue, GUINT_TO_POINTER(id),
+                                       command_cmp_by_id);
+       if (l == NULL) {
+               queue = attrib->responses;
+               if (!queue)
+                       return FALSE;
+               l = g_queue_find_custom(queue, GUINT_TO_POINTER(id),
+                                       command_cmp_by_id);
+       }
+
+       if (l == NULL)
+               return FALSE;
+
+       cmd = l->data;
+
+       if (cmd == g_queue_peek_head(queue) && cmd->sent)
+               cmd->func = NULL;
+       else {
+               g_queue_remove(queue, cmd);
+               command_destroy(cmd);
+       }
+
+       return TRUE;
+}
+
+static gboolean cancel_all_per_queue(GQueue *queue)
+{
+       struct command *c, *head = NULL;
+       gboolean first = TRUE;
+
+       if (queue == NULL)
+               return FALSE;
+
+       while ((c = g_queue_pop_head(queue))) {
+               if (first && c->sent) {
+                       /* If the command was sent ignore its callback ... */
+                       c->func = NULL;
+                       head = c;
+                       continue;
+               }
+
+               first = FALSE;
+               command_destroy(c);
+       }
+
+       if (head) {
+               /* ... and put it back in the queue */
+               g_queue_push_head(queue, head);
+       }
+
+       return TRUE;
+}
+
+gboolean g_attrib_cancel_all(GAttrib *attrib)
+{
+       gboolean ret;
+
+       if (attrib == NULL)
+               return FALSE;
+
+       ret = cancel_all_per_queue(attrib->requests);
+       ret = cancel_all_per_queue(attrib->responses) && ret;
+
+       return ret;
+}
+
+gboolean g_attrib_set_debug(GAttrib *attrib,
+               GAttribDebugFunc func, gpointer user_data)
+{
+       return TRUE;
+}
+
+uint8_t *g_attrib_get_buffer(GAttrib *attrib, int *len)
+{
+       if (len == NULL)
+               return NULL;
+
+       *len = attrib->buflen;
+
+       return attrib->buf;
+}
+
+gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)
+{
+       if (mtu < ATT_DEFAULT_LE_MTU)
+               return FALSE;
+
+       attrib->buf = g_realloc(attrib->buf, mtu);
+
+       attrib->buflen = mtu;
+
+       return TRUE;
+}
+
+guint g_attrib_register(GAttrib *attrib, guint8 opcode,
+                               GAttribNotifyFunc func, gpointer user_data,
+                               GDestroyNotify notify)
+{
+       static guint next_evt_id = 0;
+       struct event *event;
+
+       event = g_try_new0(struct event, 1);
+       if (event == NULL)
+               return 0;
+
+       event->expected = opcode;
+       event->func = func;
+       event->user_data = user_data;
+       event->notify = notify;
+       event->id = ++next_evt_id;
+
+       attrib->events = g_slist_append(attrib->events, event);
+
+       return event->id;
+}
+
+static gint event_cmp_by_id(gconstpointer a, gconstpointer b)
+{
+       const struct event *evt = a;
+       guint id = GPOINTER_TO_UINT(b);
+
+       return evt->id - id;
+}
+
+gboolean g_attrib_is_encrypted(GAttrib *attrib)
+{
+       BtIOSecLevel sec_level;
+
+       if (!bt_io_get(attrib->io, BT_IO_L2CAP, NULL,
+                       BT_IO_OPT_SEC_LEVEL, &sec_level,
+                       BT_IO_OPT_INVALID))
+               return FALSE;
+
+       return sec_level > BT_IO_SEC_LOW;
+}
+
+gboolean g_attrib_unregister(GAttrib *attrib, guint id)
+{
+       struct event *evt;
+       GSList *l;
+
+       l = g_slist_find_custom(attrib->events, GUINT_TO_POINTER(id),
+                                                       event_cmp_by_id);
+       if (l == NULL)
+               return FALSE;
+
+       evt = l->data;
+
+       attrib->events = g_slist_remove(attrib->events, evt);
+
+       if (evt->notify)
+               evt->notify(evt->user_data);
+
+       g_free(evt);
+
+       return TRUE;
+}
+
+gboolean g_attrib_unregister_all(GAttrib *attrib)
+{
+       GSList *l;
+
+       if (attrib->events == NULL)
+               return FALSE;
+
+       for (l = attrib->events; l; l = l->next) {
+               struct event *evt = l->data;
+
+               if (evt->notify)
+                       evt->notify(evt->user_data);
+
+               g_free(evt);
+       }
+
+       g_slist_free(attrib->events);
+       attrib->events = NULL;
+
+       return TRUE;
+}
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
new file mode 100644 (file)
index 0000000..f73b741
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef __GATTRIB_H
+#define __GATTRIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GATTRIB_ALL_EVENTS 0xFF
+#define GATTRIB_ALL_REQS 0xFE
+
+struct _GAttrib;
+typedef struct _GAttrib GAttrib;
+
+typedef void (*GAttribResultFunc) (guint8 status, const guint8 *pdu,
+                                       guint16 len, gpointer user_data);
+typedef void (*GAttribDisconnectFunc)(gpointer user_data);
+typedef void (*GAttribDebugFunc)(const char *str, gpointer user_data);
+typedef void (*GAttribNotifyFunc)(const guint8 *pdu, guint16 len,
+                                                       gpointer user_data);
+
+GAttrib *g_attrib_new(GIOChannel *io);
+GAttrib *g_attrib_ref(GAttrib *attrib);
+void g_attrib_unref(GAttrib *attrib);
+
+GIOChannel *g_attrib_get_channel(GAttrib *attrib);
+
+gboolean g_attrib_set_destroy_function(GAttrib *attrib,
+               GDestroyNotify destroy, gpointer user_data);
+
+guint g_attrib_send(GAttrib *attrib, guint id, guint8 opcode,
+                       const guint8 *pdu, guint16 len, GAttribResultFunc func,
+                       gpointer user_data, GDestroyNotify notify);
+
+gboolean g_attrib_cancel(GAttrib *attrib, guint id);
+gboolean g_attrib_cancel_all(GAttrib *attrib);
+
+gboolean g_attrib_set_debug(GAttrib *attrib,
+               GAttribDebugFunc func, gpointer user_data);
+
+guint g_attrib_register(GAttrib *attrib, guint8 opcode,
+               GAttribNotifyFunc func, gpointer user_data,
+                                       GDestroyNotify notify);
+
+gboolean g_attrib_is_encrypted(GAttrib *attrib);
+
+uint8_t *g_attrib_get_buffer(GAttrib *attrib, int *len);
+gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu);
+
+gboolean g_attrib_unregister(GAttrib *attrib, guint id);
+gboolean g_attrib_unregister_all(GAttrib *attrib);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/attrib/gatttool.c b/attrib/gatttool.c
new file mode 100644 (file)
index 0000000..8a43ec1
--- /dev/null
@@ -0,0 +1,628 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/uuid.h>
+
+#include "att.h"
+#include "btio.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "gatttool.h"
+
+static gchar *opt_src = NULL;
+static gchar *opt_dst = NULL;
+static gchar *opt_dst_type = NULL;
+static gchar *opt_value = NULL;
+static gchar *opt_sec_level = NULL;
+static bt_uuid_t *opt_uuid = NULL;
+static int opt_start = 0x0001;
+static int opt_end = 0xffff;
+static int opt_handle = -1;
+static int opt_mtu = 0;
+static int opt_psm = 0;
+static int opt_offset = 0;
+static gboolean opt_primary = FALSE;
+static gboolean opt_characteristics = FALSE;
+static gboolean opt_char_read = FALSE;
+static gboolean opt_listen = FALSE;
+static gboolean opt_char_desc = FALSE;
+static gboolean opt_char_write = FALSE;
+static gboolean opt_char_write_req = FALSE;
+static gboolean opt_interactive = FALSE;
+static GMainLoop *event_loop;
+static gboolean got_error = FALSE;
+static GSourceFunc operation;
+
+struct characteristic_data {
+       GAttrib *attrib;
+       uint16_t start;
+       uint16_t end;
+};
+
+static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+       GAttrib *attrib = user_data;
+       uint8_t opdu[ATT_MAX_MTU];
+       uint16_t handle, i, olen = 0;
+
+       handle = att_get_u16(&pdu[1]);
+
+       switch (pdu[0]) {
+       case ATT_OP_HANDLE_NOTIFY:
+               g_print("Notification handle = 0x%04x value: ", handle);
+               break;
+       case ATT_OP_HANDLE_IND:
+               g_print("Indication   handle = 0x%04x value: ", handle);
+               break;
+       default:
+               g_print("Invalid opcode\n");
+               return;
+       }
+
+       for (i = 3; i < len; i++)
+               g_print("%02x ", pdu[i]);
+
+       g_print("\n");
+
+       if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
+               return;
+
+       olen = enc_confirmation(opdu, sizeof(opdu));
+
+       if (olen > 0)
+               g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
+}
+
+static gboolean listen_start(gpointer user_data)
+{
+       GAttrib *attrib = user_data;
+
+       g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler,
+                                                       attrib, NULL);
+       g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler,
+                                                       attrib, NULL);
+
+       return FALSE;
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+       GAttrib *attrib;
+
+       if (err) {
+               g_printerr("%s\n", err->message);
+               got_error = TRUE;
+               g_main_loop_quit(event_loop);
+       }
+
+       attrib = g_attrib_new(io);
+
+       if (opt_listen)
+               g_idle_add(listen_start, attrib);
+
+       operation(attrib);
+}
+
+static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
+{
+       GSList *l;
+
+       if (status) {
+               g_printerr("Discover all primary services failed: %s\n",
+                                                       att_ecode2str(status));
+               goto done;
+       }
+
+       for (l = services; l; l = l->next) {
+               struct gatt_primary *prim = l->data;
+               g_print("attr handle = 0x%04x, end grp handle = 0x%04x "
+                       "uuid: %s\n", prim->range.start, prim->range.end, prim->uuid);
+       }
+
+done:
+       g_main_loop_quit(event_loop);
+}
+
+static void primary_by_uuid_cb(GSList *ranges, guint8 status,
+                                                       gpointer user_data)
+{
+       GSList *l;
+
+       if (status != 0) {
+               g_printerr("Discover primary services by UUID failed: %s\n",
+                                                       att_ecode2str(status));
+               goto done;
+       }
+
+       for (l = ranges; l; l = l->next) {
+               struct att_range *range = l->data;
+               g_print("Starting handle: %04x Ending handle: %04x\n",
+                                               range->start, range->end);
+       }
+
+done:
+       g_main_loop_quit(event_loop);
+}
+
+static gboolean primary(gpointer user_data)
+{
+       GAttrib *attrib = user_data;
+
+       if (opt_uuid)
+               gatt_discover_primary(attrib, opt_uuid, primary_by_uuid_cb,
+                                                                       NULL);
+       else
+               gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
+
+       return FALSE;
+}
+
+static void char_discovered_cb(GSList *characteristics, guint8 status,
+                                                       gpointer user_data)
+{
+       GSList *l;
+
+       if (status) {
+               g_printerr("Discover all characteristics failed: %s\n",
+                                                       att_ecode2str(status));
+               goto done;
+       }
+
+       for (l = characteristics; l; l = l->next) {
+               struct gatt_char *chars = l->data;
+
+               g_print("handle = 0x%04x, char properties = 0x%02x, char value "
+                       "handle = 0x%04x, uuid = %s\n", chars->handle,
+                       chars->properties, chars->value_handle, chars->uuid);
+       }
+
+done:
+       g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics(gpointer user_data)
+{
+       GAttrib *attrib = user_data;
+
+       gatt_discover_char(attrib, opt_start, opt_end, opt_uuid,
+                                               char_discovered_cb, NULL);
+
+       return FALSE;
+}
+
+static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       uint8_t value[ATT_MAX_MTU];
+       int i, vlen;
+
+       if (status != 0) {
+               g_printerr("Characteristic value/descriptor read failed: %s\n",
+                                                       att_ecode2str(status));
+               goto done;
+       }
+       if (!dec_read_resp(pdu, plen, value, &vlen)) {
+               g_printerr("Protocol error\n");
+               goto done;
+       }
+       g_print("Characteristic value/descriptor: ");
+       for (i = 0; i < vlen; i++)
+               g_print("%02x ", value[i]);
+       g_print("\n");
+
+done:
+       if (opt_listen == FALSE)
+               g_main_loop_quit(event_loop);
+}
+
+static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
+                                       guint16 plen, gpointer user_data)
+{
+       struct characteristic_data *char_data = user_data;
+       struct att_data_list *list;
+       int i;
+
+       if (status == ATT_ECODE_ATTR_NOT_FOUND &&
+                                       char_data->start != opt_start)
+               goto done;
+
+       if (status != 0) {
+               g_printerr("Read characteristics by UUID failed: %s\n",
+                                                       att_ecode2str(status));
+               goto done;
+       }
+
+       list = dec_read_by_type_resp(pdu, plen);
+       if (list == NULL)
+               goto done;
+
+       for (i = 0; i < list->num; i++) {
+               uint8_t *value = list->data[i];
+               int j;
+
+               char_data->start = att_get_u16(value) + 1;
+
+               g_print("handle: 0x%04x \t value: ", att_get_u16(value));
+               value += 2;
+               for (j = 0; j < list->len - 2; j++, value++)
+                       g_print("%02x ", *value);
+               g_print("\n");
+       }
+
+       att_data_list_free(list);
+
+done:
+       g_free(char_data);
+       g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_read(gpointer user_data)
+{
+       GAttrib *attrib = user_data;
+
+       if (opt_uuid != NULL) {
+               struct characteristic_data *char_data;
+
+               char_data = g_new(struct characteristic_data, 1);
+               char_data->attrib = attrib;
+               char_data->start = opt_start;
+               char_data->end = opt_end;
+
+               gatt_read_char_by_uuid(attrib, opt_start, opt_end, opt_uuid,
+                                               char_read_by_uuid_cb, char_data);
+
+               return FALSE;
+       }
+
+       if (opt_handle <= 0) {
+               g_printerr("A valid handle is required\n");
+               g_main_loop_quit(event_loop);
+               return FALSE;
+       }
+
+       gatt_read_char(attrib, opt_handle, opt_offset, char_read_cb, attrib);
+
+       return FALSE;
+}
+
+static void mainloop_quit(gpointer user_data)
+{
+       uint8_t *value = user_data;
+
+       g_free(value);
+       g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_write(gpointer user_data)
+{
+       GAttrib *attrib = user_data;
+       uint8_t *value;
+       size_t len;
+
+       if (opt_handle <= 0) {
+               g_printerr("A valid handle is required\n");
+               goto error;
+       }
+
+       if (opt_value == NULL || opt_value[0] == '\0') {
+               g_printerr("A value is required\n");
+               goto error;
+       }
+
+       len = gatt_attr_data_from_string(opt_value, &value);
+       if (len == 0) {
+               g_printerr("Invalid value\n");
+               goto error;
+       }
+
+       gatt_write_cmd(attrib, opt_handle, value, len, mainloop_quit, value);
+
+       return FALSE;
+
+error:
+       g_main_loop_quit(event_loop);
+       return FALSE;
+}
+
+static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       if (status != 0) {
+               g_printerr("Characteristic Write Request failed: "
+                                               "%s\n", att_ecode2str(status));
+               goto done;
+       }
+
+       if (!dec_write_resp(pdu, plen)) {
+               g_printerr("Protocol error\n");
+               goto done;
+       }
+
+       g_print("Characteristic value was written successfully\n");
+
+done:
+       if (opt_listen == FALSE)
+               g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_write_req(gpointer user_data)
+{
+       GAttrib *attrib = user_data;
+       uint8_t *value;
+       size_t len;
+
+       if (opt_handle <= 0) {
+               g_printerr("A valid handle is required\n");
+               goto error;
+       }
+
+       if (opt_value == NULL || opt_value[0] == '\0') {
+               g_printerr("A value is required\n");
+               goto error;
+       }
+
+       len = gatt_attr_data_from_string(opt_value, &value);
+       if (len == 0) {
+               g_printerr("Invalid value\n");
+               goto error;
+       }
+
+       gatt_write_char(attrib, opt_handle, value, len, char_write_req_cb,
+                                                                       NULL);
+
+       return FALSE;
+
+error:
+       g_main_loop_quit(event_loop);
+       return FALSE;
+}
+
+static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       struct att_data_list *list;
+       guint8 format;
+       int i;
+
+       if (status != 0) {
+               g_printerr("Discover all characteristic descriptors failed: "
+                                               "%s\n", att_ecode2str(status));
+               goto done;
+       }
+
+       list = dec_find_info_resp(pdu, plen, &format);
+       if (list == NULL)
+               goto done;
+
+       for (i = 0; i < list->num; i++) {
+               char uuidstr[MAX_LEN_UUID_STR];
+               uint16_t handle;
+               uint8_t *value;
+               bt_uuid_t uuid;
+
+               value = list->data[i];
+               handle = att_get_u16(value);
+
+               if (format == 0x01)
+                       uuid = att_get_uuid16(&value[2]);
+               else
+                       uuid = att_get_uuid128(&value[2]);
+
+               bt_uuid_to_string(&uuid, uuidstr, MAX_LEN_UUID_STR);
+               g_print("handle = 0x%04x, uuid = %s\n", handle, uuidstr);
+       }
+
+       att_data_list_free(list);
+
+done:
+       if (opt_listen == FALSE)
+               g_main_loop_quit(event_loop);
+}
+
+static gboolean characteristics_desc(gpointer user_data)
+{
+       GAttrib *attrib = user_data;
+
+       gatt_find_info(attrib, opt_start, opt_end, char_desc_cb, NULL);
+
+       return FALSE;
+}
+
+static gboolean parse_uuid(const char *key, const char *value,
+                               gpointer user_data, GError **error)
+{
+       if (!value)
+               return FALSE;
+
+       opt_uuid = g_try_malloc(sizeof(bt_uuid_t));
+       if (opt_uuid == NULL)
+               return FALSE;
+
+       if (bt_string_to_uuid(opt_uuid, value) < 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static GOptionEntry primary_char_options[] = {
+       { "start", 's' , 0, G_OPTION_ARG_INT, &opt_start,
+               "Starting handle(optional)", "0x0001" },
+       { "end", 'e' , 0, G_OPTION_ARG_INT, &opt_end,
+               "Ending handle(optional)", "0xffff" },
+       { "uuid", 'u', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK,
+               parse_uuid, "UUID16 or UUID128(optional)", "0x1801"},
+       { NULL },
+};
+
+static GOptionEntry char_rw_options[] = {
+       { "handle", 'a' , 0, G_OPTION_ARG_INT, &opt_handle,
+               "Read/Write characteristic by handle(required)", "0x0001" },
+       { "value", 'n' , 0, G_OPTION_ARG_STRING, &opt_value,
+               "Write characteristic value (required for write operation)",
+               "0x0001" },
+       { "offset", 'o', 0, G_OPTION_ARG_INT, &opt_offset,
+               "Offset to long read characteristic by handle", "N"},
+       {NULL},
+};
+
+static GOptionEntry gatt_options[] = {
+       { "primary", 0, 0, G_OPTION_ARG_NONE, &opt_primary,
+               "Primary Service Discovery", NULL },
+       { "characteristics", 0, 0, G_OPTION_ARG_NONE, &opt_characteristics,
+               "Characteristics Discovery", NULL },
+       { "char-read", 0, 0, G_OPTION_ARG_NONE, &opt_char_read,
+               "Characteristics Value/Descriptor Read", NULL },
+       { "char-write", 0, 0, G_OPTION_ARG_NONE, &opt_char_write,
+               "Characteristics Value Write Without Response (Write Command)",
+               NULL },
+       { "char-write-req", 0, 0, G_OPTION_ARG_NONE, &opt_char_write_req,
+               "Characteristics Value Write (Write Request)", NULL },
+       { "char-desc", 0, 0, G_OPTION_ARG_NONE, &opt_char_desc,
+               "Characteristics Descriptor Discovery", NULL },
+       { "listen", 0, 0, G_OPTION_ARG_NONE, &opt_listen,
+               "Listen for notifications and indications", NULL },
+       { "interactive", 'I', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
+               &opt_interactive, "Use interactive mode", NULL },
+       { NULL },
+};
+
+static GOptionEntry options[] = {
+       { "adapter", 'i', 0, G_OPTION_ARG_STRING, &opt_src,
+               "Specify local adapter interface", "hciX" },
+       { "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
+               "Specify remote Bluetooth address", "MAC" },
+       { "addr-type", 't', 0, G_OPTION_ARG_STRING, &opt_dst_type,
+               "Set LE address type. Default: public", "[public | random]"},
+       { "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
+               "Specify the MTU size", "MTU" },
+       { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
+               "Specify the PSM for GATT/ATT over BR/EDR", "PSM" },
+       { "sec-level", 'l', 0, G_OPTION_ARG_STRING, &opt_sec_level,
+               "Set security level. Default: low", "[low | medium | high]"},
+       { NULL },
+};
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+       GOptionGroup *gatt_group, *params_group, *char_rw_group;
+       GError *gerr = NULL;
+       GIOChannel *chan;
+
+       opt_dst_type = g_strdup("public");
+       opt_sec_level = g_strdup("low");
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       /* GATT commands */
+       gatt_group = g_option_group_new("gatt", "GATT commands",
+                                       "Show all GATT commands", NULL, NULL);
+       g_option_context_add_group(context, gatt_group);
+       g_option_group_add_entries(gatt_group, gatt_options);
+
+       /* Primary Services and Characteristics arguments */
+       params_group = g_option_group_new("params",
+                       "Primary Services/Characteristics arguments",
+                       "Show all Primary Services/Characteristics arguments",
+                       NULL, NULL);
+       g_option_context_add_group(context, params_group);
+       g_option_group_add_entries(params_group, primary_char_options);
+
+       /* Characteristics value/descriptor read/write arguments */
+       char_rw_group = g_option_group_new("char-read-write",
+               "Characteristics Value/Descriptor Read/Write arguments",
+               "Show all Characteristics Value/Descriptor Read/Write "
+               "arguments",
+               NULL, NULL);
+       g_option_context_add_group(context, char_rw_group);
+       g_option_group_add_entries(char_rw_group, char_rw_options);
+
+       if (g_option_context_parse(context, &argc, &argv, &gerr) == FALSE) {
+               g_printerr("%s\n", gerr->message);
+               g_error_free(gerr);
+       }
+
+       if (opt_interactive) {
+               interactive(opt_src, opt_dst, opt_dst_type, opt_psm);
+               goto done;
+       }
+
+       if (opt_primary)
+               operation = primary;
+       else if (opt_characteristics)
+               operation = characteristics;
+       else if (opt_char_read)
+               operation = characteristics_read;
+       else if (opt_char_write)
+               operation = characteristics_write;
+       else if (opt_char_write_req)
+               operation = characteristics_write_req;
+       else if (opt_char_desc)
+               operation = characteristics_desc;
+       else {
+               gchar *help = g_option_context_get_help(context, TRUE, NULL);
+               g_print("%s\n", help);
+               g_free(help);
+               got_error = TRUE;
+               goto done;
+       }
+
+       chan = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
+                                       opt_psm, opt_mtu, connect_cb);
+       if (chan == NULL) {
+               got_error = TRUE;
+               goto done;
+       }
+
+       event_loop = g_main_loop_new(NULL, FALSE);
+
+       g_main_loop_run(event_loop);
+
+       g_main_loop_unref(event_loop);
+
+done:
+       g_option_context_free(context);
+       g_free(opt_src);
+       g_free(opt_dst);
+       g_free(opt_uuid);
+       g_free(opt_sec_level);
+
+       if (got_error)
+               exit(EXIT_FAILURE);
+       else
+               exit(EXIT_SUCCESS);
+}
diff --git a/attrib/gatttool.h b/attrib/gatttool.h
new file mode 100644 (file)
index 0000000..a38339b
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int interactive(const gchar *src, const gchar *dst, const gchar *dst_type,
+               gboolean le);
+GIOChannel *gatt_connect(const gchar *src, const gchar *dst,
+                       const gchar *dst_type, const gchar *sec_level,
+                       int psm, int mtu, BtIOConnect connect_cb);
+size_t gatt_attr_data_from_string(const char *str, uint8_t **data);
diff --git a/attrib/interactive.c b/attrib/interactive.c
new file mode 100644 (file)
index 0000000..0a01cdf
--- /dev/null
@@ -0,0 +1,890 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include <bluetooth/uuid.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "att.h"
+#include "btio.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "gatttool.h"
+
+static GIOChannel *iochannel = NULL;
+static GAttrib *attrib = NULL;
+static GMainLoop *event_loop;
+static GString *prompt;
+
+static gchar *opt_src = NULL;
+static gchar *opt_dst = NULL;
+static gchar *opt_dst_type = NULL;
+static gchar *opt_sec_level = NULL;
+static int opt_psm = 0;
+static int opt_mtu = 0;
+
+struct characteristic_data {
+       uint16_t orig_start;
+       uint16_t start;
+       uint16_t end;
+       bt_uuid_t uuid;
+};
+
+static void cmd_help(int argcp, char **argvp);
+
+enum state {
+       STATE_DISCONNECTED,
+       STATE_CONNECTING,
+       STATE_CONNECTED
+} conn_state;
+
+static char *get_prompt(void)
+{
+       if (conn_state == STATE_CONNECTING) {
+               g_string_assign(prompt, "Connecting... ");
+               return prompt->str;
+       }
+
+       if (conn_state == STATE_CONNECTED)
+               g_string_assign(prompt, "[CON]");
+       else
+               g_string_assign(prompt, "[   ]");
+
+       if (opt_dst)
+               g_string_append_printf(prompt, "[%17s]", opt_dst);
+       else
+               g_string_append_printf(prompt, "[%17s]", "");
+
+       if (opt_psm)
+               g_string_append(prompt, "[BR]");
+       else
+               g_string_append(prompt, "[LE]");
+
+       g_string_append(prompt, "> ");
+
+       return prompt->str;
+}
+
+
+static void set_state(enum state st)
+{
+       conn_state = st;
+       rl_set_prompt(get_prompt());
+       rl_redisplay();
+}
+
+static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+       uint8_t opdu[ATT_MAX_MTU];
+       uint16_t handle, i, olen;
+
+       handle = att_get_u16(&pdu[1]);
+
+       printf("\n");
+       switch (pdu[0]) {
+       case ATT_OP_HANDLE_NOTIFY:
+               printf("Notification handle = 0x%04x value: ", handle);
+               break;
+       case ATT_OP_HANDLE_IND:
+               printf("Indication   handle = 0x%04x value: ", handle);
+               break;
+       default:
+               printf("Invalid opcode\n");
+               return;
+       }
+
+       for (i = 3; i < len; i++)
+               printf("%02x ", pdu[i]);
+
+       printf("\n");
+       rl_forced_update_display();
+
+       if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
+               return;
+
+       olen = enc_confirmation(opdu, sizeof(opdu));
+
+       if (olen > 0)
+               g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+       if (err) {
+               printf("connect error: %s\n", err->message);
+               set_state(STATE_DISCONNECTED);
+               return;
+       }
+
+       attrib = g_attrib_new(iochannel);
+       g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler,
+                                                       attrib, NULL);
+       g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler,
+                                                       attrib, NULL);
+       set_state(STATE_CONNECTED);
+}
+
+static void disconnect_io()
+{
+       if (conn_state == STATE_DISCONNECTED)
+               return;
+
+       g_attrib_unref(attrib);
+       attrib = NULL;
+       opt_mtu = 0;
+
+       g_io_channel_shutdown(iochannel, FALSE, NULL);
+       g_io_channel_unref(iochannel);
+       iochannel = NULL;
+
+       set_state(STATE_DISCONNECTED);
+}
+
+static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
+{
+       GSList *l;
+
+       if (status) {
+               printf("Discover all primary services failed: %s\n",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       printf("\n");
+       for (l = services; l; l = l->next) {
+               struct gatt_primary *prim = l->data;
+               printf("attr handle: 0x%04x, end grp handle: 0x%04x "
+                       "uuid: %s\n", prim->range.start, prim->range.end, prim->uuid);
+       }
+
+       rl_forced_update_display();
+}
+
+static void primary_by_uuid_cb(GSList *ranges, guint8 status,
+                                                       gpointer user_data)
+{
+       GSList *l;
+
+       if (status) {
+               printf("Discover primary services by UUID failed: %s\n",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       printf("\n");
+       for (l = ranges; l; l = l->next) {
+               struct att_range *range = l->data;
+               g_print("Starting handle: 0x%04x Ending handle: 0x%04x\n",
+                                               range->start, range->end);
+       }
+
+       rl_forced_update_display();
+}
+
+static void char_cb(GSList *characteristics, guint8 status, gpointer user_data)
+{
+       GSList *l;
+
+       if (status) {
+               printf("Discover all characteristics failed: %s\n",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       printf("\n");
+       for (l = characteristics; l; l = l->next) {
+               struct gatt_char *chars = l->data;
+
+               printf("handle: 0x%04x, char properties: 0x%02x, char value "
+                               "handle: 0x%04x, uuid: %s\n", chars->handle,
+                               chars->properties, chars->value_handle,
+                               chars->uuid);
+       }
+
+       rl_forced_update_display();
+}
+
+static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       struct att_data_list *list;
+       guint8 format;
+       int i;
+
+       if (status != 0) {
+               printf("Discover all characteristic descriptors failed: "
+                                               "%s\n", att_ecode2str(status));
+               return;
+       }
+
+       list = dec_find_info_resp(pdu, plen, &format);
+       if (list == NULL)
+               return;
+
+       printf("\n");
+       for (i = 0; i < list->num; i++) {
+               char uuidstr[MAX_LEN_UUID_STR];
+               uint16_t handle;
+               uint8_t *value;
+               bt_uuid_t uuid;
+
+               value = list->data[i];
+               handle = att_get_u16(value);
+
+               if (format == 0x01)
+                       uuid = att_get_uuid16(&value[2]);
+               else
+                       uuid = att_get_uuid128(&value[2]);
+
+               bt_uuid_to_string(&uuid, uuidstr, MAX_LEN_UUID_STR);
+               printf("handle: 0x%04x, uuid: %s\n", handle, uuidstr);
+       }
+
+       att_data_list_free(list);
+
+       rl_forced_update_display();
+}
+
+static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       uint8_t value[ATT_MAX_MTU];
+       int i, vlen;
+
+       if (status != 0) {
+               printf("Characteristic value/descriptor read failed: %s\n",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       if (!dec_read_resp(pdu, plen, value, &vlen)) {
+               printf("Protocol error\n");
+               return;
+       }
+
+       printf("\nCharacteristic value/descriptor: ");
+       for (i = 0; i < vlen; i++)
+               printf("%02x ", value[i]);
+       printf("\n");
+
+       rl_forced_update_display();
+}
+
+static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
+                                       guint16 plen, gpointer user_data)
+{
+       struct characteristic_data *char_data = user_data;
+       struct att_data_list *list;
+       int i;
+
+       if (status == ATT_ECODE_ATTR_NOT_FOUND &&
+                               char_data->start != char_data->orig_start)
+               goto done;
+
+       if (status != 0) {
+               printf("Read characteristics by UUID failed: %s\n",
+                                                       att_ecode2str(status));
+               goto done;
+       }
+
+       list = dec_read_by_type_resp(pdu, plen);
+       if (list == NULL)
+               goto done;
+
+       for (i = 0; i < list->num; i++) {
+               uint8_t *value = list->data[i];
+               int j;
+
+               char_data->start = att_get_u16(value) + 1;
+
+               printf("\nhandle: 0x%04x \t value: ", att_get_u16(value));
+               value += 2;
+               for (j = 0; j < list->len - 2; j++, value++)
+                       printf("%02x ", *value);
+               printf("\n");
+       }
+
+       att_data_list_free(list);
+
+       rl_forced_update_display();
+
+done:
+       g_free(char_data);
+}
+
+static void cmd_exit(int argcp, char **argvp)
+{
+       rl_callback_handler_remove();
+       g_main_loop_quit(event_loop);
+}
+
+static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond,
+                               gpointer user_data)
+{
+       disconnect_io();
+
+       return FALSE;
+}
+
+static void cmd_connect(int argcp, char **argvp)
+{
+       if (conn_state != STATE_DISCONNECTED)
+               return;
+
+       if (argcp > 1) {
+               g_free(opt_dst);
+               opt_dst = g_strdup(argvp[1]);
+
+               g_free(opt_dst_type);
+               if (argcp > 2)
+                       opt_dst_type = g_strdup(argvp[2]);
+               else
+                       opt_dst_type = g_strdup("public");
+       }
+
+       if (opt_dst == NULL) {
+               printf("Remote Bluetooth address required\n");
+               return;
+       }
+
+       set_state(STATE_CONNECTING);
+       iochannel = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
+                                               opt_psm, opt_mtu, connect_cb);
+       if (iochannel == NULL)
+               set_state(STATE_DISCONNECTED);
+       else
+               g_io_add_watch(iochannel, G_IO_HUP, channel_watcher, NULL);
+}
+
+static void cmd_disconnect(int argcp, char **argvp)
+{
+       disconnect_io();
+}
+
+static void cmd_primary(int argcp, char **argvp)
+{
+       bt_uuid_t uuid;
+
+       if (conn_state != STATE_CONNECTED) {
+               printf("Command failed: disconnected\n");
+               return;
+       }
+
+       if (argcp == 1) {
+               gatt_discover_primary(attrib, NULL, primary_all_cb, NULL);
+               return;
+       }
+
+       if (bt_string_to_uuid(&uuid, argvp[1]) < 0) {
+               printf("Invalid UUID\n");
+               return;
+       }
+
+       gatt_discover_primary(attrib, &uuid, primary_by_uuid_cb, NULL);
+}
+
+static int strtohandle(const char *src)
+{
+       char *e;
+       int dst;
+
+       errno = 0;
+       dst = strtoll(src, &e, 16);
+       if (errno != 0 || *e != '\0')
+               return -EINVAL;
+
+       return dst;
+}
+
+static void cmd_char(int argcp, char **argvp)
+{
+       int start = 0x0001;
+       int end = 0xffff;
+
+       if (conn_state != STATE_CONNECTED) {
+               printf("Command failed: disconnected\n");
+               return;
+       }
+
+       if (argcp > 1) {
+               start = strtohandle(argvp[1]);
+               if (start < 0) {
+                       printf("Invalid start handle: %s\n", argvp[1]);
+                       return;
+               }
+       }
+
+       if (argcp > 2) {
+               end = strtohandle(argvp[2]);
+               if (end < 0) {
+                       printf("Invalid end handle: %s\n", argvp[2]);
+                       return;
+               }
+       }
+
+       if (argcp > 3) {
+               bt_uuid_t uuid;
+
+               if (bt_string_to_uuid(&uuid, argvp[3]) < 0) {
+                       printf("Invalid UUID\n");
+                       return;
+               }
+
+               gatt_discover_char(attrib, start, end, &uuid, char_cb, NULL);
+               return;
+       }
+
+       gatt_discover_char(attrib, start, end, NULL, char_cb, NULL);
+}
+
+static void cmd_char_desc(int argcp, char **argvp)
+{
+       int start = 0x0001;
+       int end = 0xffff;
+
+       if (conn_state != STATE_CONNECTED) {
+               printf("Command failed: disconnected\n");
+               return;
+       }
+
+       if (argcp > 1) {
+               start = strtohandle(argvp[1]);
+               if (start < 0) {
+                       printf("Invalid start handle: %s\n", argvp[1]);
+                       return;
+               }
+       }
+
+       if (argcp > 2) {
+               end = strtohandle(argvp[2]);
+               if (end < 0) {
+                       printf("Invalid end handle: %s\n", argvp[2]);
+                       return;
+               }
+       }
+
+       gatt_find_info(attrib, start, end, char_desc_cb, NULL);
+}
+
+static void cmd_read_hnd(int argcp, char **argvp)
+{
+       int handle;
+       int offset = 0;
+
+       if (conn_state != STATE_CONNECTED) {
+               printf("Command failed: disconnected\n");
+               return;
+       }
+
+       if (argcp < 2) {
+               printf("Missing argument: handle\n");
+               return;
+       }
+
+       handle = strtohandle(argvp[1]);
+       if (handle < 0) {
+               printf("Invalid handle: %s\n", argvp[1]);
+               return;
+       }
+
+       if (argcp > 2) {
+               char *e;
+
+               errno = 0;
+               offset = strtol(argvp[2], &e, 0);
+               if (errno != 0 || *e != '\0') {
+                       printf("Invalid offset: %s\n", argvp[2]);
+                       return;
+               }
+       }
+
+       gatt_read_char(attrib, handle, offset, char_read_cb, attrib);
+}
+
+static void cmd_read_uuid(int argcp, char **argvp)
+{
+       struct characteristic_data *char_data;
+       int start = 0x0001;
+       int end = 0xffff;
+       bt_uuid_t uuid;
+
+       if (conn_state != STATE_CONNECTED) {
+               printf("Command failed: disconnected\n");
+               return;
+       }
+
+       if (argcp < 2) {
+               printf("Missing argument: UUID\n");
+               return;
+       }
+
+       if (bt_string_to_uuid(&uuid, argvp[1]) < 0) {
+               printf("Invalid UUID\n");
+               return;
+       }
+
+       if (argcp > 2) {
+               start = strtohandle(argvp[2]);
+               if (start < 0) {
+                       printf("Invalid start handle: %s\n", argvp[1]);
+                       return;
+               }
+       }
+
+       if (argcp > 3) {
+               end = strtohandle(argvp[3]);
+               if (end < 0) {
+                       printf("Invalid end handle: %s\n", argvp[2]);
+                       return;
+               }
+       }
+
+       char_data = g_new(struct characteristic_data, 1);
+       char_data->orig_start = start;
+       char_data->start = start;
+       char_data->end = end;
+       char_data->uuid = uuid;
+
+       gatt_read_char_by_uuid(attrib, start, end, &char_data->uuid,
+                                       char_read_by_uuid_cb, char_data);
+}
+
+static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       if (status != 0) {
+               printf("Characteristic Write Request failed: "
+                                               "%s\n", att_ecode2str(status));
+               return;
+       }
+
+       if (!dec_write_resp(pdu, plen)) {
+               printf("Protocol error\n");
+               return;
+       }
+
+       printf("Characteristic value was written successfully\n");
+}
+
+static void cmd_char_write(int argcp, char **argvp)
+{
+       uint8_t *value;
+       size_t plen;
+       int handle;
+
+       if (conn_state != STATE_CONNECTED) {
+               printf("Command failed: disconnected\n");
+               return;
+       }
+
+       if (argcp < 3) {
+               printf("Usage: %s <handle> <new value>\n", argvp[0]);
+               return;
+       }
+
+       handle = strtohandle(argvp[1]);
+       if (handle <= 0) {
+               printf("A valid handle is required\n");
+               return;
+       }
+
+       plen = gatt_attr_data_from_string(argvp[2], &value);
+       if (plen == 0) {
+               g_printerr("Invalid value\n");
+               return;
+       }
+
+       if (g_strcmp0("char-write-req", argvp[0]) == 0)
+               gatt_write_char(attrib, handle, value, plen,
+                                       char_write_req_cb, NULL);
+       else
+               gatt_write_char(attrib, handle, value, plen, NULL, NULL);
+
+       g_free(value);
+}
+
+static void cmd_sec_level(int argcp, char **argvp)
+{
+       GError *gerr = NULL;
+       BtIOSecLevel sec_level;
+
+       if (argcp < 2) {
+               printf("sec-level: %s\n", opt_sec_level);
+               return;
+       }
+
+       if (strcasecmp(argvp[1], "medium") == 0)
+               sec_level = BT_IO_SEC_MEDIUM;
+       else if (strcasecmp(argvp[1], "high") == 0)
+               sec_level = BT_IO_SEC_HIGH;
+       else if (strcasecmp(argvp[1], "low") == 0)
+               sec_level = BT_IO_SEC_LOW;
+       else {
+               printf("Allowed values: low | medium | high\n");
+               return;
+       }
+
+       g_free(opt_sec_level);
+       opt_sec_level = g_strdup(argvp[1]);
+
+       if (conn_state != STATE_CONNECTED)
+               return;
+
+       if (opt_psm) {
+               printf("It must be reconnected to this change take effect\n");
+               return;
+       }
+
+       bt_io_set(iochannel, BT_IO_L2CAP, &gerr,
+                       BT_IO_OPT_SEC_LEVEL, sec_level,
+                       BT_IO_OPT_INVALID);
+
+       if (gerr) {
+               printf("Error: %s\n", gerr->message);
+               g_error_free(gerr);
+       }
+}
+
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       uint16_t mtu;
+
+       if (status != 0) {
+               printf("Exchange MTU Request failed: %s\n",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       if (!dec_mtu_resp(pdu, plen, &mtu)) {
+               printf("Protocol error\n");
+               return;
+       }
+
+       mtu = MIN(mtu, opt_mtu);
+       /* Set new value for MTU in client */
+       if (g_attrib_set_mtu(attrib, mtu))
+               printf("MTU was exchanged successfully: %d\n", mtu);
+       else
+               printf("Error exchanging MTU\n");
+}
+
+static void cmd_mtu(int argcp, char **argvp)
+{
+       if (conn_state != STATE_CONNECTED) {
+               printf("Command failed: not connected.\n");
+               return;
+       }
+
+       if (opt_psm) {
+               printf("Command failed: operation is only available for LE"
+                                                       " transport.\n");
+               return;
+       }
+
+       if (argcp < 2) {
+               printf("Usage: mtu <value>\n");
+               return;
+       }
+
+       if (opt_mtu) {
+               printf("Command failed: MTU exchange can only occur once per"
+                                                       " connection.\n");
+               return;
+       }
+
+       errno = 0;
+       opt_mtu = strtoll(argvp[1], NULL, 0);
+       if (errno != 0 || opt_mtu < ATT_DEFAULT_LE_MTU) {
+               printf("Invalid value. Minimum MTU size is %d\n",
+                                                       ATT_DEFAULT_LE_MTU);
+               return;
+       }
+
+       gatt_exchange_mtu(attrib, opt_mtu, exchange_mtu_cb, NULL);
+}
+
+static struct {
+       const char *cmd;
+       void (*func)(int argcp, char **argvp);
+       const char *params;
+       const char *desc;
+} commands[] = {
+       { "help",               cmd_help,       "",
+               "Show this help"},
+       { "exit",               cmd_exit,       "",
+               "Exit interactive mode" },
+       { "quit",               cmd_exit,       "",
+               "Exit interactive mode" },
+       { "connect",            cmd_connect,    "[address [address type]]",
+               "Connect to a remote device" },
+       { "disconnect",         cmd_disconnect, "",
+               "Disconnect from a remote device" },
+       { "primary",            cmd_primary,    "[UUID]",
+               "Primary Service Discovery" },
+       { "characteristics",    cmd_char,       "[start hnd [end hnd [UUID]]]",
+               "Characteristics Discovery" },
+       { "char-desc",          cmd_char_desc,  "[start hnd] [end hnd]",
+               "Characteristics Descriptor Discovery" },
+       { "char-read-hnd",      cmd_read_hnd,   "<handle> [offset]",
+               "Characteristics Value/Descriptor Read by handle" },
+       { "char-read-uuid",     cmd_read_uuid,  "<UUID> [start hnd] [end hnd]",
+               "Characteristics Value/Descriptor Read by UUID" },
+       { "char-write-req",     cmd_char_write, "<handle> <new value>",
+               "Characteristic Value Write (Write Request)" },
+       { "char-write-cmd",     cmd_char_write, "<handle> <new value>",
+               "Characteristic Value Write (No response)" },
+       { "sec-level",          cmd_sec_level,  "[low | medium | high]",
+               "Set security level. Default: low" },
+       { "mtu",                cmd_mtu,        "<value>",
+               "Exchange MTU for GATT/ATT" },
+       { NULL, NULL, NULL}
+};
+
+static void cmd_help(int argcp, char **argvp)
+{
+       int i;
+
+       for (i = 0; commands[i].cmd; i++)
+               printf("%-15s %-30s %s\n", commands[i].cmd,
+                               commands[i].params, commands[i].desc);
+}
+
+static void parse_line(char *line_read)
+{
+       gchar **argvp;
+       int argcp;
+       int i;
+
+       if (line_read == NULL) {
+               printf("\n");
+               cmd_exit(0, NULL);
+               return;
+       }
+
+       line_read = g_strstrip(line_read);
+
+       if (*line_read == '\0')
+               return;
+
+       add_history(line_read);
+
+       g_shell_parse_argv(line_read, &argcp, &argvp, NULL);
+
+       for (i = 0; commands[i].cmd; i++)
+               if (strcasecmp(commands[i].cmd, argvp[0]) == 0)
+                       break;
+
+       if (commands[i].cmd)
+               commands[i].func(argcp, argvp);
+       else
+               printf("%s: command not found\n", argvp[0]);
+
+       g_strfreev(argvp);
+}
+
+static gboolean prompt_read(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+               g_io_channel_unref(chan);
+               return FALSE;
+       }
+
+       rl_callback_read_char();
+
+       return TRUE;
+}
+
+static char *completion_generator(const char *text, int state)
+{
+       static int index = 0, len = 0;
+       const char *cmd = NULL;
+
+       if (state == 0) {
+               index = 0;
+               len = strlen(text);
+       }
+
+       while ((cmd = commands[index].cmd) != NULL) {
+               index++;
+               if (strncmp(cmd, text, len) == 0)
+                       return strdup(cmd);
+       }
+
+       return NULL;
+}
+
+static char **commands_completion(const char *text, int start, int end)
+{
+       if (start == 0)
+               return rl_completion_matches(text, &completion_generator);
+       else
+               return NULL;
+}
+
+int interactive(const gchar *src, const gchar *dst,
+               const gchar *dst_type, int psm)
+{
+       GIOChannel *pchan;
+       gint events;
+
+       opt_sec_level = g_strdup("low");
+
+       opt_src = g_strdup(src);
+       opt_dst = g_strdup(dst);
+       opt_dst_type = g_strdup(dst_type);
+       opt_psm = psm;
+
+       prompt = g_string_new(NULL);
+
+       event_loop = g_main_loop_new(NULL, FALSE);
+
+       pchan = g_io_channel_unix_new(fileno(stdin));
+       g_io_channel_set_close_on_unref(pchan, TRUE);
+       events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+       g_io_add_watch(pchan, events, prompt_read, NULL);
+
+       rl_attempted_completion_function = commands_completion;
+       rl_callback_handler_install(get_prompt(), parse_line);
+
+       g_main_loop_run(event_loop);
+
+       rl_callback_handler_remove();
+       cmd_disconnect(0, NULL);
+       g_io_channel_unref(pchan);
+       g_main_loop_unref(event_loop);
+       g_string_free(prompt, TRUE);
+
+       g_free(opt_src);
+       g_free(opt_dst);
+       g_free(opt_sec_level);
+
+       return 0;
+}
diff --git a/attrib/utils.c b/attrib/utils.c
new file mode 100644 (file)
index 0000000..d856fe2
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "btio.h"
+#include "gatttool.h"
+
+GIOChannel *gatt_connect(const gchar *src, const gchar *dst,
+                               const gchar *dst_type, const gchar *sec_level,
+                               int psm, int mtu, BtIOConnect connect_cb)
+{
+       GIOChannel *chan;
+       bdaddr_t sba, dba;
+       uint8_t dest_type;
+       GError *err = NULL;
+       BtIOSecLevel sec;
+
+       /* Remote device */
+       if (dst == NULL) {
+               g_printerr("Remote Bluetooth address required\n");
+               return NULL;
+       }
+       str2ba(dst, &dba);
+
+       /* Local adapter */
+       if (src != NULL) {
+               if (!strncmp(src, "hci", 3))
+                       hci_devba(atoi(src + 3), &sba);
+               else
+                       str2ba(src, &sba);
+       } else
+               bacpy(&sba, BDADDR_ANY);
+
+       /* Not used for BR/EDR */
+       if (strcmp(dst_type, "random") == 0)
+               dest_type = BDADDR_LE_RANDOM;
+       else
+               dest_type = BDADDR_LE_PUBLIC;
+
+       if (strcmp(sec_level, "medium") == 0)
+               sec = BT_IO_SEC_MEDIUM;
+       else if (strcmp(sec_level, "high") == 0)
+               sec = BT_IO_SEC_HIGH;
+       else
+               sec = BT_IO_SEC_LOW;
+
+       if (psm == 0)
+               chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &sba,
+                               BT_IO_OPT_DEST_BDADDR, &dba,
+                               BT_IO_OPT_DEST_TYPE, dest_type,
+                               BT_IO_OPT_CID, ATT_CID,
+                               BT_IO_OPT_SEC_LEVEL, sec,
+                               BT_IO_OPT_INVALID);
+       else
+               chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &sba,
+                               BT_IO_OPT_DEST_BDADDR, &dba,
+                               BT_IO_OPT_PSM, psm,
+                               BT_IO_OPT_IMTU, mtu,
+                               BT_IO_OPT_SEC_LEVEL, sec,
+                               BT_IO_OPT_INVALID);
+
+       if (err) {
+               g_printerr("%s\n", err->message);
+               g_error_free(err);
+               return NULL;
+       }
+
+       return chan;
+}
+
+size_t gatt_attr_data_from_string(const char *str, uint8_t **data)
+{
+       char tmp[3];
+       size_t size, i;
+
+       size = strlen(str) / 2;
+       *data = g_try_malloc0(size);
+       if (*data == NULL)
+               return 0;
+
+       tmp[2] = '\0';
+       for (i = 0; i < size; i++) {
+               memcpy(tmp, str + (i * 2), 2);
+               (*data)[i] = (uint8_t) strtol(tmp, NULL, 16);
+       }
+
+       return size;
+}
diff --git a/audio/a2dp-codecs.h b/audio/a2dp-codecs.h
new file mode 100644 (file)
index 0000000..51c796a
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define A2DP_CODEC_SBC                 0x00
+#define A2DP_CODEC_MPEG12              0x01
+#define A2DP_CODEC_MPEG24              0x02
+#define A2DP_CODEC_ATRAC               0x03
+
+#define 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
+
+typedef struct {
+       uint8_t channel_mode:4;
+       uint8_t frequency:4;
+       uint8_t allocation_method:2;
+       uint8_t subbands:2;
+       uint8_t block_length:4;
+       uint8_t min_bitpool;
+       uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+       uint8_t channel_mode:4;
+       uint8_t crc:1;
+       uint8_t layer:3;
+       uint8_t frequency:6;
+       uint8_t mpf:1;
+       uint8_t rfa:1;
+       uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+typedef struct {
+       uint8_t frequency:4;
+       uint8_t channel_mode:4;
+       uint8_t block_length:4;
+       uint8_t subbands:2;
+       uint8_t allocation_method:2;
+       uint8_t min_bitpool;
+       uint8_t max_bitpool;
+} __attribute__ ((packed)) a2dp_sbc_t;
+
+typedef struct {
+       uint8_t layer:3;
+       uint8_t crc:1;
+       uint8_t channel_mode:4;
+       uint8_t rfa:1;
+       uint8_t mpf:1;
+       uint8_t frequency:6;
+       uint16_t bitrate;
+} __attribute__ ((packed)) a2dp_mpeg_t;
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/audio/a2dp.c b/audio/a2dp.c
new file mode 100644 (file)
index 0000000..404be53
--- /dev/null
@@ -0,0 +1,2436 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "log.h"
+#include "device.h"
+#include "manager.h"
+#include "avdtp.h"
+#include "sink.h"
+#include "source.h"
+#include "unix.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 {
+       struct a2dp_server *server;
+       struct a2dp_endpoint *endpoint;
+       uint8_t type;
+       uint8_t codec;
+       struct avdtp_local_sep *lsep;
+       struct avdtp *session;
+       struct avdtp_stream *stream;
+       guint suspend_timer;
+       gboolean delay_reporting;
+       gboolean locked;
+       gboolean suspending;
+       gboolean starting;
+       void *user_data;
+       GDestroyNotify destroy;
+};
+
+struct a2dp_setup_cb {
+       struct a2dp_setup *setup;
+       a2dp_select_cb_t select_cb;
+       a2dp_config_cb_t config_cb;
+       a2dp_stream_cb_t resume_cb;
+       a2dp_stream_cb_t suspend_cb;
+       guint source_id;
+       void *user_data;
+       unsigned int id;
+};
+
+struct a2dp_setup {
+       struct audio_device *dev;
+       struct avdtp *session;
+       struct a2dp_sep *sep;
+       struct avdtp_remote_sep *rsep;
+       struct avdtp_stream *stream;
+       struct avdtp_error *err;
+       avdtp_set_configuration_cb setconf_cb;
+       GSList *caps;
+       gboolean reconfigure;
+       gboolean start;
+       GSList *cb;
+       int ref;
+};
+
+static DBusConnection *connection = NULL;
+
+struct a2dp_server {
+       bdaddr_t src;
+       GSList *sinks;
+       GSList *sources;
+       uint32_t source_record_id;
+       uint32_t sink_record_id;
+       uint16_t version;
+       gboolean sink_enabled;
+       gboolean source_enabled;
+};
+
+static GSList *servers = NULL;
+static GSList *setups = NULL;
+static unsigned int cb_id = 0;
+
+static struct a2dp_setup *setup_ref(struct a2dp_setup *setup)
+{
+       setup->ref++;
+
+       DBG("%p: ref=%d", setup, setup->ref);
+
+       return setup;
+}
+
+static struct 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 struct a2dp_setup *setup_new(struct avdtp *session)
+{
+       struct audio_device *dev;
+       struct a2dp_setup *setup;
+
+       dev = a2dp_get_dev(session);
+       if (!dev) {
+               error("Unable to create setup");
+               return NULL;
+       }
+
+       setup = g_new0(struct a2dp_setup, 1);
+       setup->session = avdtp_ref(session);
+       setup->dev = a2dp_get_dev(session);
+       setups = g_slist_append(setups, setup);
+
+       return setup;
+}
+
+static void setup_free(struct a2dp_setup *s)
+{
+       DBG("%p", s);
+
+       setups = g_slist_remove(setups, s);
+       if (s->session)
+               avdtp_unref(s->session);
+       g_slist_free_full(s->cb, g_free);
+       g_slist_free_full(s->caps, g_free);
+       g_free(s);
+}
+
+static void setup_unref(struct a2dp_setup *setup)
+{
+       setup->ref--;
+
+       DBG("%p: ref=%d", setup, setup->ref);
+
+       if (setup->ref > 0)
+               return;
+
+       setup_free(setup);
+}
+
+static struct a2dp_setup_cb *setup_cb_new(struct a2dp_setup *setup)
+{
+       struct a2dp_setup_cb *cb;
+
+       cb = g_new0(struct a2dp_setup_cb, 1);
+       cb->setup = setup;
+       cb->id = ++cb_id;
+
+       setup->cb = g_slist_append(setup->cb, cb);
+       return cb;
+}
+
+static void setup_cb_free(struct a2dp_setup_cb *cb)
+{
+       struct a2dp_setup *setup = cb->setup;
+
+       if (cb->source_id)
+               g_source_remove(cb->source_id);
+
+       setup->cb = g_slist_remove(setup->cb, cb);
+       setup_unref(cb->setup);
+       g_free(cb);
+}
+
+static void finalize_setup_errno(struct a2dp_setup *s, int err,
+                                       GSourceFunc cb1, ...)
+{
+       GSourceFunc finalize;
+       va_list args;
+       struct avdtp_error avdtp_err;
+
+       if (err < 0) {
+               avdtp_error_init(&avdtp_err, AVDTP_ERRNO, -err);
+               s->err = &avdtp_err;
+       }
+
+       va_start(args, cb1);
+       finalize = cb1;
+       setup_ref(s);
+       while (finalize != NULL) {
+               finalize(s);
+               finalize = va_arg(args, GSourceFunc);
+       }
+       setup_unref(s);
+       va_end(args);
+}
+
+static gboolean finalize_config(gpointer data)
+{
+       struct a2dp_setup *s = data;
+       GSList *l;
+       struct avdtp_stream *stream = s->err ? NULL : s->stream;
+
+       for (l = s->cb; l != NULL; ) {
+               struct a2dp_setup_cb *cb = l->data;
+
+               l = l->next;
+
+               if (!cb->config_cb)
+                       continue;
+
+               cb->config_cb(s->session, s->sep, stream, s->err,
+                                                       cb->user_data);
+               setup_cb_free(cb);
+       }
+
+       return FALSE;
+}
+
+static gboolean finalize_resume(gpointer data)
+{
+       struct a2dp_setup *s = data;
+       GSList *l;
+
+       for (l = s->cb; l != NULL; ) {
+               struct a2dp_setup_cb *cb = l->data;
+
+               l = l->next;
+
+               if (!cb->resume_cb)
+                       continue;
+
+               cb->resume_cb(s->session, s->err, cb->user_data);
+               setup_cb_free(cb);
+       }
+
+       return FALSE;
+}
+
+static gboolean finalize_suspend(gpointer data)
+{
+       struct a2dp_setup *s = data;
+       GSList *l;
+
+       for (l = s->cb; l != NULL; ) {
+               struct a2dp_setup_cb *cb = l->data;
+
+               l = l->next;
+
+               if (!cb->suspend_cb)
+                       continue;
+
+               cb->suspend_cb(s->session, s->err, cb->user_data);
+               setup_cb_free(cb);
+       }
+
+       return FALSE;
+}
+
+static void finalize_select(struct a2dp_setup *s)
+{
+       GSList *l;
+
+       for (l = s->cb; l != NULL; ) {
+               struct a2dp_setup_cb *cb = l->data;
+
+               l = l->next;
+
+               if (!cb->select_cb)
+                       continue;
+
+               cb->select_cb(s->session, s->sep, s->caps, cb->user_data);
+               setup_cb_free(cb);
+       }
+}
+
+static struct a2dp_setup *find_setup_by_session(struct avdtp *session)
+{
+       GSList *l;
+
+       for (l = setups; l != NULL; l = l->next) {
+               struct a2dp_setup *setup = l->data;
+
+               if (setup->session == session)
+                       return setup;
+       }
+
+       return NULL;
+}
+
+static struct a2dp_setup *a2dp_setup_get(struct avdtp *session)
+{
+       struct a2dp_setup *setup;
+
+       setup = find_setup_by_session(session);
+       if (!setup) {
+               setup = setup_new(session);
+               if (!setup)
+                       return NULL;
+       }
+
+       return setup_ref(setup);
+}
+
+static struct a2dp_setup *find_setup_by_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;
+
+       if (sep->endpoint && sep->endpoint->clear_configuration)
+               sep->endpoint->clear_configuration(sep, sep->user_data);
+}
+
+static gboolean auto_config(gpointer data)
+{
+       struct a2dp_setup *setup = data;
+       struct avdtp_error *err = NULL;
+
+       /* Check if configuration was aborted */
+       if (setup->sep->stream == NULL)
+               return FALSE;
+
+       if (setup->err != NULL) {
+               err = setup->err;
+               goto done;
+       }
+
+       avdtp_stream_add_cb(setup->session, setup->stream,
+                               stream_state_changed, setup->sep);
+
+       if (setup->sep->type == AVDTP_SEP_TYPE_SOURCE)
+               sink_new_stream(setup->dev, setup->session, setup->stream);
+       else
+               source_new_stream(setup->dev, setup->session, setup->stream);
+
+done:
+       if (setup->setconf_cb)
+               setup->setconf_cb(setup->session, setup->stream, setup->err);
+
+       finalize_config(setup);
+
+       if (err)
+               g_free(err);
+
+       setup_unref(setup);
+
+       return FALSE;
+}
+
+static gboolean sbc_setconf_ind(struct avdtp *session,
+                                       struct avdtp_local_sep *sep,
+                                       struct avdtp_stream *stream,
+                                       GSList *caps,
+                                       avdtp_set_configuration_cb cb,
+                                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Set_Configuration_Ind", sep);
+       else
+               DBG("Source %p: Set_Configuration_Ind", sep);
+
+       setup = a2dp_setup_get(session);
+       if (!setup)
+               return FALSE;
+
+       a2dp_sep->stream = stream;
+       setup->sep = a2dp_sep;
+       setup->stream = stream;
+       setup->setconf_cb = cb;
+
+       /* Check valid settings */
+       for (; caps != NULL; caps = g_slist_next(caps)) {
+               struct avdtp_service_capability *cap = caps->data;
+               struct avdtp_media_codec_capability *codec_cap;
+               struct sbc_codec_cap *sbc_cap;
+
+               if (cap->category == AVDTP_DELAY_REPORTING &&
+                                       !a2dp_sep->delay_reporting) {
+                       setup->err = g_new(struct avdtp_error, 1);
+                       avdtp_error_init(setup->err, AVDTP_DELAY_REPORTING,
+                                               AVDTP_UNSUPPORTED_CONFIGURATION);
+                       goto done;
+               }
+
+               if (cap->category != AVDTP_MEDIA_CODEC)
+                       continue;
+
+               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) {
+                       setup->err = g_new(struct avdtp_error, 1);
+                       avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+                                       AVDTP_UNSUPPORTED_CONFIGURATION);
+                       goto done;
+               }
+       }
+
+done:
+       g_idle_add(auto_config, setup);
+       return TRUE;
+}
+
+static gboolean sbc_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               gboolean get_all, GSList **caps, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct sbc_codec_cap sbc_cap;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Get_Capability_Ind", sep);
+       else
+               DBG("Source %p: Get_Capability_Ind", sep);
+
+       *caps = NULL;
+
+       media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+                                               NULL, 0);
+
+       *caps = g_slist_append(*caps, media_transport);
+
+       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;
+
+       sbc_cap.frequency = ( SBC_SAMPLING_FREQ_48000 |
+                               SBC_SAMPLING_FREQ_44100 |
+                               SBC_SAMPLING_FREQ_32000 |
+                               SBC_SAMPLING_FREQ_16000 );
+
+       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);
+
+       if (get_all) {
+               struct avdtp_service_capability *delay_reporting;
+               delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+                                                               NULL, 0);
+               *caps = g_slist_append(*caps, delay_reporting);
+       }
+
+       return TRUE;
+}
+
+static gboolean mpeg_setconf_ind(struct avdtp *session,
+                                       struct avdtp_local_sep *sep,
+                                       struct avdtp_stream *stream,
+                                       GSList *caps,
+                                       avdtp_set_configuration_cb cb,
+                                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Set_Configuration_Ind", sep);
+       else
+               DBG("Source %p: Set_Configuration_Ind", sep);
+
+       setup = a2dp_setup_get(session);
+       if (!setup)
+               return FALSE;
+
+       a2dp_sep->stream = stream;
+       setup->sep = a2dp_sep;
+       setup->stream = stream;
+       setup->setconf_cb = cb;
+
+       for (; caps != NULL; caps = g_slist_next(caps)) {
+               struct avdtp_service_capability *cap = caps->data;
+
+               if (cap->category == AVDTP_DELAY_REPORTING &&
+                                       !a2dp_sep->delay_reporting) {
+                       setup->err = g_new(struct avdtp_error, 1);
+                       avdtp_error_init(setup->err, AVDTP_DELAY_REPORTING,
+                                       AVDTP_UNSUPPORTED_CONFIGURATION);
+                       goto done;
+               }
+       }
+
+done:
+       g_idle_add(auto_config, setup);
+       return TRUE;
+}
+
+static gboolean mpeg_getcap_ind(struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               gboolean get_all,
+                               GSList **caps, uint8_t *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct mpeg_codec_cap mpeg_cap;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Get_Capability_Ind", sep);
+       else
+               DBG("Source %p: Get_Capability_Ind", sep);
+
+       *caps = NULL;
+
+       media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+                                               NULL, 0);
+
+       *caps = g_slist_append(*caps, media_transport);
+
+       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);
+
+       if (get_all) {
+               struct avdtp_service_capability *delay_reporting;
+               delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+                                                               NULL, 0);
+               *caps = g_slist_append(*caps, delay_reporting);
+       }
+
+       return TRUE;
+}
+
+
+static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
+{
+       if (ret == FALSE) {
+               setup->err = g_new(struct avdtp_error, 1);
+               avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+                                       AVDTP_UNSUPPORTED_CONFIGURATION);
+       }
+
+       auto_config(setup);
+}
+
+static gboolean endpoint_setconf_ind(struct avdtp *session,
+                                               struct avdtp_local_sep *sep,
+                                               struct avdtp_stream *stream,
+                                               GSList *caps,
+                                               avdtp_set_configuration_cb cb,
+                                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Set_Configuration_Ind", sep);
+       else
+               DBG("Source %p: Set_Configuration_Ind", sep);
+
+       setup = a2dp_setup_get(session);
+       if (!session)
+               return FALSE;
+
+       a2dp_sep->stream = stream;
+       setup->sep = a2dp_sep;
+       setup->stream = stream;
+       setup->setconf_cb = cb;
+
+       for (; caps != NULL; caps = g_slist_next(caps)) {
+               struct avdtp_service_capability *cap = caps->data;
+               struct avdtp_media_codec_capability *codec;
+               gboolean ret;
+
+               if (cap->category == AVDTP_DELAY_REPORTING &&
+                                       !a2dp_sep->delay_reporting) {
+                       setup->err = g_new(struct avdtp_error, 1);
+                       avdtp_error_init(setup->err, AVDTP_DELAY_REPORTING,
+                                       AVDTP_UNSUPPORTED_CONFIGURATION);
+                       goto done;
+               }
+
+               if (cap->category != AVDTP_MEDIA_CODEC)
+                       continue;
+
+               codec = (struct avdtp_media_codec_capability *) cap->data;
+
+               if (codec->media_codec_type != a2dp_sep->codec) {
+                       setup->err = g_new(struct avdtp_error, 1);
+                       avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+                                       AVDTP_UNSUPPORTED_CONFIGURATION);
+                       goto done;
+               }
+
+               ret = a2dp_sep->endpoint->set_configuration(a2dp_sep,
+                                               setup->dev, codec->data,
+                                               cap->length - sizeof(*codec),
+                                               setup,
+                                               endpoint_setconf_cb,
+                                               a2dp_sep->user_data);
+               if (ret == 0)
+                       return TRUE;
+
+               setup->err = g_new(struct avdtp_error, 1);
+               avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
+                                       AVDTP_UNSUPPORTED_CONFIGURATION);
+               break;
+       }
+
+done:
+       g_idle_add(auto_config, setup);
+       return TRUE;
+}
+
+static gboolean endpoint_getcap_ind(struct avdtp *session,
+                                       struct avdtp_local_sep *sep,
+                                       gboolean get_all, GSList **caps,
+                                       uint8_t *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct avdtp_media_codec_capability *codec_caps;
+       uint8_t *capabilities;
+       size_t length;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Get_Capability_Ind", sep);
+       else
+               DBG("Source %p: Get_Capability_Ind", sep);
+
+       *caps = NULL;
+
+       media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+                                               NULL, 0);
+
+       *caps = g_slist_append(*caps, media_transport);
+
+       length = a2dp_sep->endpoint->get_capabilities(a2dp_sep, &capabilities,
+                                                       a2dp_sep->user_data);
+
+       codec_caps = g_malloc0(sizeof(*codec_caps) + length);
+       codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+       codec_caps->media_codec_type = a2dp_sep->codec;
+       memcpy(codec_caps->data, capabilities, length);
+
+       media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+                                               sizeof(*codec_caps) + length);
+
+       *caps = g_slist_append(*caps, media_codec);
+       g_free(codec_caps);
+
+       if (get_all) {
+               struct avdtp_service_capability *delay_reporting;
+               delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+                                                               NULL, 0);
+               *caps = g_slist_append(*caps, delay_reporting);
+       }
+
+       return TRUE;
+}
+
+static void endpoint_open_cb(struct a2dp_setup *setup, gboolean ret)
+{
+       int err;
+
+       if (ret == FALSE) {
+               setup->stream = NULL;
+               finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
+               return;
+       }
+
+       err = avdtp_open(setup->session, setup->stream);
+       if (err == 0)
+               return;
+
+       error("Error on avdtp_open %s (%d)", strerror(-err), -err);
+       setup->stream = NULL;
+       finalize_setup_errno(setup, err, finalize_config, NULL);
+}
+
+static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+       struct audio_device *dev;
+       int ret;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Set_Configuration_Cfm", sep);
+       else
+               DBG("Source %p: Set_Configuration_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+
+       if (err) {
+               if (setup) {
+                       setup->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);
+
+       /* Notify Endpoint */
+       if (a2dp_sep->endpoint) {
+               struct avdtp_service_capability *service;
+               struct avdtp_media_codec_capability *codec;
+               int err;
+
+               service = avdtp_stream_get_codec(stream);
+               codec = (struct avdtp_media_codec_capability *) service->data;
+
+               err = a2dp_sep->endpoint->set_configuration(a2dp_sep, dev,
+                                               codec->data, service->length -
+                                               sizeof(*codec),
+                                               setup,
+                                               endpoint_open_cb,
+                                               a2dp_sep->user_data);
+               if (err == 0)
+                       return;
+
+               setup->stream = NULL;
+               finalize_setup_errno(setup, -EPERM, finalize_config, NULL);
+               return;
+       }
+
+       ret = avdtp_open(session, stream);
+       if (ret < 0) {
+               error("Error on avdtp_open %s (%d)", strerror(-ret), -ret);
+               setup->stream = NULL;
+               finalize_setup_errno(setup, ret, finalize_config, NULL);
+       }
+}
+
+static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               uint8_t *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Get_Configuration_Ind", sep);
+       else
+               DBG("Source %p: Get_Configuration_Ind", sep);
+       return TRUE;
+}
+
+static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Set_Configuration_Cfm", sep);
+       else
+               DBG("Source %p: Set_Configuration_Cfm", sep);
+}
+
+static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Open_Ind", sep);
+       else
+               DBG("Source %p: Open_Ind", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return TRUE;
+
+       if (setup->reconfigure)
+               setup->reconfigure = FALSE;
+
+       finalize_config(setup);
+
+       return TRUE;
+}
+
+static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Open_Cfm", sep);
+       else
+               DBG("Source %p: Open_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       if (setup->reconfigure)
+               setup->reconfigure = FALSE;
+
+       if (err) {
+               setup->stream = NULL;
+               setup->err = err;
+       }
+
+       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)
+               DBG("Sink %p: Start_Ind", sep);
+       else
+               DBG("Source %p: Start_Ind", sep);
+
+       if (!a2dp_sep->locked) {
+               a2dp_sep->session = avdtp_ref(session);
+               a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT,
+                                               (GSourceFunc) suspend_timeout,
+                                               a2dp_sep);
+       }
+
+       if (!a2dp_sep->starting)
+               return TRUE;
+
+       a2dp_sep->starting = FALSE;
+
+       setup = find_setup_by_session(session);
+       if (setup)
+               finalize_resume(setup);
+
+       return TRUE;
+}
+
+static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Start_Cfm", sep);
+       else
+               DBG("Source %p: Start_Cfm", sep);
+
+       a2dp_sep->starting = FALSE;
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       if (err) {
+               setup->stream = NULL;
+               setup->err = err;
+       }
+
+       finalize_resume(setup);
+}
+
+static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+       gboolean start;
+       int start_err;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Suspend_Ind", sep);
+       else
+               DBG("Source %p: Suspend_Ind", sep);
+
+       if (a2dp_sep->suspend_timer) {
+               g_source_remove(a2dp_sep->suspend_timer);
+               a2dp_sep->suspend_timer = 0;
+               avdtp_unref(a2dp_sep->session);
+               a2dp_sep->session = NULL;
+       }
+
+       if (!a2dp_sep->suspending)
+               return TRUE;
+
+       a2dp_sep->suspending = FALSE;
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return TRUE;
+
+       start = setup->start;
+       setup->start = FALSE;
+
+       finalize_suspend(setup);
+
+       if (!start)
+               return TRUE;
+
+       start_err = avdtp_start(session, a2dp_sep->stream);
+       if (start_err < 0 && start_err != -EINPROGRESS) {
+               error("avdtp_start: %s (%d)", strerror(-start_err),
+                                                               -start_err);
+               finalize_setup_errno(setup, start_err, finalize_resume);
+       }
+
+       return TRUE;
+}
+
+static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+       gboolean start;
+       int start_err;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Suspend_Cfm", sep);
+       else
+               DBG("Source %p: Suspend_Cfm", sep);
+
+       a2dp_sep->suspending = FALSE;
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       start = setup->start;
+       setup->start = FALSE;
+
+       if (err) {
+               setup->stream = NULL;
+               setup->err = err;
+       }
+
+       finalize_suspend(setup);
+
+       if (!start)
+               return;
+
+       if (err) {
+               finalize_resume(setup);
+               return;
+       }
+
+       start_err = avdtp_start(session, a2dp_sep->stream);
+       if (start_err < 0 && start_err != -EINPROGRESS) {
+               error("avdtp_start: %s (%d)", strerror(-start_err),
+                                                               -start_err);
+               finalize_setup_errno(setup, start_err, finalize_suspend, NULL);
+       }
+}
+
+static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Close_Ind", sep);
+       else
+               DBG("Source %p: Close_Ind", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return TRUE;
+
+       finalize_setup_errno(setup, -ECONNRESET, finalize_suspend,
+                                                       finalize_resume, NULL);
+
+       return TRUE;
+}
+
+static gboolean a2dp_reconfigure(gpointer data)
+{
+       struct a2dp_setup *setup = data;
+       struct a2dp_sep *sep = setup->sep;
+       int posix_err;
+       struct avdtp_media_codec_capability *rsep_codec;
+       struct avdtp_service_capability *cap;
+
+       if (setup->rsep) {
+               cap = avdtp_get_codec(setup->rsep);
+               rsep_codec = (struct avdtp_media_codec_capability *) cap->data;
+       }
+
+       if (!setup->rsep || sep->codec != rsep_codec->media_codec_type)
+               setup->rsep = avdtp_find_remote_sep(setup->session, sep->lsep);
+
+       posix_err = avdtp_set_configuration(setup->session, setup->rsep,
+                                               sep->lsep,
+                                               setup->caps,
+                                               &setup->stream);
+       if (posix_err < 0) {
+               error("avdtp_set_configuration: %s", strerror(-posix_err));
+               goto failed;
+       }
+
+       return FALSE;
+
+failed:
+       finalize_setup_errno(setup, posix_err, finalize_config, NULL);
+       return FALSE;
+}
+
+static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Close_Cfm", sep);
+       else
+               DBG("Source %p: Close_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       if (err) {
+               setup->stream = NULL;
+               setup->err = err;
+               finalize_config(setup);
+               return;
+       }
+
+       if (!setup->rsep)
+               setup->rsep = avdtp_stream_get_remote_sep(stream);
+
+       if (setup->reconfigure)
+               g_timeout_add(RECONFIGURE_TIMEOUT, a2dp_reconfigure, setup);
+}
+
+static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Abort_Ind", sep);
+       else
+               DBG("Source %p: Abort_Ind", sep);
+
+       a2dp_sep->stream = NULL;
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       finalize_setup_errno(setup, -ECONNRESET, finalize_suspend,
+                                                       finalize_resume,
+                                                       finalize_config);
+
+       return;
+}
+
+static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: Abort_Cfm", sep);
+       else
+               DBG("Source %p: Abort_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       setup_unref(setup);
+}
+
+static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               uint8_t *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: ReConfigure_Ind", sep);
+       else
+               DBG("Source %p: ReConfigure_Ind", sep);
+
+       return TRUE;
+}
+
+static gboolean delayreport_ind(struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               uint8_t rseid, uint16_t delay,
+                               uint8_t *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct audio_device *dev = a2dp_get_dev(session);
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: DelayReport_Ind", sep);
+       else
+               DBG("Source %p: DelayReport_Ind", sep);
+
+       unix_delay_report(dev, rseid, delay);
+
+       return TRUE;
+}
+
+static gboolean endpoint_delayreport_ind(struct avdtp *session,
+                                               struct avdtp_local_sep *sep,
+                                               uint8_t rseid, uint16_t delay,
+                                               uint8_t *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: DelayReport_Ind", sep);
+       else
+               DBG("Source %p: DelayReport_Ind", sep);
+
+       if (a2dp_sep->endpoint == NULL ||
+                               a2dp_sep->endpoint->set_delay == NULL)
+               return FALSE;
+
+       a2dp_sep->endpoint->set_delay(a2dp_sep, delay, a2dp_sep->user_data);
+
+       return TRUE;
+}
+
+static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+       struct a2dp_setup *setup;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: ReConfigure_Cfm", sep);
+       else
+               DBG("Source %p: ReConfigure_Cfm", sep);
+
+       setup = find_setup_by_session(session);
+       if (!setup)
+               return;
+
+       if (err) {
+               setup->stream = NULL;
+               setup->err = err;
+       }
+
+       finalize_config(setup);
+}
+
+static void delay_report_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct a2dp_sep *a2dp_sep = user_data;
+
+       if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
+               DBG("Sink %p: DelayReport_Cfm", sep);
+       else
+               DBG("Source %p: DelayReport_Cfm", sep);
+}
+
+static struct avdtp_sep_cfm cfm = {
+       .set_configuration      = setconf_cfm,
+       .get_configuration      = getconf_cfm,
+       .open                   = open_cfm,
+       .start                  = start_cfm,
+       .suspend                = suspend_cfm,
+       .close                  = close_cfm,
+       .abort                  = abort_cfm,
+       .reconfigure            = reconf_cfm,
+       .delay_report           = delay_report_cfm,
+};
+
+static struct avdtp_sep_ind 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,
+       .delayreport            = delayreport_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,
+       .delayreport            = delayreport_ind,
+};
+
+static struct avdtp_sep_ind endpoint_ind = {
+       .get_capability         = endpoint_getcap_ind,
+       .set_configuration      = endpoint_setconf_ind,
+       .get_configuration      = getconf_ind,
+       .open                   = open_ind,
+       .start                  = start_ind,
+       .suspend                = suspend_ind,
+       .close                  = close_ind,
+       .abort                  = abort_ind,
+       .reconfigure            = reconf_ind,
+       .delayreport            = endpoint_delayreport_ind,
+};
+
+static sdp_record_t *a2dp_record(uint8_t type, uint16_t avdtp_ver)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t *record;
+       sdp_data_t *psm, *version, *features;
+       uint16_t lp = AVDTP_UUID;
+       uint16_t a2dp_ver = 0x0102, feat = 0x000f;
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(record, root);
+
+       if (type == AVDTP_SEP_TYPE_SOURCE)
+               sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID);
+       else
+               sdp_uuid16_create(&a2dp_uuid, AUDIO_SINK_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &a2dp_uuid);
+       sdp_set_service_classes(record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+       profile[0].version = a2dp_ver;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID);
+       proto[1] = sdp_list_append(0, &avdtp_uuid);
+       version = sdp_data_alloc(SDP_UINT16, &avdtp_ver);
+       proto[1] = sdp_list_append(proto[1], version);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       features = sdp_data_alloc(SDP_UINT16, &feat);
+       sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       if (type == AVDTP_SEP_TYPE_SOURCE)
+               sdp_set_info_attr(record, "Audio Source", 0, 0);
+       else
+               sdp_set_info_attr(record, "Audio Sink", 0, 0);
+
+       free(psm);
+       free(version);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(pfseq, 0);
+       sdp_list_free(aproto, 0);
+       sdp_list_free(root, 0);
+       sdp_list_free(svclass_id, 0);
+
+       return record;
+}
+
+static struct a2dp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+
+       for (; list; list = list->next) {
+               struct a2dp_server *server = list->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 = 0, sbc_sinks = 0;
+       int mpeg12_srcs = 0, mpeg12_sinks = 0;
+       gboolean source = TRUE, sink = FALSE, socket = FALSE;
+       gboolean delay_reporting = 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) {
+               DBG("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else {
+               if (strstr(str, "Sink"))
+                       source = TRUE;
+               if (strstr(str, "Source"))
+                       sink = TRUE;
+               if (strstr(str, "Socket"))
+                       socket = TRUE;
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(config, "General", "Disable", &err);
+
+       if (err) {
+               DBG("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else {
+               if (strstr(str, "Sink"))
+                       source = FALSE;
+               if (strstr(str, "Source"))
+                       sink = FALSE;
+               if (strstr(str, "Socket"))
+                       socket = FALSE;
+               g_free(str);
+       }
+
+       /* Don't register any local sep if Socket is disabled */
+       if (socket == FALSE)
+               goto proceed;
+
+       str = g_key_file_get_string(config, "A2DP", "SBCSources", &err);
+       if (err) {
+               DBG("audio.conf: %s", err->message);
+               g_clear_error(&err);
+               sbc_srcs = 1;
+       } else {
+               sbc_srcs = atoi(str);
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(config, "A2DP", "MPEG12Sources", &err);
+       if (err) {
+               DBG("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) {
+               DBG("audio.conf: %s", err->message);
+               g_clear_error(&err);
+               sbc_sinks = 1;
+       } else {
+               sbc_sinks = atoi(str);
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(config, "A2DP", "MPEG12Sinks", &err);
+       if (err) {
+               DBG("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, &server->version);
+               if (av_err < 0) {
+                       g_free(server);
+                       return av_err;
+               }
+
+               bacpy(&server->src, src);
+               servers = g_slist_append(servers, server);
+       }
+
+       if (config)
+               delay_reporting = g_key_file_get_boolean(config, "A2DP",
+                                               "DelayReporting", NULL);
+
+       if (delay_reporting)
+               server->version = 0x0103;
+       else
+               server->version = 0x0102;
+
+       server->source_enabled = source;
+       if (source) {
+               for (i = 0; i < sbc_srcs; i++)
+                       a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE,
+                                       A2DP_CODEC_SBC, delay_reporting,
+                                       NULL, NULL, NULL, NULL);
+
+               for (i = 0; i < mpeg12_srcs; i++)
+                       a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE,
+                                       A2DP_CODEC_MPEG12, delay_reporting,
+                                       NULL, NULL, NULL, NULL);
+       }
+       server->sink_enabled = sink;
+       if (sink) {
+               for (i = 0; i < sbc_sinks; i++)
+                       a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK,
+                                       A2DP_CODEC_SBC, delay_reporting,
+                                       NULL, NULL, NULL, NULL);
+
+               for (i = 0; i < mpeg12_sinks; i++)
+                       a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK,
+                                       A2DP_CODEC_MPEG12, delay_reporting,
+                                       NULL, NULL, NULL, NULL);
+       }
+
+       return 0;
+}
+
+static void a2dp_unregister_sep(struct a2dp_sep *sep)
+{
+       if (sep->destroy) {
+               sep->destroy(sep->user_data);
+               sep->endpoint = NULL;
+       }
+
+       avdtp_unregister_sep(sep->lsep);
+       g_free(sep);
+}
+
+void a2dp_unregister(const bdaddr_t *src)
+{
+       struct a2dp_server *server;
+
+       server = find_server(servers, src);
+       if (!server)
+               return;
+
+       g_slist_free_full(server->sinks, (GDestroyNotify) a2dp_unregister_sep);
+       g_slist_free_full(server->sources,
+                                       (GDestroyNotify) a2dp_unregister_sep);
+
+       avdtp_exit(src);
+
+       servers = g_slist_remove(servers, server);
+
+       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);
+
+       g_free(server);
+
+       if (servers)
+               return;
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
+
+struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type,
+                               uint8_t codec, gboolean delay_reporting,
+                               struct a2dp_endpoint *endpoint,
+                               void *user_data, GDestroyNotify destroy,
+                               int *err)
+{
+       struct a2dp_server *server;
+       struct a2dp_sep *sep;
+       GSList **l;
+       uint32_t *record_id;
+       sdp_record_t *record;
+       struct avdtp_sep_ind *ind;
+
+       server = find_server(servers, src);
+       if (server == NULL) {
+               if (err)
+                       *err = -EPROTONOSUPPORT;
+               return NULL;
+       }
+
+       if (type == AVDTP_SEP_TYPE_SINK && !server->sink_enabled) {
+               if (err)
+                       *err = -EPROTONOSUPPORT;
+               return NULL;
+       }
+
+       if (type == AVDTP_SEP_TYPE_SOURCE && !server->source_enabled) {
+               if (err)
+                       *err = -EPROTONOSUPPORT;
+               return NULL;
+       }
+
+       sep = g_new0(struct a2dp_sep, 1);
+
+       if (endpoint) {
+               ind = &endpoint_ind;
+               goto proceed;
+       }
+
+       ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind;
+
+proceed:
+       sep->lsep = avdtp_register_sep(&server->src, type,
+                                       AVDTP_MEDIA_TYPE_AUDIO, codec,
+                                       delay_reporting, ind, &cfm, sep);
+       if (sep->lsep == NULL) {
+               g_free(sep);
+               if (err)
+                       *err = -EINVAL;
+               return NULL;
+       }
+
+       sep->server = server;
+       sep->endpoint = endpoint;
+       sep->codec = codec;
+       sep->type = type;
+       sep->delay_reporting = delay_reporting;
+       sep->user_data = user_data;
+       sep->destroy = destroy;
+
+       if (type == AVDTP_SEP_TYPE_SOURCE) {
+               l = &server->sources;
+               record_id = &server->source_record_id;
+       } else {
+               l = &server->sinks;
+               record_id = &server->sink_record_id;
+       }
+
+       if (*record_id != 0)
+               goto add;
+
+       record = a2dp_record(type, server->version);
+       if (!record) {
+               error("Unable to allocate new service record");
+               avdtp_unregister_sep(sep->lsep);
+               g_free(sep);
+               if (err)
+                       *err = -EINVAL;
+               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->lsep);
+               g_free(sep);
+               if (err)
+                       *err = -EINVAL;
+               return NULL;
+       }
+       *record_id = record->handle;
+
+add:
+       *l = g_slist_append(*l, sep);
+
+       if (err)
+               *err = 0;
+       return sep;
+}
+
+void a2dp_remove_sep(struct a2dp_sep *sep)
+{
+       struct a2dp_server *server = sep->server;
+
+       if (sep->type == AVDTP_SEP_TYPE_SOURCE) {
+               if (g_slist_find(server->sources, sep) == NULL)
+                       return;
+               server->sources = g_slist_remove(server->sources, sep);
+               if (server->sources == NULL && server->source_record_id) {
+                       remove_record_from_server(server->source_record_id);
+                       server->source_record_id = 0;
+               }
+       } else {
+               if (g_slist_find(server->sinks, sep) == NULL)
+                       return;
+               server->sinks = g_slist_remove(server->sinks, sep);
+               if (server->sinks == NULL && server->sink_record_id) {
+                       remove_record_from_server(server->sink_record_id);
+                       server->sink_record_id = 0;
+               }
+       }
+
+       if (sep->locked)
+               return;
+
+       a2dp_unregister_sep(sep);
+}
+
+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;
+}
+
+static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+{
+       switch (freq) {
+       case SBC_SAMPLING_FREQ_16000:
+       case SBC_SAMPLING_FREQ_32000:
+               return 53;
+       case SBC_SAMPLING_FREQ_44100:
+               switch (mode) {
+               case SBC_CHANNEL_MODE_MONO:
+               case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+                       return 31;
+               case SBC_CHANNEL_MODE_STEREO:
+               case SBC_CHANNEL_MODE_JOINT_STEREO:
+                       return 53;
+               default:
+                       error("Invalid channel mode %u", mode);
+                       return 53;
+               }
+       case SBC_SAMPLING_FREQ_48000:
+               switch (mode) {
+               case SBC_CHANNEL_MODE_MONO:
+               case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+                       return 29;
+               case SBC_CHANNEL_MODE_STEREO:
+               case SBC_CHANNEL_MODE_JOINT_STEREO:
+                       return 51;
+               default:
+                       error("Invalid channel mode %u", mode);
+                       return 51;
+               }
+       default:
+               error("Invalid sampling freq %u", freq);
+               return 53;
+       }
+}
+
+static gboolean select_sbc_params(struct sbc_codec_cap *cap,
+                                       struct sbc_codec_cap *supported)
+{
+       unsigned int max_bitpool, min_bitpool;
+
+       memset(cap, 0, sizeof(struct sbc_codec_cap));
+
+       cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+       cap->cap.media_codec_type = A2DP_CODEC_SBC;
+
+       if (supported->frequency & SBC_SAMPLING_FREQ_44100)
+               cap->frequency = SBC_SAMPLING_FREQ_44100;
+       else if (supported->frequency & SBC_SAMPLING_FREQ_48000)
+               cap->frequency = SBC_SAMPLING_FREQ_48000;
+       else if (supported->frequency & SBC_SAMPLING_FREQ_32000)
+               cap->frequency = SBC_SAMPLING_FREQ_32000;
+       else if (supported->frequency & SBC_SAMPLING_FREQ_16000)
+               cap->frequency = SBC_SAMPLING_FREQ_16000;
+       else {
+               error("No supported frequencies");
+               return FALSE;
+       }
+
+       if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+               cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
+       else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO)
+               cap->channel_mode = SBC_CHANNEL_MODE_STEREO;
+       else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+               cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
+       else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO)
+               cap->channel_mode = SBC_CHANNEL_MODE_MONO;
+       else {
+               error("No supported channel modes");
+               return FALSE;
+       }
+
+       if (supported->block_length & SBC_BLOCK_LENGTH_16)
+               cap->block_length = SBC_BLOCK_LENGTH_16;
+       else if (supported->block_length & SBC_BLOCK_LENGTH_12)
+               cap->block_length = SBC_BLOCK_LENGTH_12;
+       else if (supported->block_length & SBC_BLOCK_LENGTH_8)
+               cap->block_length = SBC_BLOCK_LENGTH_8;
+       else if (supported->block_length & SBC_BLOCK_LENGTH_4)
+               cap->block_length = SBC_BLOCK_LENGTH_4;
+       else {
+               error("No supported block lengths");
+               return FALSE;
+       }
+
+       if (supported->subbands & SBC_SUBBANDS_8)
+               cap->subbands = SBC_SUBBANDS_8;
+       else if (supported->subbands & SBC_SUBBANDS_4)
+               cap->subbands = SBC_SUBBANDS_4;
+       else {
+               error("No supported subbands");
+               return FALSE;
+       }
+
+       if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS)
+               cap->allocation_method = SBC_ALLOCATION_LOUDNESS;
+       else if (supported->allocation_method & SBC_ALLOCATION_SNR)
+               cap->allocation_method = SBC_ALLOCATION_SNR;
+
+       min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool);
+       max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode),
+                                                       supported->max_bitpool);
+
+       cap->min_bitpool = min_bitpool;
+       cap->max_bitpool = max_bitpool;
+
+       return TRUE;
+}
+
+static gboolean select_capabilities(struct avdtp *session,
+                                       struct avdtp_remote_sep *rsep,
+                                       GSList **caps)
+{
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct sbc_codec_cap sbc_cap;
+
+       media_codec = avdtp_get_codec(rsep);
+       if (!media_codec)
+               return FALSE;
+
+       select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data);
+
+       media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+                                               NULL, 0);
+
+       *caps = g_slist_append(*caps, media_transport);
+
+       media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+                                               sizeof(sbc_cap));
+
+       *caps = g_slist_append(*caps, media_codec);
+
+       if (avdtp_get_delay_reporting(rsep)) {
+               struct avdtp_service_capability *delay_reporting;
+               delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+                                                               NULL, 0);
+               *caps = g_slist_append(*caps, delay_reporting);
+       }
+
+       return TRUE;
+}
+
+static void select_cb(struct a2dp_setup *setup, void *ret, int size)
+{
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct avdtp_media_codec_capability *cap;
+
+       if (size < 0) {
+               DBG("Endpoint replied an invalid configuration");
+               goto done;
+       }
+
+       media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+                                               NULL, 0);
+
+       setup->caps = g_slist_append(setup->caps, media_transport);
+
+       cap = g_malloc0(sizeof(*cap) + size);
+       cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+       cap->media_codec_type = setup->sep->codec;
+       memcpy(cap->data, ret, size);
+
+       media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
+                                               sizeof(*cap) + size);
+
+       setup->caps = g_slist_append(setup->caps, media_codec);
+       g_free(cap);
+
+done:
+       finalize_select(setup);
+}
+
+static gboolean auto_select(gpointer data)
+{
+       struct a2dp_setup *setup = data;
+
+       finalize_select(setup);
+
+       return FALSE;
+}
+
+static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
+                                       const char *sender)
+{
+       for (; list; list = list->next) {
+               struct a2dp_sep *sep = list->data;
+
+               /* Use sender's endpoint if available */
+               if (sender) {
+                       const char *name;
+
+                       if (sep->endpoint == NULL)
+                               continue;
+
+                       name = sep->endpoint->get_name(sep, sep->user_data);
+                       if (g_strcmp0(sender, name) != 0)
+                               continue;
+               }
+
+               if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
+                       continue;
+
+               return sep;
+       }
+
+       return NULL;
+}
+
+static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
+                                       const char *sender)
+{
+       struct a2dp_server *server;
+       struct a2dp_sep *sep;
+       GSList *l;
+       bdaddr_t src;
+
+       avdtp_get_peers(session, &src, NULL);
+       server = find_server(servers, &src);
+       if (!server)
+               return NULL;
+
+       l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks;
+
+       /* Check sender's seps first */
+       sep = a2dp_find_sep(session, l, sender);
+       if (sep != NULL)
+               return sep;
+
+       return a2dp_find_sep(session, l, NULL);
+}
+
+unsigned int a2dp_select_capabilities(struct avdtp *session,
+                                       uint8_t type, const char *sender,
+                                       a2dp_select_cb_t cb,
+                                       void *user_data)
+{
+       struct a2dp_setup *setup;
+       struct a2dp_setup_cb *cb_data;
+       struct a2dp_sep *sep;
+       struct avdtp_service_capability *service;
+       struct avdtp_media_codec_capability *codec;
+       int err;
+
+       sep = a2dp_select_sep(session, type, sender);
+       if (!sep) {
+               error("Unable to select SEP");
+               return 0;
+       }
+
+       setup = a2dp_setup_get(session);
+       if (!setup)
+               return 0;
+
+       cb_data = setup_cb_new(setup);
+       cb_data->select_cb = cb;
+       cb_data->user_data = user_data;
+
+       setup->sep = sep;
+       setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+
+       if (setup->rsep == NULL) {
+               error("Could not find remote sep");
+               goto fail;
+       }
+
+       /* FIXME: Remove auto select when it is not longer possible to register
+       endpoint in the configuration file */
+       if (sep->endpoint == NULL) {
+               if (!select_capabilities(session, setup->rsep,
+                                       &setup->caps)) {
+                       error("Unable to auto select remote SEP capabilities");
+                       goto fail;
+               }
+
+               g_idle_add(auto_select, setup);
+
+               return cb_data->id;
+       }
+
+       service = avdtp_get_codec(setup->rsep);
+       codec = (struct avdtp_media_codec_capability *) service->data;
+
+       err = sep->endpoint->select_configuration(sep, codec->data,
+                                       service->length - sizeof(*codec),
+                                       setup,
+                                       select_cb, sep->user_data);
+       if (err == 0)
+               return cb_data->id;
+
+fail:
+       setup_cb_free(cb_data);
+       return 0;
+
+}
+
+unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_config_cb_t cb, GSList *caps,
+                               void *user_data)
+{
+       struct a2dp_setup_cb *cb_data;
+       GSList *l;
+       struct a2dp_server *server;
+       struct a2dp_setup *setup;
+       struct a2dp_sep *tmp;
+       struct avdtp_service_capability *cap;
+       struct avdtp_media_codec_capability *codec_cap = NULL;
+       int posix_err;
+       bdaddr_t src;
+
+       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;
+
+       DBG("a2dp_config: selected SEP %p", sep->lsep);
+
+       setup = a2dp_setup_get(session);
+       if (!setup)
+               return 0;
+
+       cb_data = setup_cb_new(setup);
+       cb_data->config_cb = cb;
+       cb_data->user_data = user_data;
+
+       setup->sep = sep;
+       setup->stream = sep->stream;
+
+       /* Copy given caps if they are different than current caps */
+       if (setup->caps != caps) {
+               g_slist_free_full(setup->caps, g_free);
+               setup->caps = g_slist_copy(caps);
+       }
+
+       switch (avdtp_sep_get_state(sep->lsep)) {
+       case AVDTP_STATE_IDLE:
+               if (sep->type == AVDTP_SEP_TYPE_SOURCE)
+                       l = server->sources;
+               else
+                       l = server->sinks;
+
+               for (; l != NULL; l = l->next) {
+                       tmp = l->data;
+                       if (avdtp_has_stream(session, tmp->stream))
+                               break;
+               }
+
+               if (l != NULL) {
+                       if (a2dp_sep_get_lock(tmp))
+                               goto failed;
+                       setup->reconfigure = TRUE;
+                       if (avdtp_close(session, tmp->stream, FALSE) < 0) {
+                               error("avdtp_close failed");
+                               goto failed;
+                       }
+                       break;
+               }
+
+               setup->rsep = avdtp_find_remote_sep(session, sep->lsep);
+               if (setup->rsep == NULL) {
+                       error("No matching ACP and INT SEPs found");
+                       goto failed;
+               }
+
+               posix_err = avdtp_set_configuration(session, setup->rsep,
+                                                       sep->lsep, caps,
+                                                       &setup->stream);
+               if (posix_err < 0) {
+                       error("avdtp_set_configuration: %s",
+                               strerror(-posix_err));
+                       goto failed;
+               }
+               break;
+       case AVDTP_STATE_OPEN:
+       case AVDTP_STATE_STREAMING:
+               if (avdtp_stream_has_capabilities(setup->stream, caps)) {
+                       DBG("Configuration match: resuming");
+                       cb_data->source_id = g_idle_add(finalize_config,
+                                                               setup);
+               } else if (!setup->reconfigure) {
+                       setup->reconfigure = TRUE;
+                       if (avdtp_close(session, sep->stream, FALSE) < 0) {
+                               error("avdtp_close failed");
+                               goto failed;
+                       }
+               }
+               break;
+       default:
+               error("SEP in bad state for requesting a new stream");
+               goto failed;
+       }
+
+       return cb_data->id;
+
+failed:
+       setup_cb_free(cb_data);
+       return 0;
+}
+
+unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_stream_cb_t cb, void *user_data)
+{
+       struct a2dp_setup_cb *cb_data;
+       struct a2dp_setup *setup;
+
+       setup = a2dp_setup_get(session);
+       if (!setup)
+               return 0;
+
+       cb_data = setup_cb_new(setup);
+       cb_data->resume_cb = cb;
+       cb_data->user_data = user_data;
+
+       setup->sep = sep;
+       setup->stream = sep->stream;
+
+       switch (avdtp_sep_get_state(sep->lsep)) {
+       case AVDTP_STATE_IDLE:
+               goto failed;
+               break;
+       case AVDTP_STATE_OPEN:
+               if (avdtp_start(session, sep->stream) < 0) {
+                       error("avdtp_start failed");
+                       goto failed;
+               }
+               sep->starting = TRUE;
+               break;
+       case AVDTP_STATE_STREAMING:
+               if (!sep->suspending && sep->suspend_timer) {
+                       g_source_remove(sep->suspend_timer);
+                       sep->suspend_timer = 0;
+                       avdtp_unref(sep->session);
+                       sep->session = NULL;
+               }
+               if (sep->suspending)
+                       setup->start = TRUE;
+               else
+                       cb_data->source_id = g_idle_add(finalize_resume,
+                                                               setup);
+               break;
+       default:
+               error("SEP in bad state for resume");
+               goto failed;
+       }
+
+       return cb_data->id;
+
+failed:
+       setup_cb_free(cb_data);
+       return 0;
+}
+
+unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_stream_cb_t cb, void *user_data)
+{
+       struct a2dp_setup_cb *cb_data;
+       struct a2dp_setup *setup;
+
+       setup = a2dp_setup_get(session);
+       if (!setup)
+               return 0;
+
+       cb_data = setup_cb_new(setup);
+       cb_data->suspend_cb = cb;
+       cb_data->user_data = user_data;
+
+       setup->sep = sep;
+       setup->stream = sep->stream;
+
+       switch (avdtp_sep_get_state(sep->lsep)) {
+       case AVDTP_STATE_IDLE:
+               error("a2dp_suspend: no stream to suspend");
+               goto failed;
+               break;
+       case AVDTP_STATE_OPEN:
+               cb_data->source_id = g_idle_add(finalize_suspend, setup);
+               break;
+       case AVDTP_STATE_STREAMING:
+               if (avdtp_suspend(session, sep->stream) < 0) {
+                       error("avdtp_suspend failed");
+                       goto failed;
+               }
+               sep->suspending = TRUE;
+               break;
+       default:
+               error("SEP in bad state for suspend");
+               goto failed;
+       }
+
+       return cb_data->id;
+
+failed:
+       setup_cb_free(cb_data);
+       return 0;
+}
+
+gboolean a2dp_cancel(struct audio_device *dev, unsigned int id)
+{
+       struct a2dp_setup *setup;
+       GSList *l;
+
+       setup = find_setup_by_dev(dev);
+       if (!setup)
+               return FALSE;
+
+       for (l = setup->cb; l != NULL; l = g_slist_next(l)) {
+               struct a2dp_setup_cb *cb = l->data;
+
+               if (cb->id != id)
+                       continue;
+
+               setup_ref(setup);
+               setup_cb_free(cb);
+
+               if (!setup->cb) {
+                       DBG("aborting setup %p", setup);
+                       avdtp_abort(setup->session, setup->stream);
+                       return TRUE;
+               }
+
+               setup_unref(setup);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session)
+{
+       if (sep->locked)
+               return FALSE;
+
+       DBG("SEP %p locked", sep->lsep);
+       sep->locked = TRUE;
+
+       return TRUE;
+}
+
+gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session)
+{
+       struct a2dp_server *server = sep->server;
+       avdtp_state_t state;
+       GSList *l;
+
+       state = avdtp_sep_get_state(sep->lsep);
+
+       sep->locked = FALSE;
+
+       DBG("SEP %p unlocked", sep->lsep);
+
+       if (sep->type == AVDTP_SEP_TYPE_SOURCE)
+               l = server->sources;
+       else
+               l = server->sinks;
+
+       /* Unregister sep if it was removed */
+       if (g_slist_find(l, sep) == NULL) {
+               a2dp_unregister_sep(sep);
+               return TRUE;
+       }
+
+       if (!sep->stream || state == AVDTP_STATE_IDLE)
+               return TRUE;
+
+       switch (state) {
+       case AVDTP_STATE_OPEN:
+               /* Set timer here */
+               break;
+       case AVDTP_STATE_STREAMING:
+               if (avdtp_suspend(session, sep->stream) == 0)
+                       sep->suspending = TRUE;
+               break;
+       default:
+               break;
+       }
+
+       return TRUE;
+}
+
+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;
+}
+
+struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep)
+{
+       return sep->stream;
+}
diff --git a/audio/a2dp.h b/audio/a2dp.h
new file mode 100644 (file)
index 0000000..887c5ac
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#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;
+struct a2dp_setup;
+
+typedef void (*a2dp_endpoint_select_t) (struct a2dp_setup *setup, void *ret,
+                                       int size);
+typedef void (*a2dp_endpoint_config_t) (struct a2dp_setup *setup, gboolean ret);
+
+struct a2dp_endpoint {
+       const char *(*get_name) (struct a2dp_sep *sep, void *user_data);
+       size_t (*get_capabilities) (struct a2dp_sep *sep,
+                                               uint8_t **capabilities,
+                                               void *user_data);
+       int (*select_configuration) (struct a2dp_sep *sep,
+                                               uint8_t *capabilities,
+                                               size_t length,
+                                               struct a2dp_setup *setup,
+                                               a2dp_endpoint_select_t cb,
+                                               void *user_data);
+       int (*set_configuration) (struct a2dp_sep *sep,
+                                               struct audio_device *dev,
+                                               uint8_t *configuration,
+                                               size_t length,
+                                               struct a2dp_setup *setup,
+                                               a2dp_endpoint_config_t cb,
+                                               void *user_data);
+       void (*clear_configuration) (struct a2dp_sep *sep, void *user_data);
+       void (*set_delay) (struct a2dp_sep *sep, uint16_t delay,
+                                                       void *user_data);
+};
+
+typedef void (*a2dp_select_cb_t) (struct avdtp *session,
+                                       struct a2dp_sep *sep, GSList *caps,
+                                       void *user_data);
+typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_error *err,
+                                       void *user_data);
+typedef void (*a2dp_stream_cb_t) (struct avdtp *session,
+                                       struct avdtp_error *err,
+                                       void *user_data);
+
+int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
+void a2dp_unregister(const bdaddr_t *src);
+
+struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type,
+                               uint8_t codec, gboolean delay_reporting,
+                               struct a2dp_endpoint *endpoint,
+                               void *user_data, GDestroyNotify destroy,
+                               int *err);
+void a2dp_remove_sep(struct a2dp_sep *sep);
+
+struct a2dp_sep *a2dp_get(struct avdtp *session, struct avdtp_remote_sep *sep);
+
+unsigned int a2dp_select_capabilities(struct avdtp *session,
+                                       uint8_t type, const char *sender,
+                                       a2dp_select_cb_t cb,
+                                       void *user_data);
+unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_config_cb_t cb, GSList *caps,
+                               void *user_data);
+unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_stream_cb_t cb, void *user_data);
+unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep,
+                               a2dp_stream_cb_t cb, void *user_data);
+gboolean a2dp_cancel(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 avdtp_stream *a2dp_sep_get_stream(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..fd6092a
--- /dev/null
@@ -0,0 +1,45 @@
+# Configuration file for the audio service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# 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=Gateway,Source,Socket
+
+# 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, false means only HSP is supported
+# Defaults to true
+HFP=true
+
+# Maximum number of connected HSP/HFP devices per adapter. Defaults to 1
+MaxConnected=1
+
+# Set to true to enable use of fast connectable mode (faster page scanning)
+# for HFP when incoming call starts. Default settings are restored after
+# call is answered or rejected. Page scan interval is much shorter and page
+# scan type changed to interlaced. Such allows faster connection initiated
+# by a headset.
+FastConnectable=false
+
+# Just an example of potential config options for the other interfaces
+#[A2DP]
+#SBCSources=1
+#MPEG12Sources=0
diff --git a/audio/avctp.c b/audio/avctp.c
new file mode 100644 (file)
index 0000000..ae3c04e
--- /dev/null
@@ -0,0 +1,1115 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  Texas Instruments, Inc.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+
+#include "adapter.h"
+#include "../src/device.h"
+
+#include "log.h"
+#include "error.h"
+#include "uinput.h"
+#include "btio.h"
+#include "manager.h"
+#include "device.h"
+#include "avctp.h"
+#include "avrcp.h"
+
+#define QUIRK_NO_RELEASE 1 << 0
+
+/* Message types */
+#define AVCTP_COMMAND          0
+#define AVCTP_RESPONSE         1
+
+/* Packet types */
+#define AVCTP_PACKET_SINGLE    0
+#define AVCTP_PACKET_START     1
+#define AVCTP_PACKET_CONTINUE  2
+#define AVCTP_PACKET_END       3
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avctp_header {
+       uint8_t ipid:1;
+       uint8_t cr:1;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+       uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+struct avc_header {
+       uint8_t code:4;
+       uint8_t _hdr0:4;
+       uint8_t subunit_id:3;
+       uint8_t subunit_type:5;
+       uint8_t opcode;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avctp_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t cr:1;
+       uint8_t ipid:1;
+       uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+struct avc_header {
+       uint8_t _hdr0:4;
+       uint8_t code:4;
+       uint8_t subunit_type:5;
+       uint8_t subunit_id:3;
+       uint8_t opcode;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avctp_state_callback {
+       avctp_state_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+struct avctp_server {
+       bdaddr_t src;
+       GIOChannel *io;
+       GSList *sessions;
+};
+
+struct avctp_rsp_handler {
+       uint8_t id;
+       avctp_rsp_cb func;
+       void *user_data;
+};
+
+struct avctp {
+       struct avctp_server *server;
+       bdaddr_t dst;
+
+       avctp_state_t state;
+
+       int uinput;
+
+       GIOChannel *io;
+       guint io_id;
+
+       uint16_t mtu;
+
+       uint8_t key_quirks[256];
+       GSList *handlers;
+};
+
+struct avctp_pdu_handler {
+       uint8_t opcode;
+       avctp_pdu_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+static struct {
+       const char *name;
+       uint8_t avc;
+       uint16_t uinput;
+} key_map[] = {
+       { "PLAY",               PLAY_OP,                KEY_PLAYCD },
+       { "STOP",               STAVC_OP_OP,            KEY_STOPCD },
+       { "PAUSE",              PAUSE_OP,               KEY_PAUSECD },
+       { "FORWARD",            FORWARD_OP,             KEY_NEXTSONG },
+       { "BACKWARD",           BACKWARD_OP,            KEY_PREVIOUSSONG },
+       { "REWIND",             REWIND_OP,              KEY_REWIND },
+       { "FAST FORWARD",       FAST_FORWARD_OP,        KEY_FASTFORWARD },
+       { NULL }
+};
+
+static GSList *callbacks = NULL;
+static GSList *servers = NULL;
+static GSList *handlers = NULL;
+static uint8_t id = 0;
+
+static void auth_cb(DBusError *derr, void *user_data);
+
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+       struct uinput_event event;
+
+       memset(&event, 0, sizeof(event));
+       event.type      = type;
+       event.code      = code;
+       event.value     = value;
+
+       return write(fd, &event, sizeof(event));
+}
+
+static void send_key(int fd, uint16_t key, int pressed)
+{
+       if (fd < 0)
+               return;
+
+       send_event(fd, EV_KEY, key, pressed);
+       send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static size_t handle_panel_passthrough(struct avctp *session,
+                                       uint8_t transaction, uint8_t *code,
+                                       uint8_t *subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       const char *status;
+       int pressed, i;
+
+       if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) {
+               *code = AVC_CTYPE_REJECTED;
+               return 0;
+       }
+
+       if (operand_count == 0)
+               goto done;
+
+       if (operands[0] & 0x80) {
+               status = "released";
+               pressed = 0;
+       } else {
+               status = "pressed";
+               pressed = 1;
+       }
+
+       for (i = 0; key_map[i].name != NULL; i++) {
+               uint8_t key_quirks;
+
+               if ((operands[0] & 0x7F) != key_map[i].avc)
+                       continue;
+
+               DBG("AV/C: %s %s", key_map[i].name, status);
+
+               key_quirks = session->key_quirks[key_map[i].avc];
+
+               if (key_quirks & QUIRK_NO_RELEASE) {
+                       if (!pressed) {
+                               DBG("AV/C: Ignoring release");
+                               break;
+                       }
+
+                       DBG("AV/C: treating key press as press + release");
+                       send_key(session->uinput, key_map[i].uinput, 1);
+                       send_key(session->uinput, key_map[i].uinput, 0);
+                       break;
+               }
+
+               send_key(session->uinput, key_map[i].uinput, pressed);
+               break;
+       }
+
+       if (key_map[i].name == NULL) {
+               DBG("AV/C: unknown button 0x%02X %s",
+                                               operands[0] & 0x7F, status);
+               *code = AVC_CTYPE_NOT_IMPLEMENTED;
+               return 0;
+       }
+
+done:
+       *code = AVC_CTYPE_ACCEPTED;
+       return operand_count;
+}
+
+static size_t handle_unit_info(struct avctp *session,
+                                       uint8_t transaction, uint8_t *code,
+                                       uint8_t *subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       if (*code != AVC_CTYPE_STATUS) {
+               *code = AVC_CTYPE_REJECTED;
+               return 0;
+       }
+
+       *code = AVC_CTYPE_STABLE;
+
+       /* The first operand should be 0x07 for the UNITINFO response.
+        * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+        * Interface Command Set (section 9.2.1, page 45) specs
+        * explain this value but both use it */
+       if (operand_count >= 1)
+               operands[0] = 0x07;
+       if (operand_count >= 2)
+               operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+       DBG("reply to AVC_OP_UNITINFO");
+
+       return operand_count;
+}
+
+static size_t handle_subunit_info(struct avctp *session,
+                                       uint8_t transaction, uint8_t *code,
+                                       uint8_t *subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       if (*code != AVC_CTYPE_STATUS) {
+               *code = AVC_CTYPE_REJECTED;
+               return 0;
+       }
+
+       *code = AVC_CTYPE_STABLE;
+
+       /* The first operand should be 0x07 for the UNITINFO response.
+        * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+        * Interface Command Set (section 9.2.1, page 45) specs
+        * explain this value but both use it */
+       if (operand_count >= 2)
+               operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+       DBG("reply to AVC_OP_SUBUNITINFO");
+
+       return operand_count;
+}
+
+static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode)
+{
+       for (; list; list = list->next) {
+               struct avctp_pdu_handler *handler = list->data;
+
+               if (handler->opcode == opcode)
+                       return handler;
+       }
+
+       return NULL;
+}
+
+static void avctp_disconnected(struct avctp *session)
+{
+       struct avctp_server *server;
+
+       if (!session)
+               return;
+
+       if (session->io) {
+               g_io_channel_shutdown(session->io, TRUE, NULL);
+               g_io_channel_unref(session->io);
+               session->io = NULL;
+       }
+
+       if (session->io_id) {
+               g_source_remove(session->io_id);
+               session->io_id = 0;
+
+               if (session->state == AVCTP_STATE_CONNECTING) {
+                       struct audio_device *dev;
+
+                       dev = manager_get_device(&session->server->src,
+                                                       &session->dst, FALSE);
+                       audio_device_cancel_authorization(dev, auth_cb,
+                                                               session);
+               }
+       }
+
+       if (session->uinput >= 0) {
+               char address[18];
+
+               ba2str(&session->dst, address);
+               DBG("AVCTP: closing uinput for %s", address);
+
+               ioctl(session->uinput, UI_DEV_DESTROY);
+               close(session->uinput);
+               session->uinput = -1;
+       }
+
+       server = session->server;
+       server->sessions = g_slist_remove(server->sessions, session);
+       g_slist_free_full(session->handlers, g_free);
+       g_free(session);
+}
+
+static void avctp_set_state(struct avctp *session, avctp_state_t new_state)
+{
+       GSList *l;
+       struct audio_device *dev;
+       avctp_state_t old_state = session->state;
+
+       dev = manager_get_device(&session->server->src, &session->dst, FALSE);
+       if (dev == NULL) {
+               error("avdtp_set_state(): no matching audio device");
+               return;
+       }
+
+       session->state = new_state;
+
+       for (l = callbacks; l != NULL; l = l->next) {
+               struct avctp_state_callback *cb = l->data;
+               cb->cb(dev, old_state, new_state, cb->user_data);
+       }
+
+       switch (new_state) {
+       case AVCTP_STATE_DISCONNECTED:
+               DBG("AVCTP Disconnected");
+
+               avctp_disconnected(session);
+
+               if (old_state != AVCTP_STATE_CONNECTED)
+                       break;
+
+               if (!audio_device_is_active(dev, NULL))
+                       audio_device_set_authorized(dev, FALSE);
+
+               break;
+       case AVCTP_STATE_CONNECTING:
+               DBG("AVCTP Connecting");
+               break;
+       case AVCTP_STATE_CONNECTED:
+               DBG("AVCTP Connected");
+               break;
+       default:
+               error("Invalid AVCTP state %d", new_state);
+               return;
+       }
+}
+
+static void handle_response(struct avctp *session, struct avctp_header *avctp,
+                               struct avc_header *avc, uint8_t *operands,
+                               size_t operand_count)
+{
+       GSList *l;
+
+       for (l = session->handlers; l; l = l->next) {
+               struct avctp_rsp_handler *handler = l->data;
+
+               if (handler->id != avctp->transaction)
+                       continue;
+
+               if (handler->func && handler->func(session, avc->code,
+                                               avc->subunit_type,
+                                               operands, operand_count,
+                                               handler->user_data))
+                       return;
+
+               session->handlers = g_slist_remove(session->handlers, handler);
+               g_free(handler);
+
+               return;
+       }
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+                               gpointer data)
+{
+       struct avctp *session = data;
+       uint8_t buf[1024], *operands, code, subunit;
+       struct avctp_header *avctp;
+       struct avc_header *avc;
+       int ret, packet_size, operand_count, sock;
+       struct avctp_pdu_handler *handler;
+
+       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+               goto failed;
+
+       sock = g_io_channel_unix_get_fd(session->io);
+
+       ret = read(sock, buf, sizeof(buf));
+       if (ret <= 0)
+               goto failed;
+
+       DBG("Got %d bytes of data for AVCTP session %p", ret, session);
+
+       if ((unsigned int) ret < sizeof(struct avctp_header)) {
+               error("Too small AVCTP packet");
+               goto failed;
+       }
+
+       avctp = (struct avctp_header *) buf;
+
+       DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, "
+                       "PID 0x%04X",
+                       avctp->transaction, avctp->packet_type,
+                       avctp->cr, avctp->ipid, ntohs(avctp->pid));
+
+       ret -= sizeof(struct avctp_header);
+       if ((unsigned int) ret < sizeof(struct avc_header)) {
+               error("Too small AVCTP packet");
+               goto failed;
+       }
+
+       avc = (struct avc_header *) (buf + sizeof(struct avctp_header));
+
+       ret -= sizeof(struct avc_header);
+
+       operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header);
+       operand_count = ret;
+
+       DBG("AV/C %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, "
+                       "opcode 0x%02X, %d operands",
+                       avctp->cr ? "response" : "command",
+                       avc->code, avc->subunit_type, avc->subunit_id,
+                       avc->opcode, operand_count);
+
+       if (avctp->cr == AVCTP_RESPONSE) {
+               handle_response(session, avctp, avc, operands, operand_count);
+               return TRUE;
+       }
+
+       packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH;
+       avctp->cr = AVCTP_RESPONSE;
+
+       if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+               avc->code = AVC_CTYPE_NOT_IMPLEMENTED;
+               goto done;
+       }
+
+       if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
+               avctp->ipid = 1;
+               avc->code = AVC_CTYPE_REJECTED;
+               goto done;
+       }
+
+       handler = find_handler(handlers, avc->opcode);
+       if (!handler) {
+               DBG("handler not found for 0x%02x", avc->opcode);
+               packet_size += avrcp_handle_vendor_reject(&code, operands);
+               avc->code = code;
+               goto done;
+       }
+
+       code = avc->code;
+       subunit = avc->subunit_type;
+
+       packet_size += handler->cb(session, avctp->transaction, &code,
+                                       &subunit, operands, operand_count,
+                                       handler->user_data);
+
+       avc->code = code;
+       avc->subunit_type = subunit;
+
+done:
+       ret = write(sock, buf, packet_size);
+       if (ret != packet_size)
+               goto failed;
+
+       return TRUE;
+
+failed:
+       DBG("AVCTP session %p got disconnected", session);
+       avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+       return FALSE;
+}
+
+static int uinput_create(char *name)
+{
+       struct uinput_dev dev;
+       int fd, err, i;
+
+       fd = open("/dev/uinput", O_RDWR);
+       if (fd < 0) {
+               fd = open("/dev/input/uinput", O_RDWR);
+               if (fd < 0) {
+                       fd = open("/dev/misc/uinput", O_RDWR);
+                       if (fd < 0) {
+                               err = -errno;
+                               error("Can't open input device: %s (%d)",
+                                                       strerror(-err), -err);
+                               return err;
+                       }
+               }
+       }
+
+       memset(&dev, 0, sizeof(dev));
+       if (name)
+               strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+       dev.id.bustype = BUS_BLUETOOTH;
+       dev.id.vendor  = 0x0000;
+       dev.id.product = 0x0000;
+       dev.id.version = 0x0000;
+
+       if (write(fd, &dev, sizeof(dev)) < 0) {
+               err = -errno;
+               error("Can't write device information: %s (%d)",
+                                               strerror(-err), -err);
+               close(fd);
+               return err;
+       }
+
+       ioctl(fd, UI_SET_EVBIT, EV_KEY);
+       ioctl(fd, UI_SET_EVBIT, EV_REL);
+       ioctl(fd, UI_SET_EVBIT, EV_REP);
+       ioctl(fd, UI_SET_EVBIT, EV_SYN);
+
+       for (i = 0; key_map[i].name != NULL; i++)
+               ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
+
+       if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+               err = -errno;
+               error("Can't create uinput device: %s (%d)",
+                                               strerror(-err), -err);
+               close(fd);
+               return err;
+       }
+
+       return fd;
+}
+
+static void init_uinput(struct avctp *session)
+{
+       struct audio_device *dev;
+       char address[18], name[248 + 1];
+
+       dev = manager_get_device(&session->server->src, &session->dst, FALSE);
+
+       device_get_name(dev->btd_dev, name, sizeof(name));
+       if (g_str_equal(name, "Nokia CK-20W")) {
+               session->key_quirks[FORWARD_OP] |= QUIRK_NO_RELEASE;
+               session->key_quirks[BACKWARD_OP] |= QUIRK_NO_RELEASE;
+               session->key_quirks[PLAY_OP] |= QUIRK_NO_RELEASE;
+               session->key_quirks[PAUSE_OP] |= QUIRK_NO_RELEASE;
+       }
+
+       ba2str(&session->dst, address);
+
+       session->uinput = uinput_create(address);
+       if (session->uinput < 0)
+               error("AVRCP: failed to init uinput for %s", address);
+       else
+               DBG("AVRCP: uinput initialized for %s", address);
+}
+
+static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+       struct avctp *session = data;
+       char address[18];
+       uint16_t imtu;
+       GError *gerr = NULL;
+
+       if (err) {
+               avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+               error("%s", err->message);
+               return;
+       }
+
+       bt_io_get(chan, BT_IO_L2CAP, &gerr,
+                       BT_IO_OPT_DEST, &address,
+                       BT_IO_OPT_IMTU, &imtu,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               return;
+       }
+
+       DBG("AVCTP: connected to %s", address);
+
+       if (!session->io)
+               session->io = g_io_channel_ref(chan);
+
+       init_uinput(session);
+
+       avctp_set_state(session, AVCTP_STATE_CONNECTED);
+       session->mtu = imtu;
+       session->io_id = g_io_add_watch(chan,
+                               G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                               (GIOFunc) session_cb, session);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+       struct avctp *session = user_data;
+       GError *err = NULL;
+
+       if (session->io_id) {
+               g_source_remove(session->io_id);
+               session->io_id = 0;
+       }
+
+       if (derr && dbus_error_is_set(derr)) {
+               error("Access denied: %s", derr->message);
+               avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+               return;
+       }
+
+       if (!bt_io_accept(session->io, avctp_connect_cb, session,
+                                                               NULL, &err)) {
+               error("bt_io_accept: %s", err->message);
+               g_error_free(err);
+               avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+       }
+}
+
+static struct avctp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+       for (; list; list = list->next) {
+               struct avctp_server *server = list->data;
+
+               if (bacmp(&server->src, src) == 0)
+                       return server;
+       }
+
+       return NULL;
+}
+
+static struct avctp *find_session(GSList *list, const bdaddr_t *dst)
+{
+       for (; list != NULL; list = g_slist_next(list)) {
+               struct avctp *s = list->data;
+
+               if (bacmp(dst, &s->dst))
+                       continue;
+
+               return s;
+       }
+
+       return NULL;
+}
+
+static struct avctp *avctp_get_internal(const bdaddr_t *src,
+                                                       const bdaddr_t *dst)
+{
+       struct avctp_server *server;
+       struct avctp *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)
+               return session;
+
+       session = g_new0(struct avctp, 1);
+
+       session->server = server;
+       bacpy(&session->dst, dst);
+       session->state = AVCTP_STATE_DISCONNECTED;
+
+       server->sessions = g_slist_append(server->sessions, session);
+
+       return session;
+}
+
+static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
+{
+       struct avctp *session;
+       struct audio_device *dev;
+       char address[18];
+       bdaddr_t src, dst;
+       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);
+               g_io_channel_shutdown(chan, TRUE, NULL);
+               return;
+       }
+
+       DBG("AVCTP: incoming connect from %s", address);
+
+       session = avctp_get_internal(&src, &dst);
+       if (!session)
+               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;
+               }
+       }
+
+       if (dev->control == NULL) {
+               btd_device_add_uuid(dev->btd_dev, AVRCP_REMOTE_UUID);
+               if (dev->control == NULL)
+                       goto drop;
+       }
+
+       if (session->io) {
+               error("Refusing unexpected connect from %s", address);
+               goto drop;
+       }
+
+       avctp_set_state(session, AVCTP_STATE_CONNECTING);
+       session->io = g_io_channel_ref(chan);
+
+       if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID,
+                                               auth_cb, session) < 0)
+               goto drop;
+
+       session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                                       session_cb, session);
+       return;
+
+drop:
+       if (!session || !session->io)
+               g_io_channel_shutdown(chan, TRUE, NULL);
+       if (session)
+               avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+}
+
+static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master)
+{
+       GError *err = NULL;
+       GIOChannel *io;
+
+       io = bt_io_listen(BT_IO_L2CAP, NULL, avctp_confirm_cb, NULL,
+                               NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, src,
+                               BT_IO_OPT_PSM, AVCTP_PSM,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_MASTER, master,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("%s", err->message);
+               g_error_free(err);
+       }
+
+       return io;
+}
+
+static unsigned int passthrough_id = 0;
+static unsigned int unit_id = 0;
+static unsigned int subunit_id = 0;
+
+int avctp_register(const bdaddr_t *src, gboolean master)
+{
+       struct avctp_server *server;
+
+       server = g_new0(struct avctp_server, 1);
+       if (!server)
+               return -ENOMEM;
+
+       server->io = avctp_server_socket(src, master);
+       if (!server->io) {
+               g_free(server);
+               return -1;
+       }
+
+       bacpy(&server->src, src);
+
+       servers = g_slist_append(servers, server);
+
+       if (!passthrough_id)
+               passthrough_id = avctp_register_pdu_handler(AVC_OP_PASSTHROUGH,
+                                       handle_panel_passthrough, NULL);
+
+       if (!unit_id)
+               unit_id = avctp_register_pdu_handler(AVC_OP_UNITINFO, handle_unit_info,
+                                                                       NULL);
+
+       if (!subunit_id)
+               subunit_id = avctp_register_pdu_handler(AVC_OP_SUBUNITINFO,
+                                               handle_subunit_info, NULL);
+
+       return 0;
+}
+
+void avctp_unregister(const bdaddr_t *src)
+{
+       struct avctp_server *server;
+
+       server = find_server(servers, src);
+       if (!server)
+               return;
+
+       while (server->sessions)
+               avctp_disconnected(server->sessions->data);
+
+       servers = g_slist_remove(servers, server);
+
+       g_io_channel_shutdown(server->io, TRUE, NULL);
+       g_io_channel_unref(server->io);
+       g_free(server);
+
+       if (servers)
+               return;
+
+       if (passthrough_id) {
+               avctp_unregister_pdu_handler(passthrough_id);
+               passthrough_id = 0;
+       }
+
+       if (unit_id) {
+               avctp_unregister_pdu_handler(unit_id);
+               passthrough_id = 0;
+       }
+
+       if (subunit_id) {
+               avctp_unregister_pdu_handler(subunit_id);
+               subunit_id = 0;
+       }
+}
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op)
+{
+       unsigned char buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + 2];
+       struct avctp_header *avctp = (void *) buf;
+       struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
+       uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
+       int sk;
+
+       if (session->state != AVCTP_STATE_CONNECTED)
+               return -ENOTCONN;
+
+       memset(buf, 0, sizeof(buf));
+
+       avctp->transaction = id++;
+       avctp->packet_type = AVCTP_PACKET_SINGLE;
+       avctp->cr = AVCTP_COMMAND;
+       avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+       avc->code = AVC_CTYPE_CONTROL;
+       avc->subunit_type = AVC_SUBUNIT_PANEL;
+       avc->opcode = AVC_OP_PASSTHROUGH;
+
+       operands[0] = op & 0x7f;
+       operands[1] = 0;
+
+       sk = g_io_channel_unix_get_fd(session->io);
+
+       if (write(sk, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       /* Button release */
+       avctp->transaction = id++;
+       operands[0] |= 0x80;
+
+       if (write(sk, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
+                               uint8_t code, uint8_t subunit, uint8_t opcode,
+                               uint8_t *operands, size_t operand_count)
+{
+       uint8_t *buf;
+       struct avctp_header *avctp;
+       struct avc_header *avc;
+       uint8_t *pdu;
+       int sk, err = 0;
+       uint16_t size;
+
+       if (session->state != AVCTP_STATE_CONNECTED)
+               return -ENOTCONN;
+
+       sk = g_io_channel_unix_get_fd(session->io);
+       size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + operand_count;
+       buf = g_malloc0(size);
+
+       avctp = (void *) buf;
+       avc = (void *) &buf[AVCTP_HEADER_LENGTH];
+       pdu = (void *) &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
+
+       avctp->transaction = transaction;
+       avctp->packet_type = AVCTP_PACKET_SINGLE;
+       avctp->cr = cr;
+       avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+       avc->code = code;
+       avc->subunit_type = subunit;
+       avc->opcode = opcode;
+
+       memcpy(pdu, operands, operand_count);
+
+       if (write(sk, buf, size) < 0)
+               err = -errno;
+
+       g_free(buf);
+       return err;
+}
+
+int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
+                               uint8_t code, uint8_t subunit,
+                               uint8_t *operands, size_t operand_count)
+{
+       return avctp_send(session, transaction, AVCTP_RESPONSE, code, subunit,
+                                       AVC_OP_VENDORDEP, operands, operand_count);
+}
+
+int avctp_send_vendordep_req(struct avctp *session, uint8_t code,
+                                       uint8_t subunit, uint8_t *operands,
+                                       size_t operand_count,
+                                       avctp_rsp_cb func, void *user_data)
+{
+       struct avctp_rsp_handler *handler;
+       int err;
+
+       err = avctp_send(session, id, AVCTP_COMMAND, code, subunit,
+                               AVC_OP_VENDORDEP, operands, operand_count);
+       if (err < 0)
+               return err;
+
+       handler = g_new0(struct avctp_rsp_handler, 1);
+       handler->id = id;
+       handler->func = func;
+       handler->user_data = user_data;
+
+       session->handlers = g_slist_prepend(session->handlers, handler);
+
+       id++;
+
+       return 0;
+}
+
+unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data)
+{
+       struct avctp_state_callback *state_cb;
+       static unsigned int id = 0;
+
+       state_cb = g_new(struct avctp_state_callback, 1);
+       state_cb->cb = cb;
+       state_cb->user_data = user_data;
+       state_cb->id = ++id;
+
+       callbacks = g_slist_append(callbacks, state_cb);
+
+       return state_cb->id;
+}
+
+gboolean avctp_remove_state_cb(unsigned int id)
+{
+       GSList *l;
+
+       for (l = callbacks; l != NULL; l = l->next) {
+               struct avctp_state_callback *cb = l->data;
+               if (cb && cb->id == id) {
+                       callbacks = g_slist_remove(callbacks, cb);
+                       g_free(cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_pdu_cb cb,
+                                                       void *user_data)
+{
+       struct avctp_pdu_handler *handler;
+       static unsigned int id = 0;
+
+       handler = find_handler(handlers, opcode);
+       if (handler)
+               return 0;
+
+       handler = g_new(struct avctp_pdu_handler, 1);
+       handler->opcode = opcode;
+       handler->cb = cb;
+       handler->user_data = user_data;
+       handler->id = ++id;
+
+       handlers = g_slist_append(handlers, handler);
+
+       return handler->id;
+}
+
+gboolean avctp_unregister_pdu_handler(unsigned int id)
+{
+       GSList *l;
+
+       for (l = handlers; l != NULL; l = l->next) {
+               struct avctp_pdu_handler *handler = l->data;
+
+               if (handler->id == id) {
+                       handlers = g_slist_remove(handlers, handler);
+                       g_free(handler);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       struct avctp *session;
+       GError *err = NULL;
+       GIOChannel *io;
+
+       session = avctp_get_internal(src, dst);
+       if (!session)
+               return NULL;
+
+       if (session->state > AVCTP_STATE_DISCONNECTED)
+               return session;
+
+       avctp_set_state(session, AVCTP_STATE_CONNECTING);
+
+       io = bt_io_connect(BT_IO_L2CAP, avctp_connect_cb, session, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &session->server->src,
+                               BT_IO_OPT_DEST_BDADDR, &session->dst,
+                               BT_IO_OPT_PSM, AVCTP_PSM,
+                               BT_IO_OPT_INVALID);
+       if (err) {
+               avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+               error("%s", err->message);
+               g_error_free(err);
+               return NULL;
+       }
+
+       session->io = io;
+
+       return session;
+}
+
+void avctp_disconnect(struct avctp *session)
+{
+       if (!session->io)
+               return;
+
+       avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+}
+
+struct avctp *avctp_get(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       return avctp_get_internal(src, dst);
+}
diff --git a/audio/avctp.h b/audio/avctp.h
new file mode 100644 (file)
index 0000000..d0cbd97
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define AVCTP_PSM 23
+
+#define AVC_MTU 512
+#define AVC_HEADER_LENGTH 3
+
+/* ctype entries */
+#define AVC_CTYPE_CONTROL              0x0
+#define AVC_CTYPE_STATUS               0x1
+#define AVC_CTYPE_NOTIFY               0x3
+#define AVC_CTYPE_NOT_IMPLEMENTED      0x8
+#define AVC_CTYPE_ACCEPTED             0x9
+#define AVC_CTYPE_REJECTED             0xA
+#define AVC_CTYPE_STABLE               0xC
+#define AVC_CTYPE_CHANGED              0xD
+#define AVC_CTYPE_INTERIM              0xF
+
+/* opcodes */
+#define AVC_OP_VENDORDEP               0x00
+#define AVC_OP_UNITINFO                        0x30
+#define AVC_OP_SUBUNITINFO             0x31
+#define AVC_OP_PASSTHROUGH             0x7c
+
+/* subunits of interest */
+#define AVC_SUBUNIT_PANEL              0x09
+
+/* operands in passthrough commands */
+#define VOL_UP_OP                      0x41
+#define VOL_DOWN_OP                    0x42
+#define MUTE_OP                                0x43
+#define PLAY_OP                                0x44
+#define STAVC_OP_OP                    0x45
+#define PAUSE_OP                       0x46
+#define RECORD_OP                      0x47
+#define REWIND_OP                      0x48
+#define FAST_FORWARD_OP                        0x49
+#define EJECT_OP                       0x4a
+#define FORWARD_OP                     0x4b
+#define BACKWARD_OP                    0x4c
+
+struct avctp;
+
+typedef enum {
+       AVCTP_STATE_DISCONNECTED = 0,
+       AVCTP_STATE_CONNECTING,
+       AVCTP_STATE_CONNECTED
+} avctp_state_t;
+
+typedef void (*avctp_state_cb) (struct audio_device *dev,
+                               avctp_state_t old_state,
+                               avctp_state_t new_state,
+                               void *user_data);
+
+typedef size_t (*avctp_pdu_cb) (struct avctp *session, uint8_t transaction,
+                                       uint8_t *code, uint8_t *subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data);
+typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
+                                       uint8_t subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data);
+
+unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data);
+gboolean avctp_remove_state_cb(unsigned int id);
+
+int avctp_register(const bdaddr_t *src, gboolean master);
+void avctp_unregister(const bdaddr_t *src);
+
+struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst);
+struct avctp *avctp_get(const bdaddr_t *src, const bdaddr_t *dst);
+void avctp_disconnect(struct avctp *session);
+
+unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_pdu_cb cb,
+                                                       void *user_data);
+gboolean avctp_unregister_pdu_handler(unsigned int id);
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op);
+int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
+                               uint8_t code, uint8_t subunit,
+                               uint8_t *operands, size_t operand_count);
+int avctp_send_vendordep_req(struct avctp *session, uint8_t code,
+                                       uint8_t subunit, uint8_t *operands,
+                                       size_t operand_count,
+                                       avctp_rsp_cb func, void *user_data);
diff --git a/audio/avdtp.c b/audio/avdtp.c
new file mode 100644 (file)
index 0000000..041abc3
--- /dev/null
@@ -0,0 +1,4055 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "log.h"
+
+#include "../src/adapter.h"
+#include "../src/manager.h"
+#include "../src/device.h"
+
+#include "device.h"
+#include "manager.h"
+#include "control.h"
+#include "avdtp.h"
+#include "btio.h"
+#include "sink.h"
+#include "source.h"
+
+#define AVDTP_PSM 25
+
+#define MAX_SEID 0x3E
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define AVDTP_DISCOVER                         0x01
+#define AVDTP_GET_CAPABILITIES                 0x02
+#define AVDTP_SET_CONFIGURATION                        0x03
+#define AVDTP_GET_CONFIGURATION                        0x04
+#define AVDTP_RECONFIGURE                      0x05
+#define AVDTP_OPEN                             0x06
+#define AVDTP_START                            0x07
+#define AVDTP_CLOSE                            0x08
+#define AVDTP_SUSPEND                          0x09
+#define AVDTP_ABORT                            0x0A
+#define AVDTP_SECURITY_CONTROL                 0x0B
+#define AVDTP_GET_ALL_CAPABILITIES             0x0C
+#define AVDTP_DELAY_REPORT                     0x0D
+
+#define AVDTP_PKT_TYPE_SINGLE                  0x00
+#define AVDTP_PKT_TYPE_START                   0x01
+#define AVDTP_PKT_TYPE_CONTINUE                        0x02
+#define AVDTP_PKT_TYPE_END                     0x03
+
+#define AVDTP_MSG_TYPE_COMMAND                 0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT              0x01
+#define AVDTP_MSG_TYPE_ACCEPT                  0x02
+#define AVDTP_MSG_TYPE_REJECT                  0x03
+
+#define REQ_TIMEOUT 6
+#define ABORT_TIMEOUT 2
+#define DISCONNECT_TIMEOUT 1
+#define 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));
+
+struct delay_req {
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+       uint16_t delay;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct seid_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct setconf_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+       uint8_t int_seid:6;
+       uint8_t rfa1:2;
+
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+       uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+
+       uint8_t serv_cap;
+       uint8_t serv_cap_len;
+
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+       uint16_t delay;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct in_buf {
+       gboolean active;
+       int no_of_packets;
+       uint8_t transaction;
+       uint8_t message_type;
+       uint8_t signal_id;
+       uint8_t buf[1024];
+       uint8_t data_size;
+};
+
+struct pending_req {
+       uint8_t transaction;
+       uint8_t signal_id;
+       void *data;
+       size_t data_size;
+       struct avdtp_stream *stream; /* Set if the request targeted a stream */
+       guint timeout;
+       gboolean collided;
+};
+
+struct avdtp_remote_sep {
+       uint8_t seid;
+       uint8_t type;
+       uint8_t media_type;
+       struct avdtp_service_capability *codec;
+       gboolean delay_reporting;
+       GSList *caps; /* of type struct avdtp_service_capability */
+       struct avdtp_stream *stream;
+};
+
+struct avdtp_server {
+       bdaddr_t src;
+       uint16_t version;
+       GIOChannel *io;
+       GSList *seps;
+       GSList *sessions;
+};
+
+struct avdtp_local_sep {
+       avdtp_state_t state;
+       struct avdtp_stream *stream;
+       struct seid_info info;
+       uint8_t codec;
+       gboolean delay_reporting;
+       GSList *caps;
+       struct avdtp_sep_ind *ind;
+       struct avdtp_sep_cfm *cfm;
+       void *user_data;
+       struct avdtp_server *server;
+};
+
+struct stream_callback {
+       avdtp_stream_state_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+struct avdtp_state_callback {
+       avdtp_session_state_cb cb;
+       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;
+       gboolean delay_reporting;
+       uint16_t delay;         /* AVDTP 1.3 Delay Reporting feature */
+       gboolean starting;      /* only valid while sep state == OPEN */
+};
+
+/* Structure describing an AVDTP connection between two devices */
+
+struct avdtp {
+       int ref;
+       int free_lock;
+
+       uint16_t version;
+
+       struct avdtp_server *server;
+       bdaddr_t dst;
+
+       avdtp_session_state_t state;
+
+       /* True if the session should be automatically disconnected */
+       gboolean auto_dc;
+
+       /* True if the entire device is being disconnected */
+       gboolean device_disconnect;
+
+       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 void auth_cb(DBusError *derr, void *user_data);
+
+static struct avdtp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+       for (; list; list = list->next) {
+               struct avdtp_server *server = list->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);
+       }
+
+       /* Check if there is enough space to start packet */
+       if (session->omtu < sizeof(start)) {
+               error("No enough space to fragment packet");
+               return FALSE;
+       }
+
+       /* Count the number of needed fragments */
+       cont_fragments = (len - (session->omtu - sizeof(start))) /
+                                       (session->omtu - sizeof(cont)) + 1;
+
+       DBG("%zu bytes split into %d fragments", len, cont_fragments + 1);
+
+       /* Send the start packet */
+       memset(&start, 0, sizeof(start));
+       start.transaction = transaction;
+       start.packet_type = AVDTP_PKT_TYPE_START;
+       start.message_type = message_type;
+       start.no_of_packets = cont_fragments + 1;
+       start.signal_id = signal_id;
+
+       memcpy(session->buf, &start, sizeof(start));
+       memcpy(session->buf + sizeof(start), data,
+                                       session->omtu - sizeof(start));
+
+       if (!try_send(sock, session->buf, session->omtu))
+               return FALSE;
+
+       DBG("first packet with %zu bytes sent", session->omtu - sizeof(start));
+
+       sent = session->omtu - sizeof(start);
+
+       /* Send the continue fragments and the end packet */
+       while (sent < len) {
+               int left, to_copy;
+
+               left = len - sent;
+               if (left + sizeof(cont) > session->omtu) {
+                       cont.packet_type = AVDTP_PKT_TYPE_CONTINUE;
+                       to_copy = session->omtu - sizeof(cont);
+                       DBG("sending continue with %d bytes", to_copy);
+               } else {
+                       cont.packet_type = AVDTP_PKT_TYPE_END;
+                       to_copy = left;
+                       DBG("sending end with %d bytes", to_copy);
+               }
+
+               cont.transaction = transaction;
+               cont.message_type = message_type;
+
+               memcpy(session->buf, &cont, sizeof(cont));
+               memcpy(session->buf + sizeof(cont), data + sent, to_copy);
+
+               if (!try_send(sock, session->buf, to_copy + sizeof(cont)))
+                       return FALSE;
+
+               sent += to_copy;
+       }
+
+       return TRUE;
+}
+
+static void pending_req_free(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;
+
+       DBG("Timed out waiting for peer to close the transport channel");
+
+       stream->timer = 0;
+
+       close_stream(stream);
+
+       return FALSE;
+}
+
+static gboolean stream_open_timeout(gpointer user_data)
+{
+       struct avdtp_stream *stream = user_data;
+
+       DBG("Timed out waiting for peer to open the transport channel");
+
+       stream->timer = 0;
+
+       stream->session->pending_open = NULL;
+
+       avdtp_abort(stream->session, stream);
+
+       return FALSE;
+}
+
+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);
+
+       if (session->device_disconnect) {
+               session->dc_timer = g_idle_add(disconnect_timeout, session);
+               return;
+       }
+
+       session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+                                               disconnect_timeout,
+                                               session);
+}
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id)
+{
+       err->category = category;
+
+       if (category == AVDTP_ERRNO)
+               err->err.posix_errno = id;
+       else
+               err->err.error_code = id;
+}
+
+uint8_t avdtp_error_category(struct avdtp_error *err)
+{
+       return err->category;
+}
+
+int avdtp_error_error_code(struct avdtp_error *err)
+{
+       assert(err->category != AVDTP_ERRNO);
+       return err->err.error_code;
+}
+
+int avdtp_error_posix_errno(struct avdtp_error *err)
+{
+       assert(err->category == AVDTP_ERRNO);
+       return err->err.posix_errno;
+}
+
+static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session,
+                                                       uint8_t rseid)
+{
+       GSList *l;
+
+       for (l = session->streams; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_stream *stream = l->data;
+
+               if (stream->rseid == rseid)
+                       return stream;
+       }
+
+       return NULL;
+}
+
+static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid)
+{
+       GSList *l;
+
+       for (l = seps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_remote_sep *sep = l->data;
+
+               if (sep->seid == seid)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+static void avdtp_set_state(struct avdtp *session,
+                                       avdtp_session_state_t new_state)
+{
+       GSList *l;
+       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)
+               close_stream(stream);
+
+       if (stream->io_id)
+               g_source_remove(stream->io_id);
+
+       g_slist_free_full(stream->callbacks, g_free);
+       g_slist_free_full(stream->caps, g_free);
+
+       g_free(stream);
+}
+
+static gboolean stream_timeout(gpointer user_data)
+{
+       struct avdtp_stream *stream = user_data;
+       struct avdtp *session = stream->session;
+
+       if (avdtp_close(session, stream, FALSE) < 0)
+               error("stream_timeout: closing AVDTP stream failed");
+
+       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 int get_send_buffer_size(int sk)
+{
+       int size;
+       socklen_t optlen = sizeof(size);
+
+       if (getsockopt(sk, SOL_SOCKET, SO_SNDBUF, &size, &optlen) < 0) {
+               int err = -errno;
+               error("getsockopt(SO_SNDBUF) failed: %s (%d)", strerror(-err),
+                                                                       -err);
+               return err;
+       }
+
+       /*
+        * Doubled value is returned by getsockopt since kernel uses that
+        * space for its own purposes (see man 7 socket, bookkeeping overhead
+        * for SO_SNDBUF).
+        */
+       return size / 2;
+}
+
+static int set_send_buffer_size(int sk, int size)
+{
+       socklen_t optlen = sizeof(size);
+
+       if (setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &size, optlen) < 0) {
+               int err = -errno;
+               error("setsockopt(SO_SNDBUF) failed: %s (%d)", strerror(-err),
+                                                                       -err);
+               return err;
+       }
+
+       return 0;
+}
+
+static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
+                                       uint16_t imtu, uint16_t omtu)
+{
+       struct avdtp_stream *stream = session->pending_open;
+       struct avdtp_local_sep *sep = stream->lsep;
+       int sk, buf_size, min_buf_size;
+       GError *err = NULL;
+
+       session->pending_open = NULL;
+
+       if (stream->timer) {
+               g_source_remove(stream->timer);
+               stream->timer = 0;
+       }
+
+       if (io == NULL) {
+               if (!stream->open_acp && sep->cfm && sep->cfm->open) {
+                       struct avdtp_error err;
+                       avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+                       sep->cfm->open(session, sep, NULL, &err,
+                                       sep->user_data);
+               }
+               return;
+       }
+
+       if (stream->io == NULL)
+               stream->io = g_io_channel_ref(io);
+
+       stream->omtu = omtu;
+       stream->imtu = imtu;
+
+       /* Apply special settings only if local SEP is of type SRC */
+       if (sep->info.type != AVDTP_SEP_TYPE_SOURCE)
+               goto proceed;
+
+       bt_io_set(stream->io, BT_IO_L2CAP, &err,
+                                       BT_IO_OPT_FLUSHABLE, TRUE,
+                                       BT_IO_OPT_INVALID);
+       if (err != NULL) {
+               error("Enabling flushable packets failed: %s", err->message);
+               g_error_free(err);
+       } else
+               DBG("Flushable packets enabled");
+
+       sk = g_io_channel_unix_get_fd(stream->io);
+       buf_size = get_send_buffer_size(sk);
+       if (buf_size < 0)
+               goto proceed;
+
+       DBG("sk %d, omtu %d, send buffer size %d", sk, omtu, buf_size);
+       min_buf_size = omtu * 2;
+       if (buf_size < min_buf_size) {
+               DBG("send buffer size to be increassed to %d",
+                               min_buf_size);
+               set_send_buffer_size(sk, min_buf_size);
+       }
+
+proceed:
+       if (!stream->open_acp && sep->cfm && sep->cfm->open)
+               sep->cfm->open(session, sep, stream, NULL, sep->user_data);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+       stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       (GIOFunc) transport_cb, stream);
+}
+
+static int pending_req_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct pending_req *req = a;
+       const struct avdtp_stream *stream = b;
+
+       if (req->stream == stream)
+               return 0;
+
+       return -1;
+}
+
+static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream)
+{
+       GSList *l;
+       struct pending_req *req;
+
+       while ((l = g_slist_find_custom(session->prio_queue, stream,
+                                                       pending_req_cmp))) {
+               req = l->data;
+               pending_req_free(req);
+               session->prio_queue = g_slist_remove(session->prio_queue, req);
+       }
+
+       while ((l = g_slist_find_custom(session->req_queue, stream,
+                                                       pending_req_cmp))) {
+               req = l->data;
+               pending_req_free(req);
+               session->req_queue = g_slist_remove(session->req_queue, req);
+       }
+}
+
+static void handle_unanswered_req(struct avdtp *session,
+                                               struct avdtp_stream *stream)
+{
+       struct pending_req *req;
+       struct avdtp_local_sep *lsep;
+       struct avdtp_error err;
+
+       if (session->req->signal_id == AVDTP_ABORT) {
+               /* Avoid freeing the Abort request here */
+               DBG("handle_unanswered_req: Abort req, returning");
+               session->req->stream = NULL;
+               return;
+       }
+
+       req = session->req;
+       session->req = NULL;
+
+       avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+
+       lsep = stream->lsep;
+
+       switch (req->signal_id) {
+       case AVDTP_RECONFIGURE:
+               error("No reply to Reconfigure request");
+               if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+                       lsep->cfm->reconfigure(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_OPEN:
+               error("No reply to Open request");
+               if (lsep && lsep->cfm && lsep->cfm->open)
+                       lsep->cfm->open(session, lsep, stream, &err,
+                                       lsep->user_data);
+               break;
+       case AVDTP_START:
+               error("No reply to Start request");
+               if (lsep && lsep->cfm && lsep->cfm->start)
+                       lsep->cfm->start(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_SUSPEND:
+               error("No reply to Suspend request");
+               if (lsep && lsep->cfm && lsep->cfm->suspend)
+                       lsep->cfm->suspend(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_CLOSE:
+               error("No reply to Close request");
+               if (lsep && lsep->cfm && lsep->cfm->close)
+                       lsep->cfm->close(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_SET_CONFIGURATION:
+               error("No reply to SetConfiguration request");
+               if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+                       lsep->cfm->set_configuration(session, lsep, stream,
+                                                       &err, lsep->user_data);
+       }
+
+       pending_req_free(req);
+}
+
+static void avdtp_sep_set_state(struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               avdtp_state_t state)
+{
+       struct avdtp_stream *stream = sep->stream;
+       avdtp_state_t old_state;
+       struct avdtp_error err, *err_ptr = NULL;
+       GSList *l;
+
+       if (!stream) {
+               error("Error changing sep state: stream not available");
+               return;
+       }
+
+       if (sep->state == state) {
+               avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+               DBG("stream state change failed: %s", avdtp_strerror(&err));
+               err_ptr = &err;
+       } else {
+               err_ptr = NULL;
+               DBG("stream state changed: %s -> %s",
+                               avdtp_statestr(sep->state),
+                               avdtp_statestr(state));
+       }
+
+       old_state = sep->state;
+       sep->state = state;
+
+       switch (state) {
+       case AVDTP_STATE_CONFIGURED:
+               if (sep->info.type == AVDTP_SEP_TYPE_SINK)
+                       avdtp_delay_report(session, stream, stream->delay);
+               break;
+       case AVDTP_STATE_OPEN:
+               stream->starting = FALSE;
+               if ((old_state > AVDTP_STATE_OPEN && session->auto_dc) ||
+                                                       stream->open_acp)
+                       stream->idle_timer = g_timeout_add_seconds(STREAM_TIMEOUT,
+                                                               stream_timeout,
+                                                               stream);
+               break;
+       case AVDTP_STATE_STREAMING:
+               if (stream->idle_timer) {
+                       g_source_remove(stream->idle_timer);
+                       stream->idle_timer = 0;
+               }
+               stream->open_acp = FALSE;
+               break;
+       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;
+               }
+               if (session->pending_open == stream)
+                       handle_transport_connect(session, NULL, 0, 0);
+               if (session->req && session->req->stream == stream)
+                       handle_unanswered_req(session, stream);
+               /* Remove pending commands for this stream from the queue */
+               cleanup_queue(session, stream);
+               break;
+       default:
+               break;
+       }
+
+       l = stream->callbacks;
+       while (l != NULL) {
+               struct stream_callback *cb = l->data;
+               l = g_slist_next(l);
+               cb->cb(stream, old_state, state, err_ptr, cb->user_data);
+       }
+
+       if (state == AVDTP_STATE_IDLE &&
+                               g_slist_find(session->streams, stream)) {
+               session->streams = g_slist_remove(session->streams, stream);
+               stream_free(stream);
+       }
+}
+
+static void finalize_discovery(struct avdtp *session, int err)
+{
+       struct avdtp_error avdtp_err;
+
+       avdtp_error_init(&avdtp_err, AVDTP_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 int avdtp_cancel_authorization(struct avdtp *session)
+{
+       struct audio_device *dev;
+
+       if (session->state != AVDTP_SESSION_STATE_CONNECTING)
+               return 0;
+
+       dev = manager_get_device(&session->server->src, &session->dst, FALSE);
+       if (dev == NULL)
+               return -ENODEV;
+
+       return audio_device_cancel_authorization(dev, auth_cb, session);
+}
+
+static void connection_lost(struct avdtp *session, int err)
+{
+       char address[18];
+
+       ba2str(&session->dst, address);
+       DBG("Disconnected from %s", address);
+
+       if (err != EACCES)
+               avdtp_cancel_authorization(session);
+
+       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--;
+
+       DBG("%p: ref=%d", session, session->ref);
+
+       if (session->ref == 1) {
+               if (session->state == AVDTP_SESSION_STATE_CONNECTING &&
+                                                               session->io) {
+                       avdtp_cancel_authorization(session);
+                       g_io_channel_shutdown(session->io, TRUE, NULL);
+                       g_io_channel_unref(session->io);
+                       session->io = NULL;
+                       avdtp_set_state(session,
+                                       AVDTP_SESSION_STATE_DISCONNECTED);
+               }
+
+               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;
+
+       DBG("%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_free_full(session->seps, g_free);
+
+       g_free(session->buf);
+
+       g_free(session);
+}
+
+struct avdtp *avdtp_ref(struct avdtp *session)
+{
+       session->ref++;
+       DBG("%p: ref=%d", session, session->ref);
+       if (session->dc_timer)
+               remove_disconnect_timer(session);
+       return session;
+}
+
+static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp_server *server,
+                                                       uint8_t seid)
+{
+       GSList *l;
+
+       for (l = server->seps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_local_sep *sep = l->data;
+
+               if (sep->info.seid == seid)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+                                               struct avdtp_local_sep *lsep)
+{
+       GSList *l;
+
+       if (lsep->info.inuse)
+               return NULL;
+
+       for (l = session->seps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_remote_sep *sep = l->data;
+               struct avdtp_service_capability *cap;
+               struct avdtp_media_codec_capability *codec_data;
+
+               /* Type must be different: source <-> sink */
+               if (sep->type == lsep->info.type)
+                       continue;
+
+               if (sep->media_type != lsep->info.media_type)
+                       continue;
+
+               if (!sep->codec)
+                       continue;
+
+               cap = sep->codec;
+               codec_data = (void *) cap->data;
+
+               if (codec_data->media_codec_type != lsep->codec)
+                       continue;
+
+               if (sep->stream == NULL)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+static GSList *caps_to_list(uint8_t *data, int size,
+                               struct avdtp_service_capability **codec,
+                               gboolean *delay_reporting)
+{
+       GSList *caps;
+       int processed;
+
+       if (delay_reporting)
+               *delay_reporting = FALSE;
+
+       for (processed = 0, caps = NULL; processed + 2 <= size;) {
+               struct avdtp_service_capability *cap;
+               uint8_t length, category;
+
+               category = data[0];
+               length = data[1];
+
+               if (processed + 2 + length > size) {
+                       error("Invalid capability data in getcap resp");
+                       break;
+               }
+
+               cap = g_malloc(sizeof(struct avdtp_service_capability) +
+                                       length);
+               memcpy(cap, data, 2 + length);
+
+               processed += 2 + length;
+               data += 2 + length;
+
+               caps = g_slist_append(caps, cap);
+
+               if (category == AVDTP_MEDIA_CODEC &&
+                               length >=
+                               sizeof(struct avdtp_media_codec_capability))
+                       *codec = cap;
+               else if (category == AVDTP_DELAY_REPORTING && delay_reporting)
+                       *delay_reporting = TRUE;
+       }
+
+       return caps;
+}
+
+static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction,
+                                                       uint8_t signal_id)
+{
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT,
+                                                       signal_id, NULL, 0);
+}
+
+static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+                                                       void *buf, int size)
+{
+       GSList *l;
+       unsigned int rsp_size, sep_count, i;
+       struct seid_info *seps;
+       gboolean ret;
+
+       sep_count = g_slist_length(session->server->seps);
+
+       if (sep_count == 0) {
+               uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
+               return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_DISCOVER, &err, sizeof(err));
+       }
+
+       rsp_size = sep_count * sizeof(struct seid_info);
+
+       seps = g_new0(struct seid_info, sep_count);
+
+       for (l = session->server->seps, i = 0; l != NULL; l = l->next, i++) {
+               struct avdtp_local_sep *sep = l->data;
+
+               memcpy(&seps[i], &sep->info, sizeof(struct seid_info));
+       }
+
+       ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                               AVDTP_DISCOVER, seps, rsp_size);
+       g_free(seps);
+
+       return ret;
+}
+
+static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, unsigned int size,
+                                       gboolean get_all)
+{
+       GSList *l, *caps;
+       struct avdtp_local_sep *sep = NULL;
+       unsigned int rsp_size;
+       uint8_t err, buf[1024], *ptr = buf;
+       uint8_t cmd;
+
+       cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES;
+
+       if (size < sizeof(struct seid_req)) {
+               err = AVDTP_BAD_LENGTH;
+               goto failed;
+       }
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (get_all && session->server->version < 0x0103)
+               return avdtp_unknown_cmd(session, transaction, cmd);
+
+       if (!sep->ind->get_capability(session, sep, get_all, &caps,
+                                                       &err, sep->user_data))
+               goto failed;
+
+       for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (rsp_size + cap->length + 2 > sizeof(buf))
+                       break;
+
+               memcpy(ptr, cap, cap->length + 2);
+               rsp_size += cap->length + 2;
+               ptr += cap->length + 2;
+
+               g_free(cap);
+       }
+
+       g_slist_free(caps);
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd,
+                                                               buf, rsp_size);
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd,
+                                                       &err, sizeof(err));
+}
+
+static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream,
+                                               struct avdtp_error *err)
+{
+       struct conf_rej rej;
+       struct avdtp_local_sep *sep;
+
+       if (err != NULL) {
+               rej.error = AVDTP_UNSUPPORTED_CONFIGURATION;
+               rej.category = err->err.error_code;
+               avdtp_send(session, session->in.transaction,
+                               AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION,
+                               &rej, sizeof(rej));
+               return;
+       }
+
+       if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                       AVDTP_SET_CONFIGURATION, NULL, 0)) {
+               stream_free(stream);
+               return;
+       }
+
+       sep = stream->lsep;
+       sep->stream = stream;
+       sep->info.inuse = 1;
+       session->streams = g_slist_append(session->streams, stream);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+}
+
+static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
+                               struct setconf_req *req, unsigned int size)
+{
+       struct conf_rej rej;
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err, category = 0x00;
+       struct 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");
+               err = AVDTP_BAD_STATE;
+               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");
+                               err = AVDTP_BAD_STATE;
+                               goto failed;
+                       }
+               }
+               break;
+       case AVDTP_SEP_TYPE_SINK:
+               if (!dev->source) {
+                       btd_device_add_uuid(dev->btd_dev, A2DP_SOURCE_UUID);
+                       if (!dev->source) {
+                               error("Unable to get a audio source object");
+                               err = AVDTP_BAD_STATE;
+                               goto failed;
+                       }
+               }
+               break;
+       }
+
+       stream = g_new0(struct avdtp_stream, 1);
+       stream->session = session;
+       stream->lsep = sep;
+       stream->rseid = req->int_seid;
+       stream->caps = caps_to_list(req->caps,
+                                       size - sizeof(struct setconf_req),
+                                       &stream->codec,
+                                       &stream->delay_reporting);
+
+       /* Verify that the Media Transport capability's length = 0. Reject otherwise */
+       for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (cap->category == AVDTP_MEDIA_TRANSPORT && cap->length != 0) {
+                       err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
+                       goto failed_stream;
+               }
+       }
+
+       if (stream->delay_reporting && session->version < 0x0103)
+               session->version = 0x0103;
+
+       if (sep->ind && sep->ind->set_configuration) {
+               if (!sep->ind->set_configuration(session, sep, stream,
+                                                       stream->caps,
+                                                       setconf_cb,
+                                                       sep->user_data)) {
+                       err = AVDTP_UNSUPPORTED_CONFIGURATION;
+                       category = 0x00;
+                       goto failed_stream;
+               }
+       } else {
+               if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                       AVDTP_SET_CONFIGURATION, NULL, 0)) {
+                       stream_free(stream);
+                       return FALSE;
+               }
+
+               sep->stream = stream;
+               sep->info.inuse = 1;
+               session->streams = g_slist_append(session->streams, stream);
+
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+       }
+
+       return TRUE;
+
+failed_stream:
+       stream_free(stream);
+failed:
+       rej.error = err;
+       rej.category = category;
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_SET_CONFIGURATION, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, int size)
+{
+       GSList *l;
+       struct avdtp_local_sep *sep = NULL;
+       int rsp_size;
+       uint8_t err;
+       uint8_t buf[1024];
+       uint8_t *ptr = buf;
+
+       if (size < (int) sizeof(struct seid_req)) {
+               error("Too short getconf request");
+               return FALSE;
+       }
+
+       memset(buf, 0, sizeof(buf));
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+       if (!sep->stream || !sep->stream->caps) {
+               err = AVDTP_UNSUPPORTED_CONFIGURATION;
+               goto failed;
+       }
+
+       for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (rsp_size + cap->length + 2 > (int) sizeof(buf))
+                       break;
+
+               memcpy(ptr, cap, cap->length + 2);
+               rsp_size += cap->length + 2;
+               ptr += cap->length + 2;
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                               AVDTP_GET_CONFIGURATION, buf, rsp_size);
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_GET_CONFIGURATION, &err, sizeof(err));
+}
+
+static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, int size)
+{
+       struct conf_rej rej;
+
+       rej.error = AVDTP_NOT_SUPPORTED_COMMAND;
+       rej.category = 0x00;
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_RECONFIGURE, &rej, sizeof(rej));
+}
+
+static void check_seid_collision(struct pending_req *req, uint8_t id)
+{
+       struct seid_req *seid = req->data;
+
+       if (seid->acp_seid == id)
+               req->collided = TRUE;
+}
+
+static void check_start_collision(struct pending_req *req, uint8_t id)
+{
+       struct start_req *start = req->data;
+       struct seid *seid = &start->first_seid;
+       int count = 1 + req->data_size - sizeof(struct start_req);
+       int i;
+
+       for (i = 0; i < count; i++, seid++) {
+               if (seid->seid == id) {
+                       req->collided = TRUE;
+                       return;
+               }
+       }
+}
+
+static void check_suspend_collision(struct pending_req *req, uint8_t id)
+{
+       struct suspend_req *suspend = req->data;
+       struct seid *seid = &suspend->first_seid;
+       int count = 1 + req->data_size - sizeof(struct suspend_req);
+       int i;
+
+       for (i = 0; i < count; i++, seid++) {
+               if (seid->seid == id) {
+                       req->collided = TRUE;
+                       return;
+               }
+       }
+}
+
+static void avdtp_check_collision(struct avdtp *session, uint8_t cmd,
+                                       struct avdtp_stream *stream)
+{
+       struct pending_req *req = session->req;
+
+       if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT))
+               return;
+
+       if (cmd == AVDTP_ABORT)
+               cmd = req->signal_id;
+
+       switch (cmd) {
+       case AVDTP_OPEN:
+       case AVDTP_CLOSE:
+               check_seid_collision(req, stream->rseid);
+               break;
+       case AVDTP_START:
+               check_start_collision(req, stream->rseid);
+               break;
+       case AVDTP_SUSPEND:
+               check_suspend_collision(req, stream->rseid);
+               break;
+       }
+}
+
+static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
+                               struct seid_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err;
+
+       if (size < sizeof(struct seid_req)) {
+               error("Too short abort request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (sep->state != AVDTP_STATE_CONFIGURED) {
+               err = AVDTP_BAD_STATE;
+               goto failed;
+       }
+
+       stream = sep->stream;
+
+       if (sep->ind && sep->ind->open) {
+               if (!sep->ind->open(session, sep, stream, &err,
+                                       sep->user_data))
+                       goto failed;
+       }
+
+       avdtp_check_collision(session, AVDTP_OPEN, stream);
+
+       if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_OPEN, NULL, 0))
+               return FALSE;
+
+       stream->open_acp = TRUE;
+       session->pending_open = stream;
+       stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+                                               stream_open_timeout,
+                                               stream);
+
+       return TRUE;
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_OPEN, &err, sizeof(err));
+}
+
+static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
+                               struct start_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       struct stream_rej rej;
+       struct seid *seid;
+       uint8_t err, failed_seid;
+       int seid_count, i;
+
+       if (size < sizeof(struct start_req)) {
+               error("Too short start request");
+               return FALSE;
+       }
+
+       seid_count = 1 + size - sizeof(struct start_req);
+
+       seid = &req->first_seid;
+
+       for (i = 0; i < seid_count; i++, seid++) {
+               failed_seid = seid->seid;
+
+               sep = find_local_sep_by_seid(session->server,
+                                       req->first_seid.seid);
+               if (!sep || !sep->stream) {
+                       err = AVDTP_BAD_ACP_SEID;
+                       goto failed;
+               }
+
+               stream = sep->stream;
+
+               /* Also reject start cmd if state is not open */
+               if (sep->state != AVDTP_STATE_OPEN) {
+                       err = AVDTP_BAD_STATE;
+                       goto failed;
+               }
+               stream->starting = TRUE;
+
+               if (sep->ind && sep->ind->start) {
+                       if (!sep->ind->start(session, sep, stream, &err,
+                                               sep->user_data))
+                               goto failed;
+               }
+
+               avdtp_check_collision(session, AVDTP_START, stream);
+
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_START, NULL, 0);
+
+failed:
+       DBG("Rejecting (%d)", err);
+       memset(&rej, 0, sizeof(rej));
+       rej.acp_seid = failed_seid;
+       rej.error = err;
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_START, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction,
+                               struct seid_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err;
+
+       if (size < sizeof(struct seid_req)) {
+               error("Too short close request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep || !sep->stream) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (sep->state != AVDTP_STATE_OPEN &&
+                       sep->state != AVDTP_STATE_STREAMING) {
+               err = AVDTP_BAD_STATE;
+               goto failed;
+       }
+
+       stream = sep->stream;
+
+       if (sep->ind && sep->ind->close) {
+               if (!sep->ind->close(session, sep, stream, &err,
+                                       sep->user_data))
+                       goto failed;
+       }
+
+       avdtp_check_collision(session, AVDTP_CLOSE, stream);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+       if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_CLOSE, NULL, 0))
+               return FALSE;
+
+       stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+                                       stream_close_timeout,
+                                       stream);
+
+       return TRUE;
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_CLOSE, &err, sizeof(err));
+}
+
+static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
+                               struct suspend_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       struct stream_rej rej;
+       struct seid *seid;
+       uint8_t err, failed_seid;
+       int seid_count, i;
+
+       if (size < sizeof(struct suspend_req)) {
+               error("Too short suspend request");
+               return FALSE;
+       }
+
+       seid_count = 1 + size - sizeof(struct suspend_req);
+
+       seid = &req->first_seid;
+
+       for (i = 0; i < seid_count; i++, seid++) {
+               failed_seid = seid->seid;
+
+               sep = find_local_sep_by_seid(session->server,
+                                       req->first_seid.seid);
+               if (!sep || !sep->stream) {
+                       err = AVDTP_BAD_ACP_SEID;
+                       goto failed;
+               }
+
+               stream = sep->stream;
+
+               if (sep->state != AVDTP_STATE_STREAMING) {
+                       err = AVDTP_BAD_STATE;
+                       goto failed;
+               }
+
+               if (sep->ind && sep->ind->suspend) {
+                       if (!sep->ind->suspend(session, sep, stream, &err,
+                                               sep->user_data))
+                               goto failed;
+               }
+
+               avdtp_check_collision(session, AVDTP_SUSPEND, stream);
+
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_SUSPEND, NULL, 0);
+
+failed:
+       memset(&rej, 0, sizeof(rej));
+       rej.acp_seid = failed_seid;
+       rej.error = err;
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_SUSPEND, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction,
+                               struct seid_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       uint8_t err;
+       gboolean ret;
+
+       if (size < sizeof(struct seid_req)) {
+               error("Too short abort request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep || !sep->stream)
+               return TRUE;
+
+       if (sep->ind && sep->ind->abort)
+               sep->ind->abort(session, sep, sep->stream, &err,
+                                                       sep->user_data);
+
+       avdtp_check_collision(session, AVDTP_ABORT, sep->stream);
+
+       ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_ABORT, NULL, 0);
+       if (ret)
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+       return ret;
+}
+
+static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, int size)
+{
+       return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL);
+}
+
+static gboolean avdtp_delayreport_cmd(struct avdtp *session,
+                                       uint8_t transaction,
+                                       struct delay_req *req,
+                                       unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err;
+
+       if (size < sizeof(struct delay_req)) {
+               error("Too short delay report request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(session->server, req->acp_seid);
+       if (!sep || !sep->stream) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       stream = sep->stream;
+
+       if (sep->state != AVDTP_STATE_CONFIGURED &&
+                                       sep->state != AVDTP_STATE_STREAMING) {
+               err = AVDTP_BAD_STATE;
+               goto failed;
+       }
+
+       stream->delay = ntohs(req->delay);
+
+       if (sep->ind && sep->ind->delayreport) {
+               if (!sep->ind->delayreport(session, sep, stream->rseid,
+                                               stream->delay, &err,
+                                               sep->user_data))
+                       goto failed;
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_DELAY_REPORT, NULL, 0);
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_DELAY_REPORT, &err, sizeof(err));
+}
+
+static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction,
+                               uint8_t signal_id, void *buf, int size)
+{
+       switch (signal_id) {
+       case AVDTP_DISCOVER:
+               DBG("Received DISCOVER_CMD");
+               return avdtp_discover_cmd(session, transaction, buf, size);
+       case AVDTP_GET_CAPABILITIES:
+               DBG("Received  GET_CAPABILITIES_CMD");
+               return avdtp_getcap_cmd(session, transaction, buf, size,
+                                                                       FALSE);
+       case AVDTP_GET_ALL_CAPABILITIES:
+               DBG("Received  GET_ALL_CAPABILITIES_CMD");
+               return avdtp_getcap_cmd(session, transaction, buf, size, TRUE);
+       case AVDTP_SET_CONFIGURATION:
+               DBG("Received SET_CONFIGURATION_CMD");
+               return avdtp_setconf_cmd(session, transaction, buf, size);
+       case AVDTP_GET_CONFIGURATION:
+               DBG("Received GET_CONFIGURATION_CMD");
+               return avdtp_getconf_cmd(session, transaction, buf, size);
+       case AVDTP_RECONFIGURE:
+               DBG("Received RECONFIGURE_CMD");
+               return avdtp_reconf_cmd(session, transaction, buf, size);
+       case AVDTP_OPEN:
+               DBG("Received OPEN_CMD");
+               return avdtp_open_cmd(session, transaction, buf, size);
+       case AVDTP_START:
+               DBG("Received START_CMD");
+               return avdtp_start_cmd(session, transaction, buf, size);
+       case AVDTP_CLOSE:
+               DBG("Received CLOSE_CMD");
+               return avdtp_close_cmd(session, transaction, buf, size);
+       case AVDTP_SUSPEND:
+               DBG("Received SUSPEND_CMD");
+               return avdtp_suspend_cmd(session, transaction, buf, size);
+       case AVDTP_ABORT:
+               DBG("Received ABORT_CMD");
+               return avdtp_abort_cmd(session, transaction, buf, size);
+       case AVDTP_SECURITY_CONTROL:
+               DBG("Received SECURITY_CONTROL_CMD");
+               return avdtp_secctl_cmd(session, transaction, buf, size);
+       case AVDTP_DELAY_REPORT:
+               DBG("Received DELAY_REPORT_CMD");
+               return avdtp_delayreport_cmd(session, transaction, buf, size);
+       default:
+               DBG("Received unknown request id %u", signal_id);
+               return avdtp_unknown_cmd(session, transaction, signal_id);
+       }
+}
+
+enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS };
+
+static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
+                                                       void *buf, size_t size)
+{
+       struct avdtp_common_header *header = buf;
+       struct avdtp_single_header *single = (void *) session->buf;
+       struct avdtp_start_header *start = (void *) session->buf;
+       void *payload;
+       gsize payload_size;
+
+       switch (header->packet_type) {
+       case AVDTP_PKT_TYPE_SINGLE:
+               if (size < sizeof(*single)) {
+                       error("Received too small single packet (%zu bytes)", size);
+                       return PARSE_ERROR;
+               }
+               if (session->in.active) {
+                       error("SINGLE: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+
+               payload = session->buf + sizeof(*single);
+               payload_size = size - sizeof(*single);
+
+               session->in.active = TRUE;
+               session->in.data_size = 0;
+               session->in.no_of_packets = 1;
+               session->in.transaction = header->transaction;
+               session->in.message_type = header->message_type;
+               session->in.signal_id = single->signal_id;
+
+               break;
+       case AVDTP_PKT_TYPE_START:
+               if (size < sizeof(*start)) {
+                       error("Received too small start packet (%zu bytes)", size);
+                       return PARSE_ERROR;
+               }
+               if (session->in.active) {
+                       error("START: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+
+               session->in.active = TRUE;
+               session->in.data_size = 0;
+               session->in.transaction = header->transaction;
+               session->in.message_type = header->message_type;
+               session->in.no_of_packets = start->no_of_packets;
+               session->in.signal_id = start->signal_id;
+
+               payload = session->buf + sizeof(*start);
+               payload_size = size - sizeof(*start);
+
+               break;
+       case AVDTP_PKT_TYPE_CONTINUE:
+               if (size < sizeof(struct avdtp_continue_header)) {
+                       error("Received too small continue packet (%zu bytes)",
+                                                                       size);
+                       return PARSE_ERROR;
+               }
+               if (!session->in.active) {
+                       error("CONTINUE: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+               if (session->in.transaction != header->transaction) {
+                       error("Continue transaction id doesn't match");
+                       return PARSE_ERROR;
+               }
+               if (session->in.no_of_packets <= 1) {
+                       error("Too few continue packets");
+                       return PARSE_ERROR;
+               }
+
+               payload = session->buf + sizeof(struct avdtp_continue_header);
+               payload_size = size - sizeof(struct avdtp_continue_header);
+
+               break;
+       case AVDTP_PKT_TYPE_END:
+               if (size < sizeof(struct avdtp_continue_header)) {
+                       error("Received too small end packet (%zu bytes)", size);
+                       return PARSE_ERROR;
+               }
+               if (!session->in.active) {
+                       error("END: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+               if (session->in.transaction != header->transaction) {
+                       error("End transaction id doesn't match");
+                       return PARSE_ERROR;
+               }
+               if (session->in.no_of_packets > 1) {
+                       error("Got an end packet too early");
+                       return PARSE_ERROR;
+               }
+
+               payload = session->buf + sizeof(struct avdtp_continue_header);
+               payload_size = size - sizeof(struct avdtp_continue_header);
+
+               break;
+       default:
+               error("Invalid AVDTP packet type 0x%02X", header->packet_type);
+               return PARSE_ERROR;
+       }
+
+       if (session->in.data_size + payload_size >
+                                       sizeof(session->in.buf)) {
+               error("Not enough incoming buffer space!");
+               return PARSE_ERROR;
+       }
+
+       memcpy(session->in.buf + session->in.data_size, payload, payload_size);
+       session->in.data_size += payload_size;
+
+       if (session->in.no_of_packets > 1) {
+               session->in.no_of_packets--;
+               DBG("Received AVDTP fragment. %d to go",
+                                               session->in.no_of_packets);
+               return PARSE_FRAGMENT;
+       }
+
+       session->in.active = FALSE;
+
+       return PARSE_SUCCESS;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+                               gpointer data)
+{
+       struct avdtp *session = data;
+       struct avdtp_common_header *header;
+       ssize_t size;
+       int fd;
+
+       DBG("");
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       header = (void *) session->buf;
+
+       if (cond & (G_IO_HUP | G_IO_ERR))
+               goto failed;
+
+       fd = g_io_channel_unix_get_fd(chan);
+       size = read(fd, session->buf, session->imtu);
+       if (size < 0) {
+               error("IO Channel read error");
+               goto failed;
+       }
+
+       if ((size_t) size < sizeof(struct avdtp_common_header)) {
+               error("Received too small packet (%zu bytes)", size);
+               goto failed;
+       }
+
+       switch (avdtp_parse_data(session, session->buf, size)) {
+       case PARSE_ERROR:
+               goto failed;
+       case PARSE_FRAGMENT:
+               return TRUE;
+       case PARSE_SUCCESS:
+               break;
+       }
+
+       if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+               if (!avdtp_parse_cmd(session, session->in.transaction,
+                                       session->in.signal_id,
+                                       session->in.buf,
+                                       session->in.data_size)) {
+                       error("Unable to handle command. Disconnecting");
+                       goto failed;
+               }
+
+               if (session->ref == 1 && !session->streams && !session->req)
+                       set_disconnect_timer(session);
+
+               if (session->streams && session->dc_timer)
+                       remove_disconnect_timer(session);
+
+               if (session->req && session->req->collided) {
+                       DBG("Collision detected");
+                       goto next;
+               }
+
+               return TRUE;
+       }
+
+       if (session->req == NULL) {
+               error("No pending request, ignoring message");
+               return TRUE;
+       }
+
+       if (header->transaction != session->req->transaction) {
+               error("Transaction label doesn't match");
+               return TRUE;
+       }
+
+       if (session->in.signal_id != session->req->signal_id) {
+               error("Response signal doesn't match");
+               return TRUE;
+       }
+
+       g_source_remove(session->req->timeout);
+       session->req->timeout = 0;
+
+       switch (header->message_type) {
+       case AVDTP_MSG_TYPE_ACCEPT:
+               if (!avdtp_parse_resp(session, session->req->stream,
+                                               session->in.transaction,
+                                               session->in.signal_id,
+                                               session->in.buf,
+                                               session->in.data_size)) {
+                       error("Unable to parse accept response");
+                       goto failed;
+               }
+               break;
+       case AVDTP_MSG_TYPE_REJECT:
+               if (!avdtp_parse_rej(session, session->req->stream,
+                                               session->in.transaction,
+                                               session->in.signal_id,
+                                               session->in.buf,
+                                               session->in.data_size)) {
+                       error("Unable to parse reject response");
+                       goto failed;
+               }
+               break;
+       case AVDTP_MSG_TYPE_GEN_REJECT:
+               error("Received a General Reject message");
+               break;
+       default:
+               error("Unknown message type 0x%02X", header->message_type);
+               break;
+       }
+
+next:
+       pending_req_free(session->req);
+       session->req = NULL;
+
+       process_queue(session);
+
+       return TRUE;
+
+failed:
+       connection_lost(session, EIO);
+
+       return FALSE;
+}
+
+static struct avdtp *find_session(GSList *list, const bdaddr_t *dst)
+{
+       for (; list != NULL; list = g_slist_next(list)) {
+               struct avdtp *s = list->data;
+
+               if (bacmp(dst, &s->dst))
+                       continue;
+
+               return s;
+       }
+
+       return NULL;
+}
+
+static uint16_t get_version(struct avdtp *session)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       const sdp_record_t *rec;
+       sdp_list_t *protos;
+       sdp_data_t *proto_desc;
+       char addr[18];
+       uint16_t ver = 0x0100;
+
+       adapter = manager_find_adapter(&session->server->src);
+       if (!adapter)
+               return ver;
+
+       ba2str(&session->dst, addr);
+       device = adapter_find_device(adapter, addr);
+       if (!device)
+               return ver;
+
+       rec = btd_device_get_record(device, A2DP_SINK_UUID);
+       if (!rec)
+               rec = btd_device_get_record(device, A2DP_SOURCE_UUID);
+
+       if (!rec)
+               return ver;
+
+       if (sdp_get_access_protos(rec, &protos) < 0)
+               return ver;
+
+       proto_desc = sdp_get_proto_desc(protos, AVDTP_UUID);
+       if (proto_desc && proto_desc->dtd == SDP_UINT16)
+               ver = proto_desc->val.uint16;
+
+       sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+       sdp_list_free(protos, NULL);
+
+       return ver;
+}
+
+static struct avdtp *avdtp_get_internal(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;
+
+       session->version = get_version(session);
+
+       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);
+       DBG("AVDTP: connected %s channel to %s",
+                       session->pending_open ? "transport" : "signaling",
+                       address);
+
+       if (session->state == AVDTP_SESSION_STATE_CONNECTING) {
+               DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);
+
+               session->buf = g_malloc0(MAX(session->imtu, session->omtu));
+               avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);
+
+               if (session->io_id)
+                       g_source_remove(session->io_id);
+
+               /* This watch should be low priority since otherwise the
+                * connect callback might be dispatched before the session
+                * callback if the kernel wakes us up at the same time for
+                * them. This could happen if a headset is very quick in
+                * sending the Start command after connecting the stream
+                * transport channel.
+                */
+               session->io_id = g_io_add_watch_full(chan,
+                                               G_PRIORITY_LOW,
+                                               G_IO_IN | G_IO_ERR | G_IO_HUP
+                                               | G_IO_NVAL,
+                                               (GIOFunc) session_cb, session,
+                                               NULL);
+
+               if (session->stream_setup) {
+                       set_disconnect_timer(session);
+                       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);
+}
+
+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;
+       }
+
+       DBG("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) {
+               DBG("connect already in progress (XCASE connect:connect)");
+               goto drop;
+       }
+
+       if (session->pending_open && session->pending_open->open_acp) {
+               if (!bt_io_accept(chan, avdtp_connect_cb, session, NULL, NULL))
+                       goto drop;
+               return;
+       }
+
+       if (session->io) {
+               error("Refusing unexpected connect from %s", address);
+               goto drop;
+       }
+
+       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_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
+               avdtp_unref(session);
+               goto drop;
+       }
+
+       dev->auto_connect = auto_connect;
+
+       return;
+
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static GIOChannel *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_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("%s", err->message);
+               g_error_free(err);
+               return NULL;
+       }
+
+       return io;
+}
+
+static void queue_request(struct avdtp *session, struct pending_req *req,
+                       gboolean priority)
+{
+       if (priority)
+               session->prio_queue = g_slist_append(session->prio_queue, req);
+       else
+               session->req_queue = g_slist_append(session->req_queue, req);
+}
+
+static uint8_t req_get_seid(struct pending_req *req)
+{
+       if (req->signal_id == AVDTP_DISCOVER)
+               return 0;
+
+       return ((struct seid_req *) (req->data))->acp_seid;
+}
+
+static int cancel_request(struct avdtp *session, int err)
+{
+       struct pending_req *req;
+       struct seid_req sreq;
+       struct avdtp_local_sep *lsep;
+       struct avdtp_stream *stream;
+       uint8_t seid;
+       struct avdtp_error averr;
+
+       req = session->req;
+       session->req = NULL;
+
+       avdtp_error_init(&averr, AVDTP_ERRNO, err);
+
+       seid = req_get_seid(req);
+       if (seid)
+               stream = find_stream_by_rseid(session, seid);
+       else
+               stream = NULL;
+
+       if (stream) {
+               stream->abort_int = TRUE;
+               lsep = stream->lsep;
+       } else
+               lsep = NULL;
+
+       switch (req->signal_id) {
+       case AVDTP_RECONFIGURE:
+               error("Reconfigure: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+                       lsep->cfm->reconfigure(session, lsep, stream, &averr,
+                                               lsep->user_data);
+               break;
+       case AVDTP_OPEN:
+               error("Open: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->open)
+                       lsep->cfm->open(session, lsep, stream, &averr,
+                                       lsep->user_data);
+               break;
+       case AVDTP_START:
+               error("Start: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->start) {
+                       lsep->cfm->start(session, lsep, stream, &averr,
+                                               lsep->user_data);
+                       if (stream)
+                               stream->starting = FALSE;
+               }
+               break;
+       case AVDTP_SUSPEND:
+               error("Suspend: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->suspend)
+                       lsep->cfm->suspend(session, lsep, stream, &averr,
+                                               lsep->user_data);
+               break;
+       case AVDTP_CLOSE:
+               error("Close: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->close) {
+                       lsep->cfm->close(session, lsep, stream, &averr,
+                                               lsep->user_data);
+                       if (stream)
+                               stream->close_int = FALSE;
+               }
+               break;
+       case AVDTP_SET_CONFIGURATION:
+               error("SetConfiguration: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+                       lsep->cfm->set_configuration(session, lsep, stream,
+                                                       &averr, lsep->user_data);
+               goto failed;
+       case AVDTP_DISCOVER:
+               error("Discover: %s (%d)", strerror(err), err);
+               goto failed;
+       case AVDTP_GET_CAPABILITIES:
+               error("GetCapabilities: %s (%d)", strerror(err), err);
+               goto failed;
+       case AVDTP_ABORT:
+               error("Abort: %s (%d)", strerror(err), err);
+               goto failed;
+       }
+
+       if (!stream)
+               goto failed;
+
+       memset(&sreq, 0, sizeof(sreq));
+       sreq.acp_seid = seid;
+
+       err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq,
+                               sizeof(sreq));
+       if (err < 0) {
+               error("Unable to send abort request");
+               goto failed;
+       }
+
+       goto done;
+
+failed:
+       connection_lost(session, err);
+done:
+       pending_req_free(req);
+       return err;
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+       struct avdtp *session = user_data;
+
+       cancel_request(session, ETIMEDOUT);
+
+       return FALSE;
+}
+
+static int send_req(struct avdtp *session, gboolean priority,
+                       struct pending_req *req)
+{
+       static int transaction = 0;
+       int err;
+
+       if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {
+               session->io = l2cap_connect(session);
+               if (!session->io) {
+                       err = -EIO;
+                       goto failed;
+               }
+               avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
+       }
+
+       if (session->state < AVDTP_SESSION_STATE_CONNECTED ||
+                       session->req != NULL) {
+               queue_request(session, req, priority);
+               return 0;
+       }
+
+       req->transaction = transaction++;
+       transaction %= 16;
+
+       /* FIXME: Should we retry to send if the buffer
+       was not totally sent or in case of EINTR? */
+       if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
+                               req->signal_id, req->data, req->data_size)) {
+               err = -EIO;
+               goto failed;
+       }
+
+       session->req = req;
+
+       req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ?
+                                       ABORT_TIMEOUT : REQ_TIMEOUT,
+                                       request_timeout,
+                                       session);
+       return 0;
+
+failed:
+       g_free(req->data);
+       g_free(req);
+       return err;
+}
+
+static int send_request(struct avdtp *session, gboolean priority,
+                       struct avdtp_stream *stream, uint8_t signal_id,
+                       void *buffer, size_t size)
+{
+       struct pending_req *req;
+
+       if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
+               DBG("Unable to send requests while aborting");
+               return -EINVAL;
+       }
+
+       req = g_new0(struct pending_req, 1);
+       req->signal_id = signal_id;
+       req->data = g_malloc(size);
+       memcpy(req->data, buffer, size);
+       req->data_size = size;
+       req->stream = stream;
+
+       return send_req(session, priority, req);
+}
+
+static gboolean avdtp_discover_resp(struct avdtp *session,
+                                       struct discover_resp *resp, int size)
+{
+       int sep_count, i;
+       uint8_t getcap_cmd;
+       int ret = 0;
+       gboolean getcap_pending = FALSE;
+
+       if (session->version >= 0x0103 && session->server->version >= 0x0103)
+               getcap_cmd = AVDTP_GET_ALL_CAPABILITIES;
+       else
+               getcap_cmd = AVDTP_GET_CAPABILITIES;
+
+       sep_count = size / sizeof(struct seid_info);
+
+       for (i = 0; i < sep_count; i++) {
+               struct avdtp_remote_sep *sep;
+               struct avdtp_stream *stream;
+               struct seid_req req;
+
+               DBG("seid %d type %d media %d in use %d",
+                               resp->seps[i].seid, resp->seps[i].type,
+                               resp->seps[i].media_type, resp->seps[i].inuse);
+
+               stream = find_stream_by_rseid(session, resp->seps[i].seid);
+
+               sep = find_remote_sep(session->seps, resp->seps[i].seid);
+               if (!sep) {
+                       if (resp->seps[i].inuse && !stream)
+                               continue;
+                       sep = g_new0(struct avdtp_remote_sep, 1);
+                       session->seps = g_slist_append(session->seps, sep);
+               }
+
+               sep->stream = stream;
+               sep->seid = resp->seps[i].seid;
+               sep->type = resp->seps[i].type;
+               sep->media_type = resp->seps[i].media_type;
+
+               memset(&req, 0, sizeof(req));
+               req.acp_seid = sep->seid;
+
+               ret = send_request(session, TRUE, NULL, getcap_cmd,
+                                                       &req, sizeof(req));
+               if (ret < 0)
+                       break;
+               getcap_pending = TRUE;
+       }
+
+       if (!getcap_pending)
+               finalize_discovery(session, -ret);
+
+       return TRUE;
+}
+
+static gboolean avdtp_get_capabilities_resp(struct avdtp *session,
+                                               struct getcap_resp *resp,
+                                               unsigned int size)
+{
+       struct avdtp_remote_sep *sep;
+       uint8_t seid;
+
+       /* Check for minimum required packet size includes:
+        *   1. getcap resp header
+        *   2. media transport capability (2 bytes)
+        *   3. media codec capability type + length (2 bytes)
+        *   4. the actual media codec elements
+        * */
+       if (size < (sizeof(struct getcap_resp) + 4 +
+                               sizeof(struct avdtp_media_codec_capability))) {
+               error("Too short getcap resp packet");
+               return FALSE;
+       }
+
+       seid = ((struct seid_req *) session->req->data)->acp_seid;
+
+       sep = find_remote_sep(session->seps, seid);
+
+       DBG("seid %d type %d media %d", sep->seid,
+                                       sep->type, sep->media_type);
+
+       if (sep->caps) {
+               g_slist_free_full(sep->caps, g_free);
+               sep->caps = NULL;
+               sep->codec = NULL;
+               sep->delay_reporting = FALSE;
+       }
+
+       sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp),
+                                       &sep->codec, &sep->delay_reporting);
+
+       return TRUE;
+}
+
+static gboolean avdtp_set_configuration_resp(struct avdtp *session,
+                                               struct avdtp_stream *stream,
+                                               struct avdtp_single_header *resp,
+                                               int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       if (sep->cfm && sep->cfm->set_configuration)
+               sep->cfm->set_configuration(session, sep, stream, NULL,
+                                               sep->user_data);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
+       return TRUE;
+}
+
+static gboolean avdtp_reconfigure_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_single_header *resp, int size)
+{
+       return TRUE;
+}
+
+static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream,
+                               struct seid_rej *resp, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       stream->io = l2cap_connect(session);
+       if (!stream->io) {
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+               return FALSE;
+       }
+
+       session->pending_open = stream;
+
+       return TRUE;
+}
+
+static gboolean avdtp_start_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       struct seid_rej *resp, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       if (sep->cfm && sep->cfm->start)
+               sep->cfm->start(session, sep, stream, NULL, sep->user_data);
+
+       /* We might be in STREAMING already if both sides send START_CMD at the
+        * same time and the one in SNK role doesn't reject it as it should */
+       if (sep->state != AVDTP_STATE_STREAMING)
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+
+       return TRUE;
+}
+
+static gboolean avdtp_close_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       struct seid_rej *resp, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+       close_stream(stream);
+
+       return TRUE;
+}
+
+static gboolean avdtp_suspend_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       void *data, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+       if (sep->cfm && sep->cfm->suspend)
+               sep->cfm->suspend(session, sep, stream, NULL, sep->user_data);
+
+       return TRUE;
+}
+
+static gboolean avdtp_abort_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       struct seid_rej *resp, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+       if (sep->cfm && sep->cfm->abort)
+               sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+
+       return TRUE;
+}
+
+static gboolean avdtp_delay_report_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       void *data, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       if (sep->cfm && sep->cfm->delay_report)
+               sep->cfm->delay_report(session, sep, stream, NULL, sep->user_data);
+
+       return TRUE;
+}
+
+static gboolean avdtp_parse_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       uint8_t transaction, uint8_t signal_id,
+                                       void *buf, int size)
+{
+       struct pending_req *next;
+       const char *get_all = "";
+
+       if (session->prio_queue)
+               next = session->prio_queue->data;
+       else if (session->req_queue)
+               next = session->req_queue->data;
+       else
+               next = NULL;
+
+       switch (signal_id) {
+       case AVDTP_DISCOVER:
+               DBG("DISCOVER request succeeded");
+               return avdtp_discover_resp(session, buf, size);
+       case AVDTP_GET_ALL_CAPABILITIES:
+               get_all = "ALL_";
+       case AVDTP_GET_CAPABILITIES:
+               DBG("GET_%sCAPABILITIES request succeeded", get_all);
+               if (!avdtp_get_capabilities_resp(session, buf, size))
+                       return FALSE;
+               if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES ||
+                               next->signal_id == AVDTP_GET_ALL_CAPABILITIES)))
+                       finalize_discovery(session, 0);
+               return TRUE;
+       }
+
+       /* The remaining commands require an existing stream so bail out
+        * here if the stream got unexpectedly disconnected */
+       if (!stream) {
+               DBG("AVDTP: stream was closed while waiting for reply");
+               return TRUE;
+       }
+
+       switch (signal_id) {
+       case AVDTP_SET_CONFIGURATION:
+               DBG("SET_CONFIGURATION request succeeded");
+               return avdtp_set_configuration_resp(session, stream,
+                                                               buf, size);
+       case AVDTP_RECONFIGURE:
+               DBG("RECONFIGURE request succeeded");
+               return avdtp_reconfigure_resp(session, stream, buf, size);
+       case AVDTP_OPEN:
+               DBG("OPEN request succeeded");
+               return avdtp_open_resp(session, stream, buf, size);
+       case AVDTP_SUSPEND:
+               DBG("SUSPEND request succeeded");
+               return avdtp_suspend_resp(session, stream, buf, size);
+       case AVDTP_START:
+               DBG("START request succeeded");
+               return avdtp_start_resp(session, stream, buf, size);
+       case AVDTP_CLOSE:
+               DBG("CLOSE request succeeded");
+               return avdtp_close_resp(session, stream, buf, size);
+       case AVDTP_ABORT:
+               DBG("ABORT request succeeded");
+               return avdtp_abort_resp(session, stream, buf, size);
+       case AVDTP_DELAY_REPORT:
+               DBG("DELAY_REPORT request succeeded");
+               return avdtp_delay_report_resp(session, stream, buf, size);
+       }
+
+       error("Unknown signal id in accept response: %u", signal_id);
+       return TRUE;
+}
+
+static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size,
+                                       struct avdtp_error *err)
+{
+       if (size < sizeof(struct seid_rej)) {
+               error("Too small packet for seid_rej");
+               return FALSE;
+       }
+
+       avdtp_error_init(err, 0x00, rej->error);
+
+       return TRUE;
+}
+
+static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size,
+                               struct avdtp_error *err)
+{
+       if (size < sizeof(struct conf_rej)) {
+               error("Too small packet for conf_rej");
+               return FALSE;
+       }
+
+       avdtp_error_init(err, rej->category, rej->error);
+
+       return TRUE;
+}
+
+static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size,
+                                       struct avdtp_error *err,
+                                       uint8_t *acp_seid)
+{
+       if (size < sizeof(struct stream_rej)) {
+               error("Too small packet for stream_rej");
+               return FALSE;
+       }
+
+       avdtp_error_init(err, 0x00, rej->error);
+
+       if (acp_seid)
+               *acp_seid = rej->acp_seid;
+
+       return TRUE;
+}
+
+static gboolean avdtp_parse_rej(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       uint8_t transaction, uint8_t signal_id,
+                                       void *buf, int size)
+{
+       struct avdtp_error err;
+       uint8_t acp_seid;
+       struct avdtp_local_sep *sep = stream ? stream->lsep : NULL;
+
+       switch (signal_id) {
+       case AVDTP_DISCOVER:
+               if (!seid_rej_to_err(buf, size, &err))
+                       return FALSE;
+               error("DISCOVER request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               return TRUE;
+       case AVDTP_GET_CAPABILITIES:
+       case AVDTP_GET_ALL_CAPABILITIES:
+               if (!seid_rej_to_err(buf, size, &err))
+                       return FALSE;
+               error("GET_CAPABILITIES request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               return TRUE;
+       case AVDTP_OPEN:
+               if (!seid_rej_to_err(buf, size, &err))
+                       return FALSE;
+               error("OPEN request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->open)
+                       sep->cfm->open(session, sep, stream, &err,
+                                       sep->user_data);
+               return TRUE;
+       case AVDTP_SET_CONFIGURATION:
+               if (!conf_rej_to_err(buf, size, &err))
+                       return FALSE;
+               error("SET_CONFIGURATION request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->set_configuration)
+                       sep->cfm->set_configuration(session, sep, stream,
+                                                       &err, sep->user_data);
+               return TRUE;
+       case AVDTP_RECONFIGURE:
+               if (!conf_rej_to_err(buf, size, &err))
+                       return FALSE;
+               error("RECONFIGURE request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->reconfigure)
+                       sep->cfm->reconfigure(session, sep, stream, &err,
+                                               sep->user_data);
+               return TRUE;
+       case AVDTP_START:
+               if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+                       return FALSE;
+               error("START request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->start) {
+                       sep->cfm->start(session, sep, stream, &err,
+                                       sep->user_data);
+                       stream->starting = FALSE;
+               }
+               return TRUE;
+       case AVDTP_SUSPEND:
+               if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+                       return FALSE;
+               error("SUSPEND request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->suspend)
+                       sep->cfm->suspend(session, sep, stream, &err,
+                                               sep->user_data);
+               return TRUE;
+       case AVDTP_CLOSE:
+               if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+                       return FALSE;
+               error("CLOSE request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->close) {
+                       sep->cfm->close(session, sep, stream, &err,
+                                       sep->user_data);
+                       stream->close_int = FALSE;
+               }
+               return TRUE;
+       case AVDTP_ABORT:
+               if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+                       return FALSE;
+               error("ABORT request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->abort)
+                       sep->cfm->abort(session, sep, stream, &err,
+                                       sep->user_data);
+               return FALSE;
+       case AVDTP_DELAY_REPORT:
+               if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+                       return FALSE;
+               error("DELAY_REPORT request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->delay_report)
+                       sep->cfm->delay_report(session, sep, stream, &err,
+                                                       sep->user_data);
+               return TRUE;
+       default:
+               error("Unknown reject response signal id: %u", signal_id);
+               return TRUE;
+       }
+}
+
+gboolean avdtp_is_connected(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       struct avdtp_server *server;
+       struct avdtp *session;
+
+       server = find_server(servers, src);
+       if (!server)
+               return FALSE;
+
+       session = find_session(server->sessions, dst);
+       if (!session)
+               return FALSE;
+
+       if (session->state != AVDTP_SESSION_STATE_DISCONNECTED)
+               return TRUE;
+
+       return FALSE;
+}
+
+struct avdtp_service_capability *avdtp_stream_get_codec(
+                                               struct avdtp_stream *stream)
+{
+       GSList *l;
+
+       for (l = stream->caps; l; l = l->next) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (cap->category == AVDTP_MEDIA_CODEC)
+                       return cap;
+       }
+
+       return NULL;
+}
+
+gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+                               struct avdtp_service_capability *cap)
+{
+       GSList *l;
+       struct avdtp_service_capability *stream_cap;
+
+       for (l = stream->caps; l; l = g_slist_next(l)) {
+               stream_cap = l->data;
+
+               if (stream_cap->category != cap->category ||
+                       stream_cap->length != cap->length)
+                       continue;
+
+               if (memcmp(stream_cap->data, cap->data, cap->length) == 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+                                       GSList *caps)
+{
+       for (; caps; caps = g_slist_next(caps)) {
+               struct avdtp_service_capability *cap = caps->data;
+
+               if (!avdtp_stream_has_capability(stream, cap))
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+                                               struct avdtp_stream *stream)
+{
+       return avdtp_get_remote_sep(stream->session, stream->rseid);
+}
+
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+                                       uint16_t *imtu, uint16_t *omtu,
+                                       GSList **caps)
+{
+       if (stream->io == NULL)
+               return FALSE;
+
+       if (sock)
+               *sock = g_io_channel_unix_get_fd(stream->io);
+
+       if (omtu)
+               *omtu = stream->omtu;
+
+       if (imtu)
+               *imtu = stream->imtu;
+
+       if (caps)
+               *caps = stream->caps;
+
+       return TRUE;
+}
+
+static int process_queue(struct avdtp *session)
+{
+       GSList **queue, *l;
+       struct pending_req *req;
+
+       if (session->req)
+               return 0;
+
+       if (session->prio_queue)
+               queue = &session->prio_queue;
+       else
+               queue = &session->req_queue;
+
+       if (!*queue)
+               return 0;
+
+       l = *queue;
+       req = l->data;
+
+       *queue = g_slist_remove(*queue, req);
+
+       return send_req(session, FALSE, req);
+}
+
+struct avdtp_remote_sep *avdtp_get_remote_sep(struct avdtp *session,
+                                               uint8_t seid)
+{
+       GSList *l;
+
+       for (l = session->seps; l; l = l->next) {
+               struct avdtp_remote_sep *sep = l->data;
+
+               if (sep->seid == seid)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep)
+{
+       return sep->seid;
+}
+
+uint8_t avdtp_get_type(struct avdtp_remote_sep *sep)
+{
+       return sep->type;
+}
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
+{
+       return sep->codec;
+}
+
+gboolean avdtp_get_delay_reporting(struct avdtp_remote_sep *sep)
+{
+       return sep->delay_reporting;
+}
+
+struct avdtp_stream *avdtp_get_stream(struct avdtp_remote_sep *sep)
+{
+       return sep->stream;
+}
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+                                                       void *data, int length)
+{
+       struct avdtp_service_capability *cap;
+
+       if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING)
+               return NULL;
+
+       cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
+       cap->category = category;
+       cap->length = length;
+       memcpy(cap->data, data, length);
+
+       return cap;
+}
+
+static gboolean process_discover(gpointer data)
+{
+       struct avdtp *session = data;
+
+       finalize_discovery(session, 0);
+
+       return FALSE;
+}
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+                       void *user_data)
+{
+       int err;
+
+       if (session->discov_cb)
+               return -EBUSY;
+
+       if (session->seps) {
+               session->discov_cb = cb;
+               session->user_data = user_data;
+               g_idle_add(process_discover, session);
+               return 0;
+       }
+
+       err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);
+       if (err == 0) {
+               session->discov_cb = cb;
+               session->user_data = user_data;
+       }
+
+       return err;
+}
+
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+                               struct avdtp_stream *stream,
+                               unsigned int id)
+{
+       GSList *l;
+       struct stream_callback *cb;
+
+       if (!stream)
+               return FALSE;
+
+       for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) {
+               struct stream_callback *tmp = l->data;
+               if (tmp && tmp->id == id) {
+                       cb = tmp;
+                       break;
+               }
+       }
+
+       if (!cb)
+               return FALSE;
+
+       stream->callbacks = g_slist_remove(stream->callbacks, cb);
+       g_free(cb);
+
+       return TRUE;
+}
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       avdtp_stream_state_cb cb, void *data)
+{
+       struct stream_callback *stream_cb;
+       static unsigned int id = 0;
+
+       stream_cb = g_new(struct stream_callback, 1);
+       stream_cb->cb = cb;
+       stream_cb->user_data = data;
+       stream_cb->id = ++id;
+
+       stream->callbacks = g_slist_append(stream->callbacks, stream_cb);
+
+       return stream_cb->id;
+}
+
+int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)
+{
+       struct seid_req req;
+
+       if (session->state < AVDTP_SESSION_STATE_CONNECTED)
+               return -EINVAL;
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+
+       return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION,
+                                                       &req, sizeof(req));
+}
+
+static void copy_capabilities(gpointer data, gpointer user_data)
+{
+       struct avdtp_service_capability *src_cap = data;
+       struct avdtp_service_capability *dst_cap;
+       GSList **l = user_data;
+
+       dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data,
+                                       src_cap->length);
+
+       *l = g_slist_append(*l, dst_cap);
+}
+
+int avdtp_set_configuration(struct avdtp *session,
+                               struct avdtp_remote_sep *rsep,
+                               struct avdtp_local_sep *lsep,
+                               GSList *caps,
+                               struct avdtp_stream **stream)
+{
+       struct setconf_req *req;
+       struct avdtp_stream *new_stream;
+       unsigned char *ptr;
+       int err, caps_len;
+       struct avdtp_service_capability *cap;
+       GSList *l;
+
+       if (session->state != AVDTP_SESSION_STATE_CONNECTED)
+               return -ENOTCONN;
+
+       if (!(lsep && rsep))
+               return -EINVAL;
+
+       DBG("%p: int_seid=%u, acp_seid=%u", session,
+                       lsep->info.seid, rsep->seid);
+
+       new_stream = g_new0(struct avdtp_stream, 1);
+       new_stream->session = session;
+       new_stream->lsep = lsep;
+       new_stream->rseid = rsep->seid;
+
+       if (rsep->delay_reporting && lsep->delay_reporting) {
+               struct avdtp_service_capability *delay_reporting;
+
+               delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+                                                               NULL, 0);
+               caps = g_slist_append(caps, delay_reporting);
+               new_stream->delay_reporting = TRUE;
+       }
+
+       g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
+
+       /* Calculate total size of request */
+       for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
+               cap = l->data;
+               caps_len += cap->length + 2;
+       }
+
+       req = g_malloc0(sizeof(struct setconf_req) + caps_len);
+
+       req->int_seid = lsep->info.seid;
+       req->acp_seid = rsep->seid;
+
+       /* Copy the capabilities into the request */
+       for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) {
+               cap = l->data;
+               memcpy(ptr, cap, cap->length + 2);
+               ptr += cap->length + 2;
+       }
+
+       err = send_request(session, FALSE, new_stream,
+                               AVDTP_SET_CONFIGURATION, req,
+                               sizeof(struct setconf_req) + caps_len);
+       if (err < 0)
+               stream_free(new_stream);
+       else {
+               lsep->info.inuse = 1;
+               lsep->stream = new_stream;
+               rsep->stream = new_stream;
+               session->streams = g_slist_append(session->streams, new_stream);
+               if (stream)
+                       *stream = new_stream;
+       }
+
+       g_free(req);
+
+       return err;
+}
+
+int avdtp_reconfigure(struct avdtp *session, GSList *caps,
+                       struct avdtp_stream *stream)
+{
+       struct reconf_req *req;
+       unsigned char *ptr;
+       int caps_len, err;
+       GSList *l;
+       struct avdtp_service_capability *cap;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state != AVDTP_STATE_OPEN)
+               return -EINVAL;
+
+       /* Calculate total size of request */
+       for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
+               cap = l->data;
+               caps_len += cap->length + 2;
+       }
+
+       req = g_malloc0(sizeof(struct reconf_req) + caps_len);
+
+       req->acp_seid = stream->rseid;
+
+       /* Copy the capabilities into the request */
+       for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) {
+               cap = l->data;
+               memcpy(ptr, cap, cap->length + 2);
+               ptr += cap->length + 2;
+       }
+
+       err = send_request(session, FALSE, stream, AVDTP_RECONFIGURE, req,
+                                               sizeof(*req) + caps_len);
+       g_free(req);
+
+       return err;
+}
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream)
+{
+       struct seid_req req;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state > AVDTP_STATE_CONFIGURED)
+               return -EINVAL;
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+
+       return send_request(session, FALSE, stream, AVDTP_OPEN,
+                                                       &req, sizeof(req));
+}
+
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
+{
+       struct start_req req;
+       int ret;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state != AVDTP_STATE_OPEN)
+               return -EINVAL;
+
+       /* Recommendation 12:
+        *  If the RD has configured and opened a stream it is also responsible
+        *  to start the streaming via GAVDP_START.
+        */
+       if (stream->open_acp) {
+               stream->starting = TRUE;
+               return 0;
+       }
+
+       if (stream->close_int == TRUE) {
+               error("avdtp_start: rejecting start since close is initiated");
+               return -EINVAL;
+       }
+
+       if (stream->starting == TRUE) {
+               DBG("stream already started");
+               return -EINPROGRESS;
+       }
+
+       memset(&req, 0, sizeof(req));
+       req.first_seid.seid = stream->rseid;
+
+       ret = send_request(session, FALSE, stream, AVDTP_START,
+                                                       &req, sizeof(req));
+       if (ret == 0)
+               stream->starting = TRUE;
+
+       return ret;
+}
+
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+               gboolean immediate)
+{
+       struct seid_req req;
+       int ret;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state < AVDTP_STATE_OPEN)
+               return -EINVAL;
+
+       if (stream->close_int == TRUE) {
+               error("avdtp_close: rejecting since close is already initiated");
+               return -EINVAL;
+       }
+
+       if (immediate && session->req && stream == session->req->stream)
+               return avdtp_abort(session, stream);
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+
+       ret = send_request(session, FALSE, stream, AVDTP_CLOSE,
+                                                       &req, sizeof(req));
+       if (ret == 0)
+               stream->close_int = TRUE;
+
+       return ret;
+}
+
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream)
+{
+       struct seid_req req;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int)
+               return -EINVAL;
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+
+       return send_request(session, FALSE, stream, AVDTP_SUSPEND,
+                                                       &req, sizeof(req));
+}
+
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
+{
+       struct seid_req req;
+       int ret;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state == AVDTP_STATE_ABORTING)
+               return -EINVAL;
+
+       if (session->req && stream == session->req->stream)
+               return cancel_request(session, ECANCELED);
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+
+       ret = send_request(session, TRUE, stream, AVDTP_ABORT,
+                                                       &req, sizeof(req));
+       if (ret == 0)
+               stream->abort_int = TRUE;
+
+       return ret;
+}
+
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+                                                       uint16_t delay)
+{
+       struct delay_req req;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state != AVDTP_STATE_CONFIGURED &&
+                               stream->lsep->state != AVDTP_STATE_STREAMING)
+               return -EINVAL;
+
+       if (!stream->delay_reporting || session->version < 0x0103 ||
+                                       session->server->version < 0x0103)
+               return -EINVAL;
+
+       stream->delay = delay;
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+       req.delay = htons(delay);
+
+       return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT,
+                                                       &req, sizeof(req));
+}
+
+struct avdtp_local_sep *avdtp_register_sep(const bdaddr_t *src, uint8_t type,
+                                               uint8_t media_type,
+                                               uint8_t codec_type,
+                                               gboolean delay_reporting,
+                                               struct avdtp_sep_ind *ind,
+                                               struct avdtp_sep_cfm *cfm,
+                                               void *user_data)
+{
+       struct avdtp_server *server;
+       struct avdtp_local_sep *sep;
+
+       server = find_server(servers, src);
+       if (!server)
+               return NULL;
+
+       if (g_slist_length(server->seps) > MAX_SEID)
+               return NULL;
+
+       sep = g_new0(struct avdtp_local_sep, 1);
+
+       sep->state = AVDTP_STATE_IDLE;
+       sep->info.seid = g_slist_length(server->seps) + 1;
+       sep->info.type = type;
+       sep->info.media_type = media_type;
+       sep->codec = codec_type;
+       sep->ind = ind;
+       sep->cfm = cfm;
+       sep->user_data = user_data;
+       sep->server = server;
+       sep->delay_reporting = TRUE;
+
+       DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
+                       sep->info.type, sep->codec, sep->info.seid);
+       server->seps = g_slist_append(server->seps, sep);
+
+       return sep;
+}
+
+int avdtp_unregister_sep(struct avdtp_local_sep *sep)
+{
+       struct avdtp_server *server;
+
+       if (!sep)
+               return -EINVAL;
+
+       server = sep->server;
+       server->seps = g_slist_remove(server->seps, sep);
+
+       if (sep->stream)
+               release_stream(sep->stream, sep->stream->session);
+
+       DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
+                       sep->info.type, sep->codec, sep->info.seid);
+
+       g_free(sep);
+
+       return 0;
+}
+
+static GIOChannel *avdtp_server_socket(const bdaddr_t *src, gboolean master)
+{
+       GError *err = NULL;
+       GIOChannel *io;
+
+       io = bt_io_listen(BT_IO_L2CAP, NULL, avdtp_confirm_cb,
+                               NULL, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, src,
+                               BT_IO_OPT_PSM, AVDTP_PSM,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_MASTER, master,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("%s", err->message);
+               g_error_free(err);
+       }
+
+       return io;
+}
+
+const char *avdtp_strerror(struct avdtp_error *err)
+{
+       if (err->category == AVDTP_ERRNO)
+               return strerror(err->err.posix_errno);
+
+       switch(err->err.error_code) {
+       case AVDTP_BAD_HEADER_FORMAT:
+               return "Bad Header Format";
+       case AVDTP_BAD_LENGTH:
+               return "Bad Packet Length";
+       case AVDTP_BAD_ACP_SEID:
+               return "Bad Acceptor SEID";
+       case AVDTP_SEP_IN_USE:
+               return "Stream End Point in Use";
+       case AVDTP_SEP_NOT_IN_USE:
+               return "Stream End Point Not in Use";
+       case AVDTP_BAD_SERV_CATEGORY:
+               return "Bad Service Category";
+       case AVDTP_BAD_PAYLOAD_FORMAT:
+               return "Bad Payload format";
+       case AVDTP_NOT_SUPPORTED_COMMAND:
+               return "Command Not Supported";
+       case AVDTP_INVALID_CAPABILITIES:
+               return "Invalid Capabilities";
+       case AVDTP_BAD_RECOVERY_TYPE:
+               return "Bad Recovery Type";
+       case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT:
+               return "Bad Media Transport Format";
+       case AVDTP_BAD_RECOVERY_FORMAT:
+               return "Bad Recovery Format";
+       case AVDTP_BAD_ROHC_FORMAT:
+               return "Bad Header Compression Format";
+       case AVDTP_BAD_CP_FORMAT:
+               return "Bad Content Protetion Format";
+       case AVDTP_BAD_MULTIPLEXING_FORMAT:
+               return "Bad Multiplexing Format";
+       case AVDTP_UNSUPPORTED_CONFIGURATION:
+               return "Configuration not supported";
+       case AVDTP_BAD_STATE:
+               return "Bad State";
+       default:
+               return "Unknow error";
+       }
+}
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep)
+{
+       return sep->state;
+}
+
+void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst)
+{
+       if (src)
+               bacpy(src, &session->server->src);
+       if (dst)
+               bacpy(dst, &session->dst);
+}
+
+int avdtp_init(const bdaddr_t *src, GKeyFile *config, uint16_t *version)
+{
+       GError *err = NULL;
+       gboolean tmp, master = TRUE;
+       struct avdtp_server *server;
+       uint16_t ver = 0x0102;
+
+       if (!config)
+               goto proceed;
+
+       tmp = g_key_file_get_boolean(config, "General",
+                       "Master", &err);
+       if (err) {
+               DBG("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else
+               master = tmp;
+
+       tmp = g_key_file_get_boolean(config, "General", "AutoConnect",
+                       &err);
+       if (err)
+               g_clear_error(&err);
+       else
+               auto_connect = tmp;
+
+       if (g_key_file_get_boolean(config, "A2DP", "DelayReporting", NULL))
+               ver = 0x0103;
+
+proceed:
+       server = g_new0(struct avdtp_server, 1);
+       if (!server)
+               return -ENOMEM;
+
+       server->version = ver;
+
+       if (version)
+               *version = server->version;
+
+       server->io = avdtp_server_socket(src, master);
+       if (!server->io) {
+               g_free(server);
+               return -1;
+       }
+
+       bacpy(&server->src, src);
+
+       servers = g_slist_append(servers, server);
+
+       return 0;
+}
+
+void avdtp_exit(const bdaddr_t *src)
+{
+       struct avdtp_server *server;
+       GSList *l;
+
+       server = find_server(servers, src);
+       if (!server)
+               return;
+
+       l = server->sessions;
+       while (l) {
+               struct avdtp *session = l->data;
+
+               l = l->next;
+               /* value of l pointer should be updated before invoking
+                * connection_lost since it internally uses avdtp_unref
+                * which operates on server->session list as well
+                */
+               connection_lost(session, -ECONNABORTED);
+       }
+
+       servers = g_slist_remove(servers, server);
+
+       g_io_channel_shutdown(server->io, TRUE, NULL);
+       g_io_channel_unref(server->io);
+       g_free(server);
+}
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream)
+{
+       return g_slist_find(session->streams, stream) ? TRUE : FALSE;
+}
+
+void avdtp_set_auto_disconnect(struct avdtp *session, gboolean auto_dc)
+{
+       session->auto_dc = auto_dc;
+}
+
+gboolean avdtp_stream_setup_active(struct avdtp *session)
+{
+       return session->stream_setup;
+}
+
+void avdtp_set_device_disconnect(struct avdtp *session, gboolean dev_dc)
+{
+       session->device_disconnect = dev_dc;
+}
+
+unsigned int avdtp_add_state_cb(avdtp_session_state_cb cb, void *user_data)
+{
+       struct avdtp_state_callback *state_cb;
+       static unsigned int id = 0;
+
+       state_cb = g_new(struct avdtp_state_callback, 1);
+       state_cb->cb = cb;
+       state_cb->user_data = user_data;
+       state_cb->id = ++id;
+
+       avdtp_callbacks = g_slist_append(avdtp_callbacks, state_cb);
+
+       return state_cb->id;
+}
+
+gboolean avdtp_remove_state_cb(unsigned int id)
+{
+       GSList *l;
+
+       for (l = avdtp_callbacks; l != NULL; l = l->next) {
+               struct avdtp_state_callback *cb = l->data;
+               if (cb && cb->id == id) {
+                       avdtp_callbacks = g_slist_remove(avdtp_callbacks, cb);
+                       g_free(cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
diff --git a/audio/avdtp.h b/audio/avdtp.h
new file mode 100644 (file)
index 0000000..dac093b
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef enum {
+       AVDTP_SESSION_STATE_DISCONNECTED,
+       AVDTP_SESSION_STATE_CONNECTING,
+       AVDTP_SESSION_STATE_CONNECTED
+} avdtp_session_state_t;
+
+struct avdtp;
+struct avdtp_stream;
+struct avdtp_local_sep;
+struct avdtp_remote_sep;
+struct avdtp_error {
+       uint8_t category;
+       union {
+               uint8_t error_code;
+               int posix_errno;
+       } err;
+};
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT                  0x01
+#define AVDTP_REPORTING                                0x02
+#define AVDTP_RECOVERY                         0x03
+#define AVDTP_CONTENT_PROTECTION               0x04
+#define AVDTP_HEADER_COMPRESSION               0x05
+#define AVDTP_MULTIPLEXING                     0x06
+#define AVDTP_MEDIA_CODEC                      0x07
+#define AVDTP_DELAY_REPORTING                  0x08
+#define AVDTP_ERRNO                            0xff
+
+/* AVDTP error definitions */
+#define AVDTP_BAD_HEADER_FORMAT                        0x01
+#define AVDTP_BAD_LENGTH                       0x11
+#define AVDTP_BAD_ACP_SEID                     0x12
+#define AVDTP_SEP_IN_USE                       0x13
+#define AVDTP_SEP_NOT_IN_USE                   0x14
+#define AVDTP_BAD_SERV_CATEGORY                        0x17
+#define AVDTP_BAD_PAYLOAD_FORMAT               0x18
+#define AVDTP_NOT_SUPPORTED_COMMAND            0x19
+#define AVDTP_INVALID_CAPABILITIES             0x1A
+#define AVDTP_BAD_RECOVERY_TYPE                        0x22
+#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT       0x23
+#define AVDTP_BAD_RECOVERY_FORMAT              0x25
+#define AVDTP_BAD_ROHC_FORMAT                  0x26
+#define AVDTP_BAD_CP_FORMAT                    0x27
+#define AVDTP_BAD_MULTIPLEXING_FORMAT          0x28
+#define AVDTP_UNSUPPORTED_CONFIGURATION                0x29
+#define AVDTP_BAD_STATE                                0x31
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE                  0x00
+#define AVDTP_SEP_TYPE_SINK                    0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO                 0x00
+#define AVDTP_MEDIA_TYPE_VIDEO                 0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA            0x02
+
+typedef enum {
+       AVDTP_STATE_IDLE,
+       AVDTP_STATE_CONFIGURED,
+       AVDTP_STATE_OPEN,
+       AVDTP_STATE_STREAMING,
+       AVDTP_STATE_CLOSING,
+       AVDTP_STATE_ABORTING,
+} avdtp_state_t;
+
+struct avdtp_service_capability {
+       uint8_t category;
+       uint8_t length;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_media_codec_capability {
+       uint8_t rfa0:4;
+       uint8_t media_type:4;
+       uint8_t media_codec_type;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_media_codec_capability {
+       uint8_t media_type:4;
+       uint8_t rfa0:4;
+       uint8_t media_codec_type;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+typedef void (*avdtp_session_state_cb) (struct audio_device *dev,
+                                       struct avdtp *session,
+                                       avdtp_session_state_t old_state,
+                                       avdtp_session_state_t new_state,
+                                       void *user_data);
+
+typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream,
+                                       avdtp_state_t old_state,
+                                       avdtp_state_t new_state,
+                                       struct avdtp_error *err,
+                                       void *user_data);
+
+typedef void (*avdtp_set_configuration_cb) (struct avdtp *session,
+                                               struct avdtp_stream *stream,
+                                               struct avdtp_error *err);
+
+/* Callbacks for when a reply is received to a command that we sent */
+struct avdtp_sep_cfm {
+       void (*set_configuration) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_error *err,
+                                       void *user_data);
+       void (*get_configuration) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_error *err,
+                                       void *user_data);
+       void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data);
+       void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data);
+       void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data);
+       void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data);
+       void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data);
+       void (*reconfigure) (struct avdtp *session,
+                               struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data);
+       void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data);
+};
+
+/* Callbacks for indicating when we received a new command. The return value
+ * indicates whether the command should be rejected or accepted */
+struct avdtp_sep_ind {
+       gboolean (*get_capability) (struct avdtp *session,
+                                       struct avdtp_local_sep *sep,
+                                       gboolean get_all,
+                                       GSList **caps, uint8_t *err,
+                                       void *user_data);
+       gboolean (*set_configuration) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       struct avdtp_stream *stream,
+                                       GSList *caps,
+                                       avdtp_set_configuration_cb cb,
+                                       void *user_data);
+       gboolean (*get_configuration) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       uint8_t *err, void *user_data);
+       gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data);
+       gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data);
+       gboolean (*suspend) (struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data);
+       gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data);
+       void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data);
+       gboolean (*reconfigure) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       uint8_t *err, void *user_data);
+       gboolean (*delayreport) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       uint8_t rseid, uint16_t delay,
+                                       uint8_t *err, void *user_data);
+};
+
+typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
+                                       struct avdtp_error *err, void *user_data);
+
+struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst);
+
+void avdtp_unref(struct avdtp *session);
+struct avdtp *avdtp_ref(struct avdtp *session);
+
+gboolean avdtp_is_connected(const bdaddr_t *src, const bdaddr_t *dst);
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+                                                       void *data, int size);
+
+struct avdtp_remote_sep *avdtp_get_remote_sep(struct avdtp *session,
+                                               uint8_t seid);
+
+uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep);
+
+uint8_t avdtp_get_type(struct avdtp_remote_sep *sep);
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);
+
+gboolean avdtp_get_delay_reporting(struct avdtp_remote_sep *sep);
+
+struct avdtp_stream *avdtp_get_stream(struct avdtp_remote_sep *sep);
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+                       void *user_data);
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream);
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       avdtp_stream_state_cb cb, void *data);
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+                               struct avdtp_stream *stream,
+                               unsigned int id);
+
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+                                       uint16_t *imtu, uint16_t *omtu,
+                                       GSList **caps);
+struct avdtp_service_capability *avdtp_stream_get_codec(
+                                               struct avdtp_stream *stream);
+gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+                               struct avdtp_service_capability *cap);
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+                                       GSList *caps);
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+                                               struct avdtp_stream *stream);
+
+unsigned int avdtp_add_state_cb(avdtp_session_state_cb cb, void *user_data);
+
+gboolean avdtp_remove_state_cb(unsigned int id);
+
+int avdtp_set_configuration(struct avdtp *session,
+                               struct avdtp_remote_sep *rsep,
+                               struct avdtp_local_sep *lsep,
+                               GSList *caps,
+                               struct avdtp_stream **stream);
+
+int avdtp_get_configuration(struct avdtp *session,
+                               struct avdtp_stream *stream);
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_reconfigure(struct avdtp *session, GSList *caps,
+                       struct avdtp_stream *stream);
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+               gboolean immediate);
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+                                                       uint16_t delay);
+
+struct avdtp_local_sep *avdtp_register_sep(const bdaddr_t *src, uint8_t type,
+                                               uint8_t media_type,
+                                               uint8_t codec_type,
+                                               gboolean delay_reporting,
+                                               struct avdtp_sep_ind *ind,
+                                               struct avdtp_sep_cfm *cfm,
+                                               void *user_data);
+
+/* Find a matching pair of local and remote SEP ID's */
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+                                               struct avdtp_local_sep *lsep);
+
+int avdtp_unregister_sep(struct avdtp_local_sep *sep);
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep);
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id);
+const char *avdtp_strerror(struct avdtp_error *err);
+uint8_t avdtp_error_category(struct avdtp_error *err);
+int avdtp_error_error_code(struct avdtp_error *err);
+int avdtp_error_posix_errno(struct avdtp_error *err);
+
+void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst);
+
+void avdtp_set_auto_disconnect(struct avdtp *session, gboolean auto_dc);
+gboolean avdtp_stream_setup_active(struct avdtp *session);
+void avdtp_set_device_disconnect(struct avdtp *session, gboolean dev_dc);
+
+int avdtp_init(const bdaddr_t *src, GKeyFile *config, uint16_t *version);
+void avdtp_exit(const bdaddr_t *src);
diff --git a/audio/avrcp.c b/audio/avrcp.c
new file mode 100644 (file)
index 0000000..89ee112
--- /dev/null
@@ -0,0 +1,1468 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  Texas Instruments, Inc.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "manager.h"
+#include "avctp.h"
+#include "avrcp.h"
+#include "sdpd.h"
+#include "dbus-common.h"
+
+/* Company IDs for vendor dependent commands */
+#define IEEEID_BTSIG           0x001958
+
+/* Error codes for metadata transfer */
+#define E_INVALID_COMMAND      0x00
+#define E_INVALID_PARAM                0x01
+#define E_PARAM_NOT_FOUND      0x02
+#define E_INTERNAL             0x03
+
+/* Packet types */
+#define AVRCP_PACKET_TYPE_SINGLE       0x00
+#define AVRCP_PACKET_TYPE_START                0x01
+#define AVRCP_PACKET_TYPE_CONTINUING   0x02
+#define AVRCP_PACKET_TYPE_END          0x03
+
+/* PDU types for metadata transfer */
+#define AVRCP_GET_CAPABILITIES         0x10
+#define AVRCP_LIST_PLAYER_ATTRIBUTES   0X11
+#define AVRCP_LIST_PLAYER_VALUES       0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13
+#define AVRCP_SET_PLAYER_VALUE         0x14
+#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT        0x15
+#define AVRCP_GET_PLAYER_VALUE_TEXT    0x16
+#define AVRCP_DISPLAYABLE_CHARSET      0x17
+#define AVRCP_CT_BATTERY_STATUS                0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES   0x20
+#define AVRCP_GET_PLAY_STATUS          0x30
+#define AVRCP_REGISTER_NOTIFICATION    0x31
+#define AVRCP_REQUEST_CONTINUING       0x40
+#define AVRCP_ABORT_CONTINUING         0x41
+#define AVRCP_SET_ABSOLUTE_VOLUME      0x50
+
+/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
+#define CAP_COMPANY_ID         0x02
+#define CAP_EVENTS_SUPPORTED   0x03
+
+#define AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH 5
+
+#define AVRCP_FEATURE_CATEGORY_1       0x0001
+#define AVRCP_FEATURE_CATEGORY_2       0x0002
+#define AVRCP_FEATURE_CATEGORY_3       0x0004
+#define AVRCP_FEATURE_CATEGORY_4       0x0008
+#define AVRCP_FEATURE_PLAYER_SETTINGS  0x0010
+
+enum battery_status {
+       BATTERY_STATUS_NORMAL =         0,
+       BATTERY_STATUS_WARNING =        1,
+       BATTERY_STATUS_CRITICAL =       2,
+       BATTERY_STATUS_EXTERNAL =       3,
+       BATTERY_STATUS_FULL_CHARGE =    4,
+};
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avrcp_header {
+       uint8_t company_id[3];
+       uint8_t pdu_id;
+       uint8_t packet_type:2;
+       uint8_t rsvd:6;
+       uint16_t params_len;
+       uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avrcp_header {
+       uint8_t company_id[3];
+       uint8_t pdu_id;
+       uint8_t rsvd:6;
+       uint8_t packet_type:2;
+       uint16_t params_len;
+       uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#else
+#error "Unknown byte order"
+#endif
+
+#define AVRCP_MTU      (AVC_MTU - AVC_HEADER_LENGTH)
+#define AVRCP_PDU_MTU  (AVRCP_MTU - AVRCP_HEADER_LENGTH)
+
+struct avrcp_server {
+       bdaddr_t src;
+       uint32_t tg_record_id;
+       uint32_t ct_record_id;
+       GSList *players;
+       struct avrcp_player *active_player;
+};
+
+struct pending_pdu {
+       uint8_t pdu_id;
+       GList *attr_ids;
+       uint16_t offset;
+};
+
+struct avrcp_player {
+       struct avrcp_server *server;
+       struct avctp *session;
+       struct audio_device *dev;
+
+       unsigned int handler;
+       uint16_t registered_events;
+       uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
+       struct pending_pdu *pending_pdu;
+
+       struct avrcp_player_cb *cb;
+       void *user_data;
+       GDestroyNotify destroy;
+};
+
+static GSList *servers = NULL;
+static unsigned int avctp_id = 0;
+
+/* Company IDs supported by this device */
+static uint32_t company_ids[] = {
+       IEEEID_BTSIG,
+};
+
+static void register_volume_notification(struct avrcp_player *player);
+
+static sdp_record_t *avrcp_ct_record(void)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap, avctp, avrct;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t *record;
+       sdp_data_t *psm, *version, *features;
+       uint16_t lp = AVCTP_PSM;
+       uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103;
+       uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
+                                               AVRCP_FEATURE_CATEGORY_2 |
+                                               AVRCP_FEATURE_CATEGORY_3 |
+                                               AVRCP_FEATURE_CATEGORY_4 );
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(record, root);
+
+       /* Service Class ID List */
+       sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &avrct);
+       sdp_set_service_classes(record, svclass_id);
+
+       /* Protocol Descriptor List */
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&avctp, AVCTP_UUID);
+       proto[1] = sdp_list_append(0, &avctp);
+       version = sdp_data_alloc(SDP_UINT16, &avctp_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);
+
+       /* Bluetooth Profile Descriptor List */
+       sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+       profile[0].version = avrcp_ver;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(record, pfseq);
+
+       features = sdp_data_alloc(SDP_UINT16, &feat);
+       sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       sdp_set_info_attr(record, "AVRCP CT", 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 sdp_record_t *avrcp_tg_record(void)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap, avctp, avrtg;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t *record;
+       sdp_data_t *psm, *version, *features;
+       uint16_t lp = AVCTP_PSM;
+       uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
+       uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
+                                       AVRCP_FEATURE_CATEGORY_2 |
+                                       AVRCP_FEATURE_CATEGORY_3 |
+                                       AVRCP_FEATURE_CATEGORY_4 |
+                                       AVRCP_FEATURE_PLAYER_SETTINGS );
+
+       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);
+
+       /* Service Class ID List */
+       sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &avrtg);
+       sdp_set_service_classes(record, svclass_id);
+
+       /* Protocol Descriptor List */
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&avctp, AVCTP_UUID);
+       proto[1] = sdp_list_append(0, &avctp);
+       version = sdp_data_alloc(SDP_UINT16, &avctp_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);
+
+       /* Bluetooth Profile Descriptor List */
+       sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+       profile[0].version = avrcp_ver;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(record, pfseq);
+
+       features = sdp_data_alloc(SDP_UINT16, &feat);
+       sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       sdp_set_info_attr(record, "AVRCP TG", 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(aproto, 0);
+       sdp_list_free(pfseq, 0);
+       sdp_list_free(root, 0);
+       sdp_list_free(svclass_id, 0);
+
+       return record;
+}
+
+static unsigned int attr_get_max_val(uint8_t attr)
+{
+       switch (attr) {
+       case AVRCP_ATTRIBUTE_EQUALIZER:
+               return AVRCP_EQUALIZER_ON;
+       case AVRCP_ATTRIBUTE_REPEAT_MODE:
+               return AVRCP_REPEAT_MODE_GROUP;
+       case AVRCP_ATTRIBUTE_SHUFFLE:
+               return AVRCP_SHUFFLE_GROUP;
+       case AVRCP_ATTRIBUTE_SCAN:
+               return AVRCP_SCAN_GROUP;
+       }
+
+       return 0;
+}
+
+static const char *battery_status_to_str(enum battery_status status)
+{
+       switch (status) {
+       case BATTERY_STATUS_NORMAL:
+               return "normal";
+       case BATTERY_STATUS_WARNING:
+               return "warning";
+       case BATTERY_STATUS_CRITICAL:
+               return "critical";
+       case BATTERY_STATUS_EXTERNAL:
+               return "external";
+       case BATTERY_STATUS_FULL_CHARGE:
+               return "fullcharge";
+       }
+
+       return NULL;
+}
+
+/*
+ * get_company_id:
+ *
+ * Get three-byte Company_ID from incoming AVRCP message
+ */
+static uint32_t get_company_id(const uint8_t cid[3])
+{
+       return cid[0] << 16 | cid[1] << 8 | cid[2];
+}
+
+/*
+ * set_company_id:
+ *
+ * Set three-byte Company_ID into outgoing AVRCP message
+ */
+static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
+{
+       cid[0] = cid_in >> 16;
+       cid[1] = cid_in >> 8;
+       cid[2] = cid_in;
+}
+
+int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data)
+{
+       uint8_t buf[AVRCP_HEADER_LENGTH + 9];
+       struct avrcp_header *pdu = (void *) buf;
+       uint16_t size;
+       int err;
+
+       if (player->session == NULL)
+               return -ENOTCONN;
+
+       if (!(player->registered_events & (1 << id)))
+               return 0;
+
+       memset(buf, 0, sizeof(buf));
+
+       set_company_id(pdu->company_id, IEEEID_BTSIG);
+
+       pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+       pdu->params[0] = id;
+
+       DBG("id=%u", id);
+
+       switch (id) {
+       case AVRCP_EVENT_STATUS_CHANGED:
+               size = 2;
+               pdu->params[1] = *((uint8_t *)data);
+
+               break;
+       case AVRCP_EVENT_TRACK_CHANGED:
+               size = 9;
+               memcpy(&pdu->params[1], data, sizeof(uint64_t));
+
+               break;
+       case AVRCP_EVENT_TRACK_REACHED_END:
+       case AVRCP_EVENT_TRACK_REACHED_START:
+               size = 1;
+               break;
+       default:
+               error("Unknown event %u", id);
+               return -EINVAL;
+       }
+
+       pdu->params_len = htons(size);
+
+       err = avctp_send_vendordep(player->session, player->transaction_events[id],
+                                       AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
+                                       buf, size + AVRCP_HEADER_LENGTH);
+       if (err < 0)
+               return err;
+
+       /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
+       player->registered_events ^= 1 << id;
+
+       return 0;
+}
+
+static uint16_t player_write_media_attribute(struct avrcp_player *player,
+                                               uint32_t id, uint8_t *buf,
+                                               uint16_t *pos,
+                                               uint16_t *offset)
+{
+       uint16_t len;
+       uint16_t attr_len;
+       char valstr[20];
+       void *value;
+
+       DBG("%u", id);
+
+       value = player->cb->get_metadata(id, player->user_data);
+       if (value == NULL) {
+               *offset = 0;
+               return 0;
+       }
+
+       switch (id) {
+       case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+       case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+       case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+               snprintf(valstr, 20, "%u", GPOINTER_TO_UINT(value));
+               value = valstr;
+               break;
+       }
+
+       attr_len = strlen(value);
+       value = ((char *) value) + *offset;
+       len = attr_len - *offset;
+
+       if (len > AVRCP_PDU_MTU - *pos) {
+               len = AVRCP_PDU_MTU - *pos;
+               *offset += len;
+       } else {
+               *offset = 0;
+       }
+
+       memcpy(&buf[*pos], value, len);
+       *pos += len;
+
+       return attr_len;
+}
+
+static GList *player_fill_media_attribute(struct avrcp_player *player,
+                                       GList *attr_ids, uint8_t *buf,
+                                       uint16_t *pos, uint16_t *offset)
+{
+       struct media_attribute_header {
+               uint32_t id;
+               uint16_t charset;
+               uint16_t len;
+       } *hdr = NULL;
+       GList *l;
+
+       for (l = attr_ids; l != NULL; l = g_list_delete_link(l, l)) {
+               uint32_t attr = GPOINTER_TO_UINT(l->data);
+               uint16_t attr_len;
+
+               if (*offset == 0) {
+                       if (*pos + sizeof(*hdr) >= AVRCP_PDU_MTU)
+                               break;
+
+                       hdr = (void *) &buf[*pos];
+                       hdr->id = htonl(attr);
+                       hdr->charset = htons(0x6A); /* Always use UTF-8 */
+                       *pos += sizeof(*hdr);
+               }
+
+               attr_len = player_write_media_attribute(player, attr, buf,
+                                                               pos, offset);
+
+               if (hdr != NULL)
+                       hdr->len = htons(attr_len);
+
+               if (*offset > 0)
+                       break;
+       }
+
+       return l;
+}
+
+static struct pending_pdu *pending_pdu_new(uint8_t pdu_id, GList *attr_ids,
+                                                       unsigned int offset)
+{
+       struct pending_pdu *pending = g_new(struct pending_pdu, 1);
+
+       pending->pdu_id = pdu_id;
+       pending->attr_ids = attr_ids;
+       pending->offset = offset;
+
+       return pending;
+}
+
+static gboolean player_abort_pending_pdu(struct avrcp_player *player)
+{
+       if (player->pending_pdu == NULL)
+               return FALSE;
+
+       g_list_free(player->pending_pdu->attr_ids);
+       g_free(player->pending_pdu);
+       player->pending_pdu = NULL;
+
+       return TRUE;
+}
+
+static int player_set_attribute(struct avrcp_player *player,
+                                               uint8_t attr, uint8_t val)
+{
+       DBG("Change attribute: %u %u", attr, val);
+
+       return player->cb->set_setting(attr, val, player->user_data);
+}
+
+static int player_get_attribute(struct avrcp_player *player, uint8_t attr)
+{
+       int value;
+
+       DBG("attr %u", attr);
+
+       value = player->cb->get_setting(attr, player->user_data);
+       if (value < 0)
+               DBG("attr %u not supported by player", attr);
+
+       return value;
+}
+
+static uint8_t avrcp_handle_get_capabilities(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       unsigned int i;
+
+       if (len != 1)
+               goto err;
+
+       DBG("id=%u", pdu->params[0]);
+
+       switch (pdu->params[0]) {
+       case CAP_COMPANY_ID:
+               for (i = 0; i < G_N_ELEMENTS(company_ids); i++) {
+                       set_company_id(&pdu->params[2 + i * 3],
+                                                       company_ids[i]);
+               }
+
+               pdu->params_len = htons(2 + (3 * G_N_ELEMENTS(company_ids)));
+               pdu->params[1] = G_N_ELEMENTS(company_ids);
+
+               return AVC_CTYPE_STABLE;
+       case CAP_EVENTS_SUPPORTED:
+               pdu->params[1] = 4;
+               pdu->params[2] = AVRCP_EVENT_STATUS_CHANGED;
+               pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
+               pdu->params[4] = AVRCP_EVENT_TRACK_REACHED_START;
+               pdu->params[5] = AVRCP_EVENT_TRACK_REACHED_END;
+
+               pdu->params_len = htons(2 + pdu->params[1]);
+               return AVC_CTYPE_STABLE;
+       }
+
+err:
+       pdu->params_len = htons(1);
+       pdu->params[0] = E_INVALID_PARAM;
+
+       return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_list_player_attributes(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       unsigned int i;
+
+       if (len != 0) {
+               pdu->params_len = htons(1);
+               pdu->params[0] = E_INVALID_PARAM;
+               return AVC_CTYPE_REJECTED;
+       }
+
+       if (!player)
+               goto done;
+
+       for (i = 1; i <= AVRCP_ATTRIBUTE_SCAN; i++) {
+               if (player_get_attribute(player, i) < 0)
+                       continue;
+
+               len++;
+               pdu->params[len] = i;
+       }
+
+done:
+       pdu->params[0] = len;
+       pdu->params_len = htons(len + 1);
+
+       return AVC_CTYPE_STABLE;
+}
+
+static uint8_t avrcp_handle_list_player_values(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       unsigned int i;
+
+       if (len != 1 || !player)
+               goto err;
+
+       if (player_get_attribute(player, pdu->params[0]) < 0)
+               goto err;
+
+       len = attr_get_max_val(pdu->params[0]);
+
+       for (i = 1; i <= len; i++)
+               pdu->params[i] = i;
+
+       pdu->params[0] = len;
+       pdu->params_len = htons(len + 1);
+
+       return AVC_CTYPE_STABLE;
+
+err:
+       pdu->params_len = htons(1);
+       pdu->params[0] = E_INVALID_PARAM;
+       return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_get_element_attributes(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       uint64_t *identifier = (uint64_t *) &pdu->params[0];
+       uint16_t pos;
+       uint8_t nattr;
+       GList *attr_ids;
+       uint16_t offset;
+
+       if (len < 9 || *identifier != 0)
+               goto err;
+
+       nattr = pdu->params[8];
+
+       if (len < nattr * sizeof(uint32_t) + 1)
+               goto err;
+
+       if (!nattr) {
+               /*
+                * Return all available information, at least
+                * title must be returned if there's a track selected.
+                */
+               attr_ids = player->cb->list_metadata(player->user_data);
+               len = g_list_length(attr_ids);
+       } else {
+               unsigned int i;
+               uint32_t *attr = (uint32_t *) &pdu->params[9];
+
+               for (i = 0, len = 0, attr_ids = NULL; i < nattr; i++, attr++) {
+                       uint32_t id = ntohl(bt_get_unaligned(attr));
+
+                       /* Don't add invalid attributes */
+                       if (id == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+                                       id > AVRCP_MEDIA_ATTRIBUTE_LAST)
+                               continue;
+
+                       len++;
+                       attr_ids = g_list_prepend(attr_ids,
+                                                       GUINT_TO_POINTER(id));
+               }
+
+               attr_ids = g_list_reverse(attr_ids);
+       }
+
+       if (!len)
+               goto err;
+
+       player_abort_pending_pdu(player);
+       pos = 1;
+       offset = 0;
+       attr_ids = player_fill_media_attribute(player, attr_ids, pdu->params,
+                                                               &pos, &offset);
+
+       if (attr_ids != NULL) {
+               player->pending_pdu = pending_pdu_new(pdu->pdu_id, attr_ids,
+                                                               offset);
+               pdu->packet_type = AVRCP_PACKET_TYPE_START;
+       }
+
+       pdu->params[0] = len;
+       pdu->params_len = htons(pos);
+
+       return AVC_CTYPE_STABLE;
+err:
+       pdu->params_len = htons(1);
+       pdu->params[0] = E_INVALID_PARAM;
+       return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_get_current_player_value(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       uint8_t *settings;
+       unsigned int i;
+
+       if (player == NULL || len <= 1 || pdu->params[0] != len - 1)
+               goto err;
+
+       /*
+        * Save a copy of requested settings because we can override them
+        * while responding
+        */
+       settings = g_memdup(&pdu->params[1], pdu->params[0]);
+       len = 0;
+
+       /*
+        * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
+        * and send a response with the existent ones. Only if all IDs are
+        * non-existent we should send an error.
+        */
+       for (i = 0; i < pdu->params[0]; i++) {
+               int val;
+
+               if (settings[i] < AVRCP_ATTRIBUTE_EQUALIZER ||
+                                       settings[i] > AVRCP_ATTRIBUTE_SCAN) {
+                       DBG("Ignoring %u", settings[i]);
+                       continue;
+               }
+
+               val = player_get_attribute(player, settings[i]);
+               if (val < 0)
+                       continue;
+
+               pdu->params[++len] = settings[i];
+               pdu->params[++len] = val;
+       }
+
+       g_free(settings);
+
+       if (len) {
+               pdu->params[0] = len / 2;
+               pdu->params_len = htons(len + 1);
+
+               return AVC_CTYPE_STABLE;
+       }
+
+       error("No valid attributes in request");
+
+err:
+       pdu->params_len = htons(1);
+       pdu->params[0] = E_INVALID_PARAM;
+
+       return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_set_player_value(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       unsigned int i;
+       uint8_t *param;
+
+       if (len < 3 || len > 2 * pdu->params[0] + 1U)
+               goto err;
+
+       /*
+        * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs
+        * and set the existent ones. Sec. 5.2.4 is not clear however how to
+        * indicate that a certain ID was not accepted. If at least one
+        * attribute is valid, we respond with no parameters. Otherwise an
+        * E_INVALID_PARAM is sent.
+        */
+       for (len = 0, i = 0, param = &pdu->params[1]; i < pdu->params[0];
+                                                       i++, param += 2) {
+               if (player_set_attribute(player, param[0], param[1]) < 0)
+                       continue;
+
+               len++;
+       }
+
+       if (len) {
+               pdu->params_len = 0;
+
+               return AVC_CTYPE_ACCEPTED;
+       }
+
+err:
+       pdu->params_len = htons(1);
+       pdu->params[0] = E_INVALID_PARAM;
+       return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_displayable_charset(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+
+       if (len < 3) {
+               pdu->params_len = htons(1);
+               pdu->params[0] = E_INVALID_PARAM;
+               return AVC_CTYPE_REJECTED;
+       }
+
+       /*
+        * We acknowledge the commands, but we always use UTF-8 for
+        * encoding since CT is obliged to support it.
+        */
+       pdu->params_len = 0;
+       return AVC_CTYPE_STABLE;
+}
+
+static uint8_t avrcp_handle_ct_battery_status(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       const char *valstr;
+
+       if (len != 1)
+               goto err;
+
+       valstr = battery_status_to_str(pdu->params[0]);
+       if (valstr == NULL)
+               goto err;
+
+       pdu->params_len = 0;
+
+       return AVC_CTYPE_STABLE;
+
+err:
+       pdu->params_len = htons(1);
+       pdu->params[0] = E_INVALID_PARAM;
+       return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_get_play_status(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       uint32_t position;
+       uint32_t duration;
+       void *pduration;
+
+       if (len != 0) {
+               pdu->params_len = htons(1);
+               pdu->params[0] = E_INVALID_PARAM;
+               return AVC_CTYPE_REJECTED;
+       }
+
+       position = player->cb->get_position(player->user_data);
+       pduration = player->cb->get_metadata(AVRCP_MEDIA_ATTRIBUTE_DURATION,
+                                                       player->user_data);
+       if (pduration != NULL)
+               duration = htonl(GPOINTER_TO_UINT(pduration));
+       else
+               duration = htonl(UINT32_MAX);
+
+       position = htonl(position);
+
+       memcpy(&pdu->params[0], &duration, 4);
+       memcpy(&pdu->params[4], &position, 4);
+       pdu->params[8] = player->cb->get_status(player->user_data);;
+
+       pdu->params_len = htons(9);
+
+       return AVC_CTYPE_STABLE;
+}
+
+static uint8_t avrcp_handle_register_notification(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       uint64_t uid;
+
+       /*
+        * 1 byte for EventID, 4 bytes for Playback interval but the latest
+        * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP
+        * 1.3 spec, section 5.4.2.
+        */
+       if (len != 5)
+               goto err;
+
+       switch (pdu->params[0]) {
+       case AVRCP_EVENT_STATUS_CHANGED:
+               len = 2;
+               pdu->params[1] = player->cb->get_status(player->user_data);
+
+               break;
+       case AVRCP_EVENT_TRACK_CHANGED:
+               len = 9;
+               uid = player->cb->get_uid(player->user_data);
+               memcpy(&pdu->params[1], &uid, sizeof(uint64_t));
+
+               break;
+       case AVRCP_EVENT_TRACK_REACHED_END:
+       case AVRCP_EVENT_TRACK_REACHED_START:
+               len = 1;
+               break;
+       default:
+               /* All other events are not supported yet */
+               goto err;
+       }
+
+       /* Register event and save the transaction used */
+       player->registered_events |= (1 << pdu->params[0]);
+       player->transaction_events[pdu->params[0]] = transaction;
+
+       pdu->params_len = htons(len);
+
+       return AVC_CTYPE_INTERIM;
+
+err:
+       pdu->params_len = htons(1);
+       pdu->params[0] = E_INVALID_PARAM;
+       return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_request_continuing(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       struct pending_pdu *pending;
+
+       if (len != 1 || player->pending_pdu == NULL)
+               goto err;
+
+       pending = player->pending_pdu;
+
+       if (pending->pdu_id != pdu->params[0])
+               goto err;
+
+
+       len = 0;
+       pending->attr_ids = player_fill_media_attribute(player,
+                                                       pending->attr_ids,
+                                                       pdu->params, &len,
+                                                       &pending->offset);
+       pdu->pdu_id = pending->pdu_id;
+
+       if (pending->attr_ids == NULL) {
+               g_free(player->pending_pdu);
+               player->pending_pdu = NULL;
+               pdu->packet_type = AVRCP_PACKET_TYPE_END;
+       } else {
+               pdu->packet_type = AVRCP_PACKET_TYPE_CONTINUING;
+       }
+
+       pdu->params_len = htons(len);
+
+       return AVC_CTYPE_STABLE;
+err:
+       pdu->params_len = htons(1);
+       pdu->params[0] = E_INVALID_PARAM;
+       return AVC_CTYPE_REJECTED;
+}
+
+static uint8_t avrcp_handle_abort_continuing(struct avrcp_player *player,
+                                               struct avrcp_header *pdu,
+                                               uint8_t transaction)
+{
+       uint16_t len = ntohs(pdu->params_len);
+       struct pending_pdu *pending;
+
+       if (len != 1 || player->pending_pdu == NULL)
+               goto err;
+
+       pending = player->pending_pdu;
+
+       if (pending->pdu_id != pdu->params[0])
+               goto err;
+
+       player_abort_pending_pdu(player);
+       pdu->params_len = 0;
+
+       return AVC_CTYPE_ACCEPTED;
+
+err:
+       pdu->params_len = htons(1);
+       pdu->params[0] = E_INVALID_PARAM;
+       return AVC_CTYPE_REJECTED;
+}
+
+static struct pdu_handler {
+       uint8_t pdu_id;
+       uint8_t code;
+       uint8_t (*func) (struct avrcp_player *player,
+                                       struct avrcp_header *pdu,
+                                       uint8_t transaction);
+} handlers[] = {
+               { AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
+                                       avrcp_handle_get_capabilities },
+               { AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS,
+                                       avrcp_handle_list_player_attributes },
+               { AVRCP_LIST_PLAYER_VALUES, AVC_CTYPE_STATUS,
+                                       avrcp_handle_list_player_values },
+               { AVRCP_GET_ELEMENT_ATTRIBUTES, AVC_CTYPE_STATUS,
+                                       avrcp_handle_get_element_attributes },
+               { AVRCP_GET_CURRENT_PLAYER_VALUE, AVC_CTYPE_STATUS,
+                                       avrcp_handle_get_current_player_value },
+               { AVRCP_SET_PLAYER_VALUE, AVC_CTYPE_CONTROL,
+                                       avrcp_handle_set_player_value },
+               { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, AVC_CTYPE_STATUS,
+                                       NULL },
+               { AVRCP_GET_PLAYER_VALUE_TEXT, AVC_CTYPE_STATUS,
+                                       NULL },
+               { AVRCP_DISPLAYABLE_CHARSET, AVC_CTYPE_STATUS,
+                                       avrcp_handle_displayable_charset },
+               { AVRCP_CT_BATTERY_STATUS, AVC_CTYPE_STATUS,
+                                       avrcp_handle_ct_battery_status },
+               { AVRCP_GET_PLAY_STATUS, AVC_CTYPE_STATUS,
+                                       avrcp_handle_get_play_status },
+               { AVRCP_REGISTER_NOTIFICATION, AVC_CTYPE_NOTIFY,
+                                       avrcp_handle_register_notification },
+               { AVRCP_REQUEST_CONTINUING, AVC_CTYPE_CONTROL,
+                                       avrcp_handle_request_continuing },
+               { AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL,
+                                       avrcp_handle_abort_continuing },
+               { },
+};
+
+/* handle vendordep pdu inside an avctp packet */
+static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction,
+                                       uint8_t *code, uint8_t *subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       struct pdu_handler *handler;
+       struct avrcp_header *pdu = (void *) operands;
+       uint32_t company_id = get_company_id(pdu->company_id);
+
+       if (company_id != IEEEID_BTSIG) {
+               *code = AVC_CTYPE_NOT_IMPLEMENTED;
+               return 0;
+       }
+
+       DBG("AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
+                       pdu->pdu_id, company_id, pdu->params_len);
+
+       pdu->packet_type = 0;
+       pdu->rsvd = 0;
+
+       if (operand_count < AVRCP_HEADER_LENGTH) {
+               pdu->params[0] = E_INVALID_COMMAND;
+               goto err_metadata;
+       }
+
+       for (handler = handlers; handler; handler++) {
+               if (handler->pdu_id == pdu->pdu_id)
+                       break;
+       }
+
+       if (!handler || handler->code != *code) {
+               pdu->params[0] = E_INVALID_COMMAND;
+               goto err_metadata;
+       }
+
+       if (!handler->func) {
+               pdu->params[0] = E_INVALID_PARAM;
+               goto err_metadata;
+       }
+
+       *code = handler->func(player, pdu, transaction);
+
+       if (*code != AVC_CTYPE_REJECTED &&
+                               pdu->pdu_id != AVRCP_GET_ELEMENT_ATTRIBUTES &&
+                               pdu->pdu_id != AVRCP_REQUEST_CONTINUING &&
+                               pdu->pdu_id != AVRCP_ABORT_CONTINUING)
+               player_abort_pending_pdu(player);
+
+       return AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+err_metadata:
+       pdu->params_len = htons(1);
+       *code = AVC_CTYPE_REJECTED;
+
+       return AVRCP_HEADER_LENGTH + 1;
+}
+
+size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands)
+{
+    struct avrcp_header *pdu = (void *) operands;
+    uint32_t company_id = get_company_id(pdu->company_id);
+
+    *code = AVC_CTYPE_REJECTED;
+    pdu->params_len = htons(1);
+    pdu->params[0] = E_INTERNAL;
+
+    DBG("rejecting AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
+            pdu->pdu_id, company_id, pdu->params_len);
+
+    return AVRCP_HEADER_LENGTH + 1;
+}
+
+static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
+{
+       for (; list; list = list->next) {
+               struct avrcp_server *server = list->data;
+
+               if (bacmp(&server->src, src) == 0)
+                       return server;
+       }
+
+       return NULL;
+}
+
+static gboolean avrcp_handle_volume_changed(struct avctp *session,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       struct avrcp_header *pdu = (void *) operands;
+       uint8_t volume;
+
+       if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
+               return FALSE;
+
+       volume = pdu->params[1] & 0x7F;
+
+       player->cb->set_volume(volume, player->dev, player->user_data);
+
+       if (code == AVC_CTYPE_CHANGED) {
+               register_volume_notification(player);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void register_volume_notification(struct avrcp_player *player)
+{
+       uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH];
+       struct avrcp_header *pdu = (void *) buf;
+       uint8_t length;
+
+       memset(buf, 0, sizeof(buf));
+
+       set_company_id(pdu->company_id, IEEEID_BTSIG);
+       pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+       pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+       pdu->params[0] = AVRCP_EVENT_VOLUME_CHANGED;
+       pdu->params_len = htons(AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH);
+
+       length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+       avctp_send_vendordep_req(player->session, AVC_CTYPE_NOTIFY,
+                                       AVC_SUBUNIT_PANEL, buf, length,
+                                       avrcp_handle_volume_changed, player);
+}
+
+static void state_changed(struct audio_device *dev, avctp_state_t old_state,
+                               avctp_state_t new_state, void *user_data)
+{
+       struct avrcp_server *server;
+       struct avrcp_player *player;
+       const sdp_record_t *rec;
+       sdp_list_t *list;
+       sdp_profile_desc_t *desc;
+
+       server = find_server(servers, &dev->src);
+       if (!server)
+               return;
+
+       player = server->active_player;
+       if (!player)
+               return;
+
+       switch (new_state) {
+       case AVCTP_STATE_DISCONNECTED:
+               player->session = NULL;
+               player->dev = NULL;
+               player->registered_events = 0;
+
+               if (player->handler) {
+                       avctp_unregister_pdu_handler(player->handler);
+                       player->handler = 0;
+               }
+
+               break;
+       case AVCTP_STATE_CONNECTING:
+               player->session = avctp_connect(&dev->src, &dev->dst);
+               player->dev = dev;
+
+               if (!player->handler)
+                       player->handler = avctp_register_pdu_handler(
+                                                       AVC_OP_VENDORDEP,
+                                                       handle_vendordep_pdu,
+                                                       player);
+               break;
+       case AVCTP_STATE_CONNECTED:
+               rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
+               if (rec == NULL)
+                       return;
+
+               if (sdp_get_profile_descs(rec, &list) < 0)
+                       return;
+
+               desc = list->data;
+
+               if (desc && desc->version >= 0x0104)
+                       register_volume_notification(player);
+
+               sdp_list_free(list, free);
+       default:
+               return;
+       }
+}
+
+gboolean avrcp_connect(struct audio_device *dev)
+{
+       struct avctp *session;
+
+       session = avctp_connect(&dev->src, &dev->dst);
+       if (session)
+               return FALSE;
+
+       return TRUE;
+}
+
+void avrcp_disconnect(struct audio_device *dev)
+{
+       struct avctp *session;
+
+       session = avctp_get(&dev->src, &dev->dst);
+       if (!session)
+               return;
+
+       avctp_disconnect(session);
+}
+
+int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
+{
+       sdp_record_t *record;
+       gboolean tmp, master = TRUE;
+       GError *err = NULL;
+       struct avrcp_server *server;
+
+       if (config) {
+               tmp = g_key_file_get_boolean(config, "General",
+                                                       "Master", &err);
+               if (err) {
+                       DBG("audio.conf: %s", err->message);
+                       g_error_free(err);
+               } else
+                       master = tmp;
+       }
+
+       server = g_new0(struct avrcp_server, 1);
+       if (!server)
+               return -ENOMEM;
+
+       record = avrcp_tg_record();
+       if (!record) {
+               error("Unable to allocate new service record");
+               g_free(server);
+               return -1;
+       }
+
+       if (add_record_to_server(src, record) < 0) {
+               error("Unable to register AVRCP target service record");
+               g_free(server);
+               sdp_record_free(record);
+               return -1;
+       }
+       server->tg_record_id = record->handle;
+
+       record = avrcp_ct_record();
+       if (!record) {
+               error("Unable to allocate new service record");
+               g_free(server);
+               return -1;
+       }
+
+       if (add_record_to_server(src, record) < 0) {
+               error("Unable to register AVRCP service record");
+               sdp_record_free(record);
+               g_free(server);
+               return -1;
+       }
+       server->ct_record_id = record->handle;
+
+       if (avctp_register(src, master) < 0) {
+               remove_record_from_server(server->ct_record_id);
+               remove_record_from_server(server->tg_record_id);
+               g_free(server);
+               return -1;
+       }
+
+       bacpy(&server->src, src);
+
+       servers = g_slist_append(servers, server);
+
+       return 0;
+}
+
+static void player_destroy(gpointer data)
+{
+       struct avrcp_player *player = data;
+
+       if (player->destroy)
+               player->destroy(player->user_data);
+
+       player_abort_pending_pdu(player);
+
+       if (player->handler)
+               avctp_unregister_pdu_handler(player->handler);
+
+       g_free(player);
+}
+
+void avrcp_unregister(const bdaddr_t *src)
+{
+       struct avrcp_server *server;
+
+       server = find_server(servers, src);
+       if (!server)
+               return;
+
+       g_slist_free_full(server->players, player_destroy);
+
+       servers = g_slist_remove(servers, server);
+
+       remove_record_from_server(server->ct_record_id);
+       remove_record_from_server(server->tg_record_id);
+
+       avctp_unregister(&server->src);
+       g_free(server);
+
+       if (servers)
+               return;
+
+       if (avctp_id) {
+               avctp_remove_state_cb(avctp_id);
+               avctp_id = 0;
+       }
+}
+
+struct avrcp_player *avrcp_register_player(const bdaddr_t *src,
+                                               struct avrcp_player_cb *cb,
+                                               void *user_data,
+                                               GDestroyNotify destroy)
+{
+       struct avrcp_server *server;
+       struct avrcp_player *player;
+
+       server = find_server(servers, src);
+       if (!server)
+               return NULL;
+
+       player = g_new0(struct avrcp_player, 1);
+       player->server = server;
+       player->cb = cb;
+       player->user_data = user_data;
+       player->destroy = destroy;
+
+       if (!server->players)
+               server->active_player = player;
+
+       if (!avctp_id)
+               avctp_id = avctp_add_state_cb(state_changed, NULL);
+
+       server->players = g_slist_append(server->players, player);
+
+       return player;
+}
+
+void avrcp_unregister_player(struct avrcp_player *player)
+{
+       struct avrcp_server *server = player->server;
+
+       server->players = g_slist_remove(server->players, player);
+
+       if (server->active_player == player)
+               server->active_player = g_slist_nth_data(server->players, 0);
+
+       player_destroy(player);
+}
+
+static gboolean avrcp_handle_set_volume(struct avctp *session,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       struct avrcp_header *pdu = (void *) operands;
+       uint8_t volume;
+
+       if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED)
+               return FALSE;
+
+       volume = pdu->params[0] & 0x7F;
+
+       player->cb->set_volume(volume, player->dev, player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_set_volume(struct audio_device *dev, uint8_t volume)
+{
+       struct avrcp_server *server;
+       struct avrcp_player *player;
+       uint8_t buf[AVRCP_HEADER_LENGTH + 1];
+       struct avrcp_header *pdu = (void *) buf;
+
+       server = find_server(servers, &dev->src);
+       if (server == NULL)
+               return -EINVAL;
+
+       player = server->active_player;
+       if (player == NULL)
+               return -ENOTSUP;
+
+       if (player->session == NULL)
+               return -ENOTCONN;
+
+       memset(buf, 0, sizeof(buf));
+
+       set_company_id(pdu->company_id, IEEEID_BTSIG);
+
+       pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
+       pdu->params[0] = volume;
+       pdu->params_len = htons(1);
+
+       DBG("volume=%u", volume);
+
+       return avctp_send_vendordep_req(player->session, AVC_CTYPE_CONTROL,
+                                       AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+                                       avrcp_handle_set_volume, player);
+}
diff --git a/audio/avrcp.h b/audio/avrcp.h
new file mode 100644 (file)
index 0000000..bf11a6c
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/* player attributes */
+#define AVRCP_ATTRIBUTE_ILEGAL         0x00
+#define AVRCP_ATTRIBUTE_EQUALIZER      0x01
+#define AVRCP_ATTRIBUTE_REPEAT_MODE    0x02
+#define AVRCP_ATTRIBUTE_SHUFFLE                0x03
+#define AVRCP_ATTRIBUTE_SCAN           0x04
+
+/* equalizer values */
+#define AVRCP_EQUALIZER_OFF            0x01
+#define AVRCP_EQUALIZER_ON             0x02
+
+/* repeat mode values */
+#define AVRCP_REPEAT_MODE_OFF          0x01
+#define AVRCP_REPEAT_MODE_SINGLE       0x02
+#define AVRCP_REPEAT_MODE_ALL          0x03
+#define AVRCP_REPEAT_MODE_GROUP                0x04
+
+/* shuffle values */
+#define AVRCP_SHUFFLE_OFF              0x01
+#define AVRCP_SHUFFLE_ALL              0x02
+#define AVRCP_SHUFFLE_GROUP            0x03
+
+/* scan values */
+#define AVRCP_SCAN_OFF                 0x01
+#define AVRCP_SCAN_ALL                 0x02
+#define AVRCP_SCAN_GROUP               0x03
+
+/* media attributes */
+#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL  0x00
+#define AVRCP_MEDIA_ATTRIBUTE_TITLE    0x01
+#define AVRCP_MEDIA_ATTRIBUTE_ARTIST   0x02
+#define AVRCP_MEDIA_ATTRIBUTE_ALBUM    0x03
+#define AVRCP_MEDIA_ATTRIBUTE_TRACK    0x04
+#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05
+#define AVRCP_MEDIA_ATTRIBUTE_GENRE    0x06
+#define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07
+#define AVRCP_MEDIA_ATTRIBUTE_LAST     AVRCP_MEDIA_ATTRIBUTE_DURATION
+
+/* play status */
+#define AVRCP_PLAY_STATUS_STOPPED      0x00
+#define AVRCP_PLAY_STATUS_PLAYING      0x01
+#define AVRCP_PLAY_STATUS_PAUSED       0x02
+#define AVRCP_PLAY_STATUS_FWD_SEEK     0x03
+#define AVRCP_PLAY_STATUS_REV_SEEK     0x04
+#define AVRCP_PLAY_STATUS_ERROR                0xFF
+
+/* Notification events */
+#define AVRCP_EVENT_STATUS_CHANGED     0x01
+#define AVRCP_EVENT_TRACK_CHANGED      0x02
+#define AVRCP_EVENT_TRACK_REACHED_END  0x03
+#define AVRCP_EVENT_TRACK_REACHED_START        0x04
+#define AVRCP_EVENT_VOLUME_CHANGED     0x0d
+#define AVRCP_EVENT_LAST               AVRCP_EVENT_VOLUME_CHANGED
+
+struct avrcp_player_cb {
+       int (*get_setting) (uint8_t attr, void *user_data);
+       int (*set_setting) (uint8_t attr, uint8_t value, void *user_data);
+       uint64_t (*get_uid) (void *user_data);
+       void *(*get_metadata) (uint32_t id, void *user_data);
+       GList *(*list_metadata) (void *user_data);
+       uint8_t (*get_status) (void *user_data);
+       uint32_t (*get_position) (void *user_data);
+       void (*set_volume) (uint8_t volume, struct audio_device *dev,
+                                                       void *user_data);
+};
+
+int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
+void avrcp_unregister(const bdaddr_t *src);
+
+gboolean avrcp_connect(struct audio_device *dev);
+void avrcp_disconnect(struct audio_device *dev);
+int avrcp_set_volume(struct audio_device *dev, uint8_t volume);
+
+struct avrcp_player *avrcp_register_player(const bdaddr_t *src,
+                                               struct avrcp_player_cb *cb,
+                                               void *user_data,
+                                               GDestroyNotify destroy);
+void avrcp_unregister_player(struct avrcp_player *player);
+
+int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data);
+
+
+size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands);
diff --git a/audio/bluetooth.conf b/audio/bluetooth.conf
new file mode 100644 (file)
index 0000000..55b51e4
--- /dev/null
@@ -0,0 +1,36 @@
+# Please note that this ALSA configuration file fragment needs be enabled in
+# /etc/asound.conf or a similar configuration file with directives similar to
+# the following:
+#
+#@hooks [
+#      {
+#              func load
+#              files [
+#                      "/etc/alsa/bluetooth.conf"
+#              ]
+#              errors false
+#      }
+#]
+
+pcm.rawbluetooth {
+       @args [ ADDRESS ]
+       @args.ADDRESS {
+               type string
+       }
+       type bluetooth
+       device $ADDRESS
+}
+
+pcm.bluetooth {
+       @args [ ADDRESS ]
+       @args.ADDRESS {
+               type string
+       }
+       type plug
+       slave {
+               pcm {
+                       type bluetooth
+                       device $ADDRESS
+               }
+       }
+}
diff --git a/audio/control.c b/audio/control.c
new file mode 100644 (file)
index 0000000..c5a6a58
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  Texas Instruments, Inc.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "manager.h"
+#include "avctp.h"
+#include "control.h"
+#include "sdpd.h"
+#include "glib-helper.h"
+#include "dbus-common.h"
+
+static unsigned int avctp_id = 0;
+
+struct control {
+       struct audio_device *dev;
+       struct avctp *session;
+
+       gboolean target;
+};
+
+static void state_changed(struct audio_device *dev, avctp_state_t old_state,
+                               avctp_state_t new_state, void *user_data)
+{
+       struct control *control = dev->control;
+       gboolean value;
+
+       switch (new_state) {
+       case AVCTP_STATE_DISCONNECTED:
+               control->session = NULL;
+
+               if (old_state != AVCTP_STATE_CONNECTED)
+                       break;
+
+               value = FALSE;
+               g_dbus_emit_signal(dev->conn, dev->path,
+                                       AUDIO_CONTROL_INTERFACE,
+                                       "Disconnected", DBUS_TYPE_INVALID);
+               emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_CONTROL_INTERFACE, "Connected",
+                                       DBUS_TYPE_BOOLEAN, &value);
+
+               break;
+       case AVCTP_STATE_CONNECTING:
+               if (control->session)
+                       break;
+
+               control->session = avctp_get(&dev->src, &dev->dst);
+
+               break;
+       case AVCTP_STATE_CONNECTED:
+               value = TRUE;
+               g_dbus_emit_signal(dev->conn, dev->path,
+                               AUDIO_CONTROL_INTERFACE, "Connected",
+                               DBUS_TYPE_INVALID);
+               emit_property_changed(dev->conn, dev->path,
+                               AUDIO_CONTROL_INTERFACE, "Connected",
+                               DBUS_TYPE_BOOLEAN, &value);
+               break;
+       default:
+               return;
+       }
+}
+
+static DBusMessage *control_is_connected(DBusConnection *conn,
+                                               DBusMessage *msg,
+                                               void *data)
+{
+       struct audio_device *device = data;
+       struct control *control = device->control;
+       DBusMessage *reply;
+       dbus_bool_t connected;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       connected = (control->session != NULL);
+
+       dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
+                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct audio_device *device = data;
+       struct control *control = device->control;
+       int err;
+
+       if (!control->session)
+               return btd_error_not_connected(msg);
+
+       if (!control->target)
+               return btd_error_not_supported(msg);
+
+       err = avctp_send_passthrough(control->session, VOL_UP_OP);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *volume_down(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct audio_device *device = data;
+       struct control *control = device->control;
+       int err;
+
+       if (!control->session)
+               return btd_error_not_connected(msg);
+
+       if (!control->target)
+               return btd_error_not_supported(msg);
+
+       err = avctp_send_passthrough(control->session, VOL_DOWN_OP);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *control_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct audio_device *device = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       gboolean value;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       /* Connected */
+       value = (device->control->session != NULL);
+       dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static const GDBusMethodTable control_methods[] = {
+       { GDBUS_ASYNC_METHOD("IsConnected",
+                               NULL, GDBUS_ARGS({ "connected", "b" }),
+                               control_is_connected) },
+       { GDBUS_METHOD("GetProperties",
+                               NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                               control_get_properties) },
+       { GDBUS_METHOD("VolumeUp", NULL, NULL, volume_up) },
+       { GDBUS_METHOD("VolumeDown", NULL, NULL, volume_down) },
+       { }
+};
+
+static const GDBusSignalTable control_signals[] = {
+       { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) },
+       { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) },
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static void path_unregister(void *data)
+{
+       struct audio_device *dev = data;
+       struct control *control = dev->control;
+
+       DBG("Unregistered interface %s on path %s",
+               AUDIO_CONTROL_INTERFACE, dev->path);
+
+       if (control->session)
+               avctp_disconnect(control->session);
+
+       g_free(control);
+       dev->control = NULL;
+}
+
+void control_unregister(struct audio_device *dev)
+{
+       g_dbus_unregister_interface(dev->conn, dev->path,
+                                               AUDIO_CONTROL_INTERFACE);
+}
+
+void control_update(struct control *control, uint16_t uuid16)
+{
+       if (uuid16 == AV_REMOTE_TARGET_SVCLASS_ID)
+               control->target = TRUE;
+}
+
+struct control *control_init(struct audio_device *dev, uint16_t uuid16)
+{
+       struct control *control;
+
+       if (!g_dbus_register_interface(dev->conn, dev->path,
+                                       AUDIO_CONTROL_INTERFACE,
+                                       control_methods, control_signals, NULL,
+                                       dev, path_unregister))
+               return NULL;
+
+       DBG("Registered interface %s on path %s",
+               AUDIO_CONTROL_INTERFACE, dev->path);
+
+       control = g_new0(struct control, 1);
+       control->dev = dev;
+
+       control_update(control, uuid16);
+
+       if (!avctp_id)
+               avctp_id = avctp_add_state_cb(state_changed, NULL);
+
+       return control;
+}
+
+gboolean control_is_active(struct audio_device *dev)
+{
+       struct control *control = dev->control;
+
+       if (control && control->session)
+               return TRUE;
+
+       return FALSE;
+}
diff --git a/audio/control.h b/audio/control.h
new file mode 100644 (file)
index 0000000..2219e5f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define AUDIO_CONTROL_INTERFACE "org.bluez.Control"
+
+struct control *control_init(struct audio_device *dev, uint16_t uuid16);
+void control_update(struct control *control, uint16_t uuid16);
+void control_unregister(struct audio_device *dev);
+gboolean control_is_active(struct audio_device *dev);
diff --git a/audio/ctl_bluetooth.c b/audio/ctl_bluetooth.c
new file mode 100644 (file)
index 0000000..a16f476
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <alsa/asoundlib.h>
+#include <alsa/control_external.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "ipc.h"
+
+#ifdef ENABLE_DEBUG
+#define DBG(fmt, arg...)  printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
+#else
+#define DBG(fmt, arg...)
+#endif
+
+#define BLUETOOTH_MINVOL 0
+#define BLUETOOTH_MAXVOL 15
+
+struct bluetooth_data {
+       snd_ctl_ext_t ext;
+       int sock;
+};
+
+enum {
+       BLUETOOTH_PLAYBACK,
+       BLUETOOTH_CAPTURE,
+};
+
+static const char *vol_devices[2] = {
+       [BLUETOOTH_PLAYBACK]    = "Playback volume",
+       [BLUETOOTH_CAPTURE]     = "Capture volume",
+};
+
+static void bluetooth_exit(struct bluetooth_data *data)
+{
+       if (data == NULL)
+               return;
+
+       if (data->sock >= 0)
+               bt_audio_service_close(data->sock);
+
+       free(data);
+}
+
+static void bluetooth_close(snd_ctl_ext_t *ext)
+{
+       struct bluetooth_data *data = ext->private_data;
+
+       DBG("ext %p", ext);
+
+       bluetooth_exit(data);
+}
+
+static int bluetooth_elem_count(snd_ctl_ext_t *ext)
+{
+       DBG("ext %p", ext);
+
+       return 2;
+}
+
+static int bluetooth_elem_list(snd_ctl_ext_t *ext,
+                               unsigned int offset, snd_ctl_elem_id_t *id)
+{
+       DBG("ext %p offset %d id %p", ext, offset, id);
+
+       snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+
+       if (offset > 1)
+               return -EINVAL;
+
+       snd_ctl_elem_id_set_name(id, vol_devices[offset]);
+
+       return 0;
+}
+
+static snd_ctl_ext_key_t bluetooth_find_elem(snd_ctl_ext_t *ext,
+                                               const snd_ctl_elem_id_t *id)
+{
+       const char *name = snd_ctl_elem_id_get_name(id);
+       int i;
+
+       DBG("ext %p id %p name '%s'", ext, id, name);
+
+       for (i = 0; i < 2; i++)
+               if (strcmp(name, vol_devices[i]) == 0)
+                       return i;
+
+       return SND_CTL_EXT_KEY_NOT_FOUND;
+}
+
+static int bluetooth_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+                       int *type, unsigned int *acc, unsigned int *count)
+{
+       DBG("ext %p key %ld", ext, key);
+
+       *type = SND_CTL_ELEM_TYPE_INTEGER;
+       *acc = SND_CTL_EXT_ACCESS_READWRITE;
+       *count = 1;
+
+       return 0;
+}
+
+static int bluetooth_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+                                       long *imin, long *imax, long *istep)
+{
+       DBG("ext %p key %ld", ext, key);
+
+       *istep = 1;
+       *imin  = BLUETOOTH_MINVOL;
+       *imax  = BLUETOOTH_MAXVOL;
+
+       return 0;
+}
+
+static int bluetooth_send_ctl(struct bluetooth_data *data,
+                       uint8_t mode, uint8_t key, struct bt_control_rsp *rsp)
+{
+       int ret;
+       struct bt_control_req *req = (void *) rsp;
+       bt_audio_error_t *err = (void *) rsp;
+       const char *type, *name;
+
+       memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+       req->h.type = BT_REQUEST;
+       req->h.name = BT_CONTROL;
+       req->h.length = sizeof(*req);
+
+       req->mode = mode;
+       req->key = key;
+
+       ret = send(data->sock, req, BT_SUGGESTED_BUFFER_SIZE, MSG_NOSIGNAL);
+       if (ret <= 0) {
+               SYSERR("Unable to request new volume value to server");
+               return  -errno;
+       }
+
+       ret = recv(data->sock, rsp, BT_SUGGESTED_BUFFER_SIZE, 0);
+       if (ret <= 0) {
+               SNDERR("Unable to receive new volume value from server");
+               return  -errno;
+       }
+
+       if (rsp->h.type == BT_ERROR) {
+               SNDERR("BT_CONTROL failed : %s (%d)",
+                                       strerror(err->posix_errno),
+                                       err->posix_errno);
+               return -err->posix_errno;
+       }
+
+       type = bt_audio_strtype(rsp->h.type);
+       if (!type) {
+               SNDERR("Bogus message type %d "
+                               "received from audio service",
+                               rsp->h.type);
+               return -EINVAL;
+       }
+
+       name = bt_audio_strname(rsp->h.name);
+       if (!name) {
+               SNDERR("Bogus message name %d "
+                               "received from audio service",
+                               rsp->h.name);
+               return -EINVAL;
+       }
+
+       if (rsp->h.name != BT_CONTROL) {
+               SNDERR("Unexpected message %s received", type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+                                                               long *value)
+{
+       struct bluetooth_data *data = ext->private_data;
+       int ret;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_control_rsp *rsp = (void *) buf;
+
+       DBG("ext %p key %ld", ext, key);
+
+       memset(buf, 0, sizeof(buf));
+       *value = 0;
+
+       ret = bluetooth_send_ctl(data, key, 0, rsp);
+       if (ret == 0)
+               *value = rsp->key;
+
+       return ret;
+}
+
+static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key,
+                                                               long *value)
+{
+       struct bluetooth_data *data = ext->private_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_control_rsp *rsp = (void *) buf;
+       long current;
+       int ret, keyvalue;
+
+       DBG("ext %p key %ld", ext, key);
+
+       ret = bluetooth_read_integer(ext, key, &current);
+       if (ret < 0)
+               return ret;
+
+       if (*value == current)
+               return 0;
+
+       while (*value != current) {
+               keyvalue = (*value > current) ? BT_CONTROL_KEY_VOL_UP :
+                               BT_CONTROL_KEY_VOL_DOWN;
+
+               ret = bluetooth_send_ctl(data, key, keyvalue, rsp);
+               if (ret < 0)
+                       break;
+
+               current = keyvalue;
+       }
+
+       return ret;
+}
+
+static int bluetooth_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id,
+                                               unsigned int *event_mask)
+{
+       struct bluetooth_data *data = ext->private_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_control_ind *ind = (void *) buf;
+       int err;
+       const char *type, *name;
+
+       DBG("ext %p id %p", ext, id);
+
+       memset(buf, 0, sizeof(buf));
+
+       err = recv(data->sock, ind, BT_SUGGESTED_BUFFER_SIZE, MSG_DONTWAIT);
+       if (err < 0) {
+               err = -errno;
+               SNDERR("Failed while receiving data: %s (%d)", strerror(-err),
+                                                                       -err);
+               return err;
+       }
+
+       type = bt_audio_strtype(ind->h.type);
+       if (!type) {
+               SNDERR("Bogus message type %d "
+                               "received from audio service",
+                               ind->h.type);
+               return -EAGAIN;
+       }
+
+       name = bt_audio_strname(ind->h.name);
+       if (!name) {
+               SNDERR("Bogus message name %d "
+                               "received from audio service",
+                               ind->h.name);
+               return -EAGAIN;
+       }
+
+       if (ind->h.name != BT_CONTROL) {
+               SNDERR("Unexpected message %s received", name);
+               return -EAGAIN;
+       }
+
+       snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+       snd_ctl_elem_id_set_name(id, ind->mode == BLUETOOTH_PLAYBACK ?
+                               vol_devices[BLUETOOTH_PLAYBACK] :
+                               vol_devices[BLUETOOTH_CAPTURE]);
+       *event_mask = SND_CTL_EVENT_MASK_VALUE;
+
+       return 1;
+}
+
+static snd_ctl_ext_callback_t bluetooth_callback = {
+       .close                  = bluetooth_close,
+       .elem_count             = bluetooth_elem_count,
+       .elem_list              = bluetooth_elem_list,
+       .find_elem              = bluetooth_find_elem,
+       .get_attribute          = bluetooth_get_attribute,
+       .get_integer_info       = bluetooth_get_integer_info,
+       .read_integer           = bluetooth_read_integer,
+       .write_integer          = bluetooth_write_integer,
+       .read_event             = bluetooth_read_event,
+};
+
+static int bluetooth_init(struct bluetooth_data *data)
+{
+       int sk;
+
+       if (!data)
+               return -EINVAL;
+
+       memset(data, 0, sizeof(struct bluetooth_data));
+
+       data->sock = -1;
+
+       sk = bt_audio_service_open();
+       if (sk < 0)
+               return -errno;
+
+       data->sock = sk;
+
+       return 0;
+}
+
+SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth);
+
+SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth)
+{
+       struct bluetooth_data *data;
+       int err;
+
+       DBG("Bluetooth Control plugin");
+
+       data = malloc(sizeof(struct bluetooth_data));
+       if (!data) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       err = bluetooth_init(data);
+       if (err < 0)
+               goto error;
+
+       data->ext.version = SND_CTL_EXT_VERSION;
+       data->ext.card_idx = -1;
+
+       strncpy(data->ext.id, "bluetooth", sizeof(data->ext.id) - 1);
+       strncpy(data->ext.driver, "Bluetooth-Audio", sizeof(data->ext.driver) - 1);
+       strncpy(data->ext.name, "Bluetooth Audio", sizeof(data->ext.name) - 1);
+       strncpy(data->ext.longname, "Bluetooth Audio", sizeof(data->ext.longname) - 1);
+       strncpy(data->ext.mixername, "Bluetooth Audio", sizeof(data->ext.mixername) - 1);
+
+       data->ext.callback = &bluetooth_callback;
+       data->ext.poll_fd = data->sock;
+       data->ext.private_data = data;
+
+       err = snd_ctl_ext_create(&data->ext, name, mode);
+       if (err < 0)
+               goto error;
+
+       *handlep = data->ext.handle;
+
+       return 0;
+
+error:
+       bluetooth_exit(data);
+
+       return err;
+}
+
+SND_CTL_PLUGIN_SYMBOL(bluetooth);
diff --git a/audio/device.c b/audio/device.c
new file mode 100644 (file)
index 0000000..b7b993e
--- /dev/null
@@ -0,0 +1,872 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#include "error.h"
+#include "ipc.h"
+#include "dbus-common.h"
+#include "device.h"
+#include "unix.h"
+#include "avdtp.h"
+#include "control.h"
+#include "avctp.h"
+#include "avrcp.h"
+#include "headset.h"
+#include "gateway.h"
+#include "sink.h"
+#include "source.h"
+
+#define AUDIO_INTERFACE "org.bluez.Audio"
+
+#define CONTROL_CONNECT_TIMEOUT 2
+#define AVDTP_CONNECT_TIMEOUT 1
+#define AVDTP_CONNECT_TIMEOUT_BOOST 1
+#define HEADSET_CONNECT_TIMEOUT 1
+
+typedef enum {
+       AUDIO_STATE_DISCONNECTED,
+       AUDIO_STATE_CONNECTING,
+       AUDIO_STATE_CONNECTED,
+} audio_state_t;
+
+struct service_auth {
+       service_auth_cb cb;
+       void *user_data;
+};
+
+struct dev_priv {
+       audio_state_t state;
+
+       headset_state_t hs_state;
+       sink_state_t sink_state;
+       avctp_state_t avctp_state;
+       GSList *auths;
+
+       DBusMessage *conn_req;
+       DBusMessage *dc_req;
+
+       guint control_timer;
+       guint avdtp_timer;
+       guint headset_timer;
+       guint dc_id;
+
+       gboolean disconnecting;
+       gboolean authorized;
+       guint auth_idle_id;
+};
+
+static unsigned int sink_callback_id = 0;
+static unsigned int avctp_callback_id = 0;
+static unsigned int avdtp_callback_id = 0;
+static unsigned int headset_callback_id = 0;
+
+static void device_free(struct audio_device *dev)
+{
+       struct dev_priv *priv = dev->priv;
+
+       if (dev->conn)
+               dbus_connection_unref(dev->conn);
+
+       btd_device_unref(dev->btd_dev);
+
+       if (priv) {
+               if (priv->auths)
+                       audio_device_cancel_authorization(dev, NULL, NULL);
+               if (priv->control_timer)
+                       g_source_remove(priv->control_timer);
+               if (priv->avdtp_timer)
+                       g_source_remove(priv->avdtp_timer);
+               if (priv->headset_timer)
+                       g_source_remove(priv->headset_timer);
+               if (priv->dc_req)
+                       dbus_message_unref(priv->dc_req);
+               if (priv->conn_req)
+                       dbus_message_unref(priv->conn_req);
+               if (priv->dc_id)
+                       device_remove_disconnect_watch(dev->btd_dev,
+                                                       priv->dc_id);
+               g_free(priv);
+       }
+
+       g_free(dev->path);
+       g_free(dev);
+}
+
+static const char *state2str(audio_state_t state)
+{
+       switch (state) {
+       case AUDIO_STATE_DISCONNECTED:
+               return "disconnected";
+       case AUDIO_STATE_CONNECTING:
+               return "connecting";
+       case AUDIO_STATE_CONNECTED:
+               return "connected";
+       default:
+               error("Invalid audio state %d", state);
+               return NULL;
+       }
+}
+
+static gboolean control_connect_timeout(gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+
+       dev->priv->control_timer = 0;
+
+       if (dev->control)
+               avrcp_connect(dev);
+
+       return FALSE;
+}
+
+static gboolean device_set_control_timer(struct audio_device *dev)
+{
+       struct dev_priv *priv = dev->priv;
+
+       if (!dev->control)
+               return FALSE;
+
+       if (priv->control_timer)
+               return FALSE;
+
+       priv->control_timer = g_timeout_add_seconds(CONTROL_CONNECT_TIMEOUT,
+                                                       control_connect_timeout,
+                                                       dev);
+
+       return TRUE;
+}
+
+static void device_remove_control_timer(struct audio_device *dev)
+{
+       if (dev->priv->control_timer)
+               g_source_remove(dev->priv->control_timer);
+       dev->priv->control_timer = 0;
+}
+
+static void device_remove_avdtp_timer(struct audio_device *dev)
+{
+       if (dev->priv->avdtp_timer)
+               g_source_remove(dev->priv->avdtp_timer);
+       dev->priv->avdtp_timer = 0;
+}
+
+static void device_remove_headset_timer(struct audio_device *dev)
+{
+       if (dev->priv->headset_timer)
+               g_source_remove(dev->priv->headset_timer);
+       dev->priv->headset_timer = 0;
+}
+
+static void disconnect_cb(struct btd_device *btd_dev, gboolean removal,
+                               void *user_data)
+{
+       struct audio_device *dev = user_data;
+       struct dev_priv *priv = dev->priv;
+
+       if (priv->state == AUDIO_STATE_DISCONNECTED)
+               return;
+
+       if (priv->disconnecting)
+               return;
+
+       priv->disconnecting = TRUE;
+
+       device_remove_control_timer(dev);
+       device_remove_avdtp_timer(dev);
+       device_remove_headset_timer(dev);
+
+       if (dev->control)
+               avrcp_disconnect(dev);
+
+       if (dev->sink && priv->sink_state != SINK_STATE_DISCONNECTED)
+               sink_shutdown(dev->sink);
+       else if (priv->hs_state != HEADSET_STATE_DISCONNECTED)
+               headset_shutdown(dev);
+       else
+               priv->disconnecting = FALSE;
+}
+
+static void device_set_state(struct audio_device *dev, audio_state_t new_state)
+{
+       struct dev_priv *priv = dev->priv;
+       const char *state_str;
+       DBusMessage *reply = NULL;
+
+       state_str = state2str(new_state);
+       if (!state_str)
+               return;
+
+       if (new_state == AUDIO_STATE_DISCONNECTED) {
+               priv->authorized = FALSE;
+
+               if (priv->dc_id) {
+                       device_remove_disconnect_watch(dev->btd_dev,
+                                                       priv->dc_id);
+                       priv->dc_id = 0;
+               }
+       } else if (new_state == AUDIO_STATE_CONNECTING)
+               priv->dc_id = device_add_disconnect_watch(dev->btd_dev,
+                                               disconnect_cb, dev, NULL);
+
+       if (dev->priv->state == new_state) {
+               DBG("state change attempted from %s to %s",
+                                                       state_str, state_str);
+               return;
+       }
+
+       dev->priv->state = new_state;
+
+       if (new_state == AUDIO_STATE_DISCONNECTED) {
+               if (priv->dc_req) {
+                       reply = dbus_message_new_method_return(priv->dc_req);
+                       dbus_message_unref(priv->dc_req);
+                       priv->dc_req = NULL;
+                       g_dbus_send_message(dev->conn, reply);
+               }
+               priv->disconnecting = FALSE;
+       }
+
+       if (priv->conn_req && new_state != AUDIO_STATE_CONNECTING) {
+               if (new_state == AUDIO_STATE_CONNECTED)
+                       reply = dbus_message_new_method_return(priv->conn_req);
+               else
+                       reply = btd_error_failed(priv->conn_req,
+                                                       "Connect Failed");
+
+               dbus_message_unref(priv->conn_req);
+               priv->conn_req = NULL;
+               g_dbus_send_message(dev->conn, reply);
+       }
+
+       emit_property_changed(dev->conn, dev->path,
+                               AUDIO_INTERFACE, "State",
+                               DBUS_TYPE_STRING, &state_str);
+}
+
+static gboolean avdtp_connect_timeout(gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+
+       dev->priv->avdtp_timer = 0;
+
+       if (dev->sink) {
+               struct avdtp *session = avdtp_get(&dev->src, &dev->dst);
+
+               if (!session)
+                       return FALSE;
+
+               sink_setup_stream(dev->sink, session);
+               avdtp_unref(session);
+       }
+
+       return FALSE;
+}
+
+static gboolean device_set_avdtp_timer(struct audio_device *dev)
+{
+       struct dev_priv *priv = dev->priv;
+       guint timeout = AVDTP_CONNECT_TIMEOUT;
+
+       if (!dev->sink)
+               return FALSE;
+
+       if (priv->avdtp_timer)
+               return FALSE;
+
+       /* If the headset is the HSP/HFP RFCOMM initiator, give the headset
+        * time to initiate AVDTP signalling (and avoid further racing) */
+       if (dev->headset && headset_get_rfcomm_initiator(dev))
+               timeout += AVDTP_CONNECT_TIMEOUT_BOOST;
+
+       priv->avdtp_timer = g_timeout_add_seconds(timeout,
+                                                       avdtp_connect_timeout,
+                                                       dev);
+
+       return TRUE;
+}
+
+static gboolean headset_connect_timeout(gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+       struct dev_priv *priv = dev->priv;
+
+       dev->priv->headset_timer = 0;
+
+       if (dev->headset == NULL)
+               return FALSE;
+
+       if (headset_config_stream(dev, FALSE, NULL, NULL) == 0) {
+               if (priv->state != AUDIO_STATE_CONNECTED &&
+                               (priv->sink_state == SINK_STATE_CONNECTED ||
+                               priv->sink_state == SINK_STATE_PLAYING))
+                       device_set_state(dev, AUDIO_STATE_CONNECTED);
+       }
+
+       return FALSE;
+}
+
+static gboolean device_set_headset_timer(struct audio_device *dev)
+{
+       struct dev_priv *priv = dev->priv;
+
+       if (!dev->headset)
+               return FALSE;
+
+       if (priv->headset_timer)
+               return FALSE;
+
+       priv->headset_timer = g_timeout_add_seconds(HEADSET_CONNECT_TIMEOUT,
+                                               headset_connect_timeout, dev);
+
+       return TRUE;
+}
+
+static void device_avdtp_cb(struct audio_device *dev, struct avdtp *session,
+                               avdtp_session_state_t old_state,
+                               avdtp_session_state_t new_state,
+                               void *user_data)
+{
+       if (!dev->sink || !dev->control)
+               return;
+
+       if (new_state == AVDTP_SESSION_STATE_CONNECTED) {
+               if (avdtp_stream_setup_active(session))
+                       device_set_control_timer(dev);
+               else
+                       avrcp_connect(dev);
+       }
+}
+
+static void device_sink_cb(struct audio_device *dev,
+                               sink_state_t old_state,
+                               sink_state_t new_state,
+                               void *user_data)
+{
+       struct dev_priv *priv = dev->priv;
+
+       if (!dev->sink)
+               return;
+
+       priv->sink_state = new_state;
+
+       switch (new_state) {
+       case SINK_STATE_DISCONNECTED:
+               if (dev->control) {
+                       device_remove_control_timer(dev);
+                       avrcp_disconnect(dev);
+               }
+               if (priv->hs_state != HEADSET_STATE_DISCONNECTED &&
+                               (priv->dc_req || priv->disconnecting)) {
+                       headset_shutdown(dev);
+                       break;
+               }
+               if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
+                       device_set_state(dev, AUDIO_STATE_DISCONNECTED);
+               else if (old_state == SINK_STATE_CONNECTING) {
+                       switch (priv->hs_state) {
+                       case HEADSET_STATE_CONNECTED:
+                       case HEADSET_STATE_PLAY_IN_PROGRESS:
+                       case HEADSET_STATE_PLAYING:
+                               device_set_state(dev, AUDIO_STATE_CONNECTED);
+                       default:
+                               break;
+                       }
+               }
+               break;
+       case SINK_STATE_CONNECTING:
+               device_remove_avdtp_timer(dev);
+               if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
+                       device_set_state(dev, AUDIO_STATE_CONNECTING);
+               break;
+       case SINK_STATE_CONNECTED:
+               if (old_state == SINK_STATE_PLAYING)
+                       break;
+               if (dev->auto_connect) {
+                       if (!dev->headset)
+                               device_set_state(dev, AUDIO_STATE_CONNECTED);
+                       else if (priv->hs_state == HEADSET_STATE_DISCONNECTED)
+                               device_set_headset_timer(dev);
+                       else if (priv->hs_state == HEADSET_STATE_CONNECTED ||
+                                       priv->hs_state == HEADSET_STATE_PLAY_IN_PROGRESS ||
+                                       priv->hs_state == HEADSET_STATE_PLAYING)
+                               device_set_state(dev, AUDIO_STATE_CONNECTED);
+               } else if (priv->hs_state == HEADSET_STATE_DISCONNECTED ||
+                               priv->hs_state == HEADSET_STATE_CONNECTING)
+                       device_set_state(dev, AUDIO_STATE_CONNECTED);
+               break;
+       case SINK_STATE_PLAYING:
+               break;
+       }
+}
+
+static void device_avctp_cb(struct audio_device *dev,
+                               avctp_state_t old_state,
+                               avctp_state_t new_state,
+                               void *user_data)
+{
+       if (!dev->control)
+               return;
+
+       dev->priv->avctp_state = new_state;
+
+       switch (new_state) {
+       case AVCTP_STATE_DISCONNECTED:
+               break;
+       case AVCTP_STATE_CONNECTING:
+               device_remove_control_timer(dev);
+               break;
+       case AVCTP_STATE_CONNECTED:
+               break;
+       }
+}
+
+static void device_headset_cb(struct audio_device *dev,
+                               headset_state_t old_state,
+                               headset_state_t new_state,
+                               void *user_data)
+{
+       struct dev_priv *priv = dev->priv;
+
+       if (!dev->headset)
+               return;
+
+       priv->hs_state = new_state;
+
+       switch (new_state) {
+       case HEADSET_STATE_DISCONNECTED:
+               device_remove_avdtp_timer(dev);
+               if (priv->sink_state != SINK_STATE_DISCONNECTED && dev->sink &&
+                               (priv->dc_req || priv->disconnecting)) {
+                       sink_shutdown(dev->sink);
+                       break;
+               }
+               if (priv->sink_state == SINK_STATE_DISCONNECTED)
+                       device_set_state(dev, AUDIO_STATE_DISCONNECTED);
+               else if (old_state == HEADSET_STATE_CONNECTING &&
+                               (priv->sink_state == SINK_STATE_CONNECTED ||
+                               priv->sink_state == SINK_STATE_PLAYING))
+                       device_set_state(dev, AUDIO_STATE_CONNECTED);
+               break;
+       case HEADSET_STATE_CONNECTING:
+               device_remove_headset_timer(dev);
+               if (priv->sink_state == SINK_STATE_DISCONNECTED)
+                       device_set_state(dev, AUDIO_STATE_CONNECTING);
+               break;
+       case HEADSET_STATE_CONNECTED:
+               if (old_state == HEADSET_STATE_CONNECTED ||
+                               old_state == HEADSET_STATE_PLAY_IN_PROGRESS ||
+                               old_state == HEADSET_STATE_PLAYING)
+                       break;
+               if (dev->auto_connect) {
+                       if (!dev->sink)
+                               device_set_state(dev, AUDIO_STATE_CONNECTED);
+                       else if (priv->sink_state == SINK_STATE_DISCONNECTED)
+                               device_set_avdtp_timer(dev);
+                       else if (priv->sink_state == SINK_STATE_CONNECTED ||
+                                       priv->sink_state == SINK_STATE_PLAYING)
+                               device_set_state(dev, AUDIO_STATE_CONNECTED);
+               } else if (priv->sink_state == SINK_STATE_DISCONNECTED ||
+                               priv->sink_state == SINK_STATE_CONNECTING)
+                       device_set_state(dev, AUDIO_STATE_CONNECTED);
+               break;
+       case HEADSET_STATE_PLAY_IN_PROGRESS:
+               break;
+       case HEADSET_STATE_PLAYING:
+               break;
+       }
+}
+
+static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct audio_device *dev = data;
+       struct dev_priv *priv = dev->priv;
+
+       if (priv->state == AUDIO_STATE_CONNECTING)
+               return btd_error_in_progress(msg);
+       else if (priv->state == AUDIO_STATE_CONNECTED)
+               return btd_error_already_connected(msg);
+
+       dev->auto_connect = TRUE;
+
+       if (dev->headset)
+               headset_config_stream(dev, FALSE, NULL, NULL);
+
+       if (priv->state != AUDIO_STATE_CONNECTING && dev->sink) {
+               struct avdtp *session = avdtp_get(&dev->src, &dev->dst);
+
+               if (!session)
+                       return btd_error_failed(msg,
+                                       "Failed to get AVDTP session");
+
+               sink_setup_stream(dev->sink, session);
+               avdtp_unref(session);
+       }
+
+       /* The previous calls should cause a call to the state callback to
+        * indicate AUDIO_STATE_CONNECTING */
+       if (priv->state != AUDIO_STATE_CONNECTING)
+               return btd_error_failed(msg, "Connect Failed");
+
+       priv->conn_req = dbus_message_ref(msg);
+
+       return NULL;
+}
+
+static DBusMessage *dev_disconnect(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct audio_device *dev = data;
+       struct dev_priv *priv = dev->priv;
+
+       if (priv->state == AUDIO_STATE_DISCONNECTED)
+               return btd_error_not_connected(msg);
+
+       if (priv->dc_req)
+               return dbus_message_new_method_return(msg);
+
+       priv->dc_req = dbus_message_ref(msg);
+
+       if (dev->control) {
+               device_remove_control_timer(dev);
+               avrcp_disconnect(dev);
+       }
+
+       if (dev->sink && priv->sink_state != SINK_STATE_DISCONNECTED)
+               sink_shutdown(dev->sink);
+       else if (priv->hs_state != HEADSET_STATE_DISCONNECTED)
+               headset_shutdown(dev);
+       else {
+               dbus_message_unref(priv->dc_req);
+               priv->dc_req = NULL;
+               return dbus_message_new_method_return(msg);
+       }
+
+       return NULL;
+}
+
+static DBusMessage *dev_get_properties(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct audio_device *device = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *state;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       /* State */
+       state = state2str(device->priv->state);
+       if (state)
+               dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static const GDBusMethodTable dev_methods[] = {
+       { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) },
+       { GDBUS_METHOD("Disconnect", NULL, NULL, dev_disconnect) },
+       { GDBUS_METHOD("GetProperties",
+               NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+               dev_get_properties) },
+       { }
+};
+
+static const GDBusSignalTable dev_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+struct audio_device *audio_device_register(DBusConnection *conn,
+                                       struct btd_device *device,
+                                       const char *path, const bdaddr_t *src,
+                                       const bdaddr_t *dst)
+{
+       struct audio_device *dev;
+
+       if (!conn || !path)
+               return NULL;
+
+       dev = g_new0(struct audio_device, 1);
+
+       dev->btd_dev = btd_device_ref(device);
+       dev->path = g_strdup(path);
+       bacpy(&dev->dst, dst);
+       bacpy(&dev->src, src);
+       dev->conn = dbus_connection_ref(conn);
+       dev->priv = g_new0(struct dev_priv, 1);
+       dev->priv->state = AUDIO_STATE_DISCONNECTED;
+
+       if (!g_dbus_register_interface(dev->conn, dev->path,
+                                       AUDIO_INTERFACE,
+                                       dev_methods, dev_signals, NULL,
+                                       dev, NULL)) {
+               error("Unable to register %s on %s", AUDIO_INTERFACE,
+                                                               dev->path);
+               device_free(dev);
+               return NULL;
+       }
+
+       DBG("Registered interface %s on path %s", AUDIO_INTERFACE,
+                                                               dev->path);
+
+       if (sink_callback_id == 0)
+               sink_callback_id = sink_add_state_cb(device_sink_cb, NULL);
+
+       if (avdtp_callback_id == 0)
+               avdtp_callback_id = avdtp_add_state_cb(device_avdtp_cb, NULL);
+       if (avctp_callback_id == 0)
+               avctp_callback_id = avctp_add_state_cb(device_avctp_cb, NULL);
+
+       if (headset_callback_id == 0)
+               headset_callback_id = headset_add_state_cb(device_headset_cb,
+                                                                       NULL);
+
+       return dev;
+}
+
+gboolean audio_device_is_active(struct audio_device *dev,
+                                               const char *interface)
+{
+       if (!interface) {
+               if ((dev->sink || dev->source) &&
+                               avdtp_is_connected(&dev->src, &dev->dst))
+                       return TRUE;
+               if (dev->headset && headset_is_active(dev))
+                       return TRUE;
+       } else if (!strcmp(interface, AUDIO_SINK_INTERFACE) && dev->sink &&
+                               avdtp_is_connected(&dev->src, &dev->dst))
+               return TRUE;
+       else if (!strcmp(interface, AUDIO_SOURCE_INTERFACE) && dev->source &&
+                               avdtp_is_connected(&dev->src, &dev->dst))
+               return TRUE;
+       else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset &&
+                               headset_is_active(dev))
+               return TRUE;
+       else if (!strcmp(interface, AUDIO_CONTROL_INTERFACE) && dev->control &&
+                               control_is_active(dev))
+               return TRUE;
+       else if (!strcmp(interface, AUDIO_GATEWAY_INTERFACE) && dev->gateway &&
+                               gateway_is_active(dev))
+               return TRUE;
+
+       return FALSE;
+}
+
+void audio_device_unregister(struct audio_device *device)
+{
+       unix_device_removed(device);
+
+       if (device->hs_preauth_id) {
+               g_source_remove(device->hs_preauth_id);
+               device->hs_preauth_id = 0;
+       }
+
+       if (device->headset)
+               headset_unregister(device);
+
+       if (device->gateway)
+               gateway_unregister(device);
+
+       if (device->sink)
+               sink_unregister(device);
+
+       if (device->source)
+               source_unregister(device);
+
+       if (device->control)
+               control_unregister(device);
+
+       g_dbus_unregister_interface(device->conn, device->path,
+                                               AUDIO_INTERFACE);
+
+       device_free(device);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+       struct audio_device *dev = user_data;
+       struct dev_priv *priv = dev->priv;
+
+       if (derr == NULL)
+               priv->authorized = TRUE;
+
+       while (priv->auths) {
+               struct service_auth *auth = priv->auths->data;
+
+               auth->cb(derr, auth->user_data);
+               priv->auths = g_slist_remove(priv->auths, auth);
+               g_free(auth);
+       }
+}
+
+static gboolean auth_idle_cb(gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+       struct dev_priv *priv = dev->priv;
+
+       priv->auth_idle_id = 0;
+
+       auth_cb(NULL, dev);
+
+       return FALSE;
+}
+
+static gboolean audio_device_is_connected(struct audio_device *dev)
+{
+       if (dev->headset) {
+               headset_state_t state = headset_get_state(dev);
+
+               if (state == HEADSET_STATE_CONNECTED ||
+                               state == HEADSET_STATE_PLAY_IN_PROGRESS ||
+                               state == HEADSET_STATE_PLAYING)
+                       return TRUE;
+       }
+
+       if (dev->sink) {
+               sink_state_t state = sink_get_state(dev);
+
+               if (state == SINK_STATE_CONNECTED ||
+                               state == SINK_STATE_PLAYING)
+                       return TRUE;
+       }
+
+       if (dev->source) {
+               source_state_t state = source_get_state(dev);
+
+               if (state == SOURCE_STATE_CONNECTED ||
+                               state == SOURCE_STATE_PLAYING)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+int audio_device_request_authorization(struct audio_device *dev,
+                                       const char *uuid, service_auth_cb cb,
+                                       void *user_data)
+{
+       struct dev_priv *priv = dev->priv;
+       struct service_auth *auth;
+       int err;
+
+       auth = g_try_new0(struct service_auth, 1);
+       if (!auth)
+               return -ENOMEM;
+
+       auth->cb = cb;
+       auth->user_data = user_data;
+
+       priv->auths = g_slist_append(priv->auths, auth);
+       if (g_slist_length(priv->auths) > 1)
+               return 0;
+
+       if (priv->authorized || audio_device_is_connected(dev)) {
+               priv->auth_idle_id = g_idle_add(auth_idle_cb, dev);
+               return 0;
+       }
+
+       err = btd_request_authorization(&dev->src, &dev->dst, uuid, auth_cb,
+                                       dev);
+       if (err < 0) {
+               priv->auths = g_slist_remove(priv->auths, auth);
+               g_free(auth);
+       }
+
+       return err;
+}
+
+int audio_device_cancel_authorization(struct audio_device *dev,
+                                       authorization_cb cb, void *user_data)
+{
+       struct dev_priv *priv = dev->priv;
+       GSList *l, *next;
+
+       for (l = priv->auths; l != NULL; l = next) {
+               struct service_auth *auth = l->data;
+
+               next = g_slist_next(l);
+
+               if (cb && auth->cb != cb)
+                       continue;
+
+               if (user_data && auth->user_data != user_data)
+                       continue;
+
+               priv->auths = g_slist_remove(priv->auths, auth);
+               g_free(auth);
+       }
+
+       if (g_slist_length(priv->auths) == 0) {
+               if (priv->auth_idle_id > 0) {
+                       g_source_remove(priv->auth_idle_id);
+                       priv->auth_idle_id = 0;
+               } else
+                       btd_cancel_authorization(&dev->src, &dev->dst);
+       }
+
+       return 0;
+}
+
+void audio_device_set_authorized(struct audio_device *dev, gboolean auth)
+{
+       struct dev_priv *priv = dev->priv;
+
+       priv->authorized = auth;
+}
diff --git a/audio/device.h b/audio/device.h
new file mode 100644 (file)
index 0000000..75f1da9
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct source;
+struct control;
+struct target;
+struct sink;
+struct headset;
+struct gateway;
+struct dev_priv;
+
+struct audio_device {
+       struct btd_device *btd_dev;
+
+       DBusConnection *conn;
+       char *path;
+       bdaddr_t src;
+       bdaddr_t dst;
+
+       gboolean auto_connect;
+
+       struct headset *headset;
+       struct gateway *gateway;
+       struct sink *sink;
+       struct source *source;
+       struct control *control;
+       struct target *target;
+
+       guint hs_preauth_id;
+
+       struct dev_priv *priv;
+};
+
+struct audio_device *audio_device_register(DBusConnection *conn,
+                                       struct btd_device *device,
+                                       const char *path, const bdaddr_t *src,
+                                       const bdaddr_t *dst);
+
+void audio_device_unregister(struct audio_device *device);
+
+gboolean audio_device_is_active(struct audio_device *dev,
+                                               const char *interface);
+
+typedef void (*authorization_cb) (DBusError *derr, void *user_data);
+
+int audio_device_cancel_authorization(struct audio_device *dev,
+                                       authorization_cb cb, void *user_data);
+
+int audio_device_request_authorization(struct audio_device *dev,
+                                       const char *uuid, authorization_cb cb,
+                                       void *user_data);
+
+void audio_device_set_authorized(struct audio_device *dev, gboolean auth);
diff --git a/audio/gateway.c b/audio/gateway.c
new file mode 100644 (file)
index 0000000..6162948
--- /dev/null
@@ -0,0 +1,997 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2008-2009  Leonid Movshovich <event.riga@gmail.org>
+ *  Copyright (C) 2010  ProFUSION embedded systems
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "sdp-client.h"
+#include "device.h"
+#include "gateway.h"
+#include "log.h"
+#include "error.h"
+#include "btio.h"
+#include "dbus-common.h"
+
+struct hf_agent {
+       char *name;     /* Bus id */
+       char *path;     /* D-Bus path */
+       guint watch;    /* Disconnect watch */
+};
+
+struct connect_cb {
+       unsigned int id;
+       gateway_stream_cb_t cb;
+       void *cb_data;
+};
+
+struct gateway {
+       gateway_state_t state;
+       GIOChannel *rfcomm;
+       GIOChannel *sco;
+       GIOChannel *incoming;
+       GSList *callbacks;
+       struct hf_agent *agent;
+       DBusMessage *msg;
+       int version;
+       gateway_lock_t lock;
+};
+
+struct gateway_state_callback {
+       gateway_state_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+static GSList *gateway_callbacks = NULL;
+
+int gateway_close(struct audio_device *device);
+
+GQuark gateway_error_quark(void)
+{
+       return g_quark_from_static_string("gateway-error-quark");
+}
+
+static const char *state2str(gateway_state_t state)
+{
+       switch (state) {
+       case GATEWAY_STATE_DISCONNECTED:
+               return "disconnected";
+       case GATEWAY_STATE_CONNECTING:
+               return "connecting";
+       case GATEWAY_STATE_CONNECTED:
+               return "connected";
+       case GATEWAY_STATE_PLAYING:
+               return "playing";
+       default:
+               return "";
+       }
+}
+
+static void agent_free(struct hf_agent *agent)
+{
+       if (!agent)
+               return;
+
+       g_free(agent->name);
+       g_free(agent->path);
+       g_free(agent);
+}
+
+static void change_state(struct audio_device *dev, gateway_state_t new_state)
+{
+       struct gateway *gw = dev->gateway;
+       const char *val;
+       GSList *l;
+       gateway_state_t old_state;
+
+       if (gw->state == new_state)
+               return;
+
+       val = state2str(new_state);
+       old_state = gw->state;
+       gw->state = new_state;
+
+       emit_property_changed(dev->conn, dev->path,
+                       AUDIO_GATEWAY_INTERFACE, "State",
+                       DBUS_TYPE_STRING, &val);
+
+       for (l = gateway_callbacks; l != NULL; l = l->next) {
+               struct gateway_state_callback *cb = l->data;
+               cb->cb(dev, old_state, new_state, cb->user_data);
+       }
+}
+
+void gateway_set_state(struct audio_device *dev, gateway_state_t new_state)
+{
+       switch (new_state) {
+       case GATEWAY_STATE_DISCONNECTED:
+               gateway_close(dev);
+               break;
+       case GATEWAY_STATE_CONNECTING:
+       case GATEWAY_STATE_CONNECTED:
+       case GATEWAY_STATE_PLAYING:
+               break;
+       }
+}
+
+static void agent_disconnect(struct audio_device *dev, struct hf_agent *agent)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(agent->name, agent->path,
+                       "org.bluez.HandsfreeAgent", "Release");
+
+       g_dbus_send_message(dev->conn, msg);
+}
+
+static gboolean agent_sendfd(struct hf_agent *agent, int fd,
+               DBusPendingCallNotifyFunction notify, void *data)
+{
+       struct audio_device *dev = data;
+       struct gateway *gw = dev->gateway;
+       DBusMessage *msg;
+       DBusPendingCall *call;
+
+       msg = dbus_message_new_method_call(agent->name, agent->path,
+                       "org.bluez.HandsfreeAgent", "NewConnection");
+
+       dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd,
+                                       DBUS_TYPE_UINT16, &gw->version,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(dev->conn, msg,
+                                                       &call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               return FALSE;
+       }
+
+       dbus_pending_call_set_notify(call, notify, dev, NULL);
+       dbus_pending_call_unref(call);
+       dbus_message_unref(msg);
+
+       return TRUE;
+}
+
+static unsigned int connect_cb_new(struct gateway *gw,
+                                       gateway_stream_cb_t func,
+                                       void *user_data)
+{
+       struct connect_cb *cb;
+       static unsigned int free_cb_id = 1;
+
+       if (!func)
+               return 0;
+
+       cb = g_new(struct connect_cb, 1);
+
+       cb->cb = func;
+       cb->cb_data = user_data;
+       cb->id = free_cb_id++;
+
+       gw->callbacks = g_slist_append(gw->callbacks, cb);
+
+       return cb->id;
+}
+
+static void run_connect_cb(struct audio_device *dev, GError *err)
+{
+       struct gateway *gw = dev->gateway;
+       GSList *l;
+
+       for (l = gw->callbacks; l != NULL; l = l->next) {
+               struct connect_cb *cb = l->data;
+               cb->cb(dev, err, cb->cb_data);
+       }
+
+       g_slist_free_full(gw->callbacks, g_free);
+       gw->callbacks = NULL;
+}
+
+static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond,
+                       struct audio_device *dev)
+{
+       struct gateway *gw = dev->gateway;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       DBG("sco connection is released");
+       g_io_channel_shutdown(gw->sco, TRUE, NULL);
+       g_io_channel_unref(gw->sco);
+       gw->sco = NULL;
+       change_state(dev, GATEWAY_STATE_CONNECTED);
+
+       return FALSE;
+}
+
+static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+       struct audio_device *dev = (struct audio_device *) user_data;
+       struct gateway *gw = dev->gateway;
+
+       DBG("at the begin of sco_connect_cb() in gateway.c");
+
+       gw->sco = g_io_channel_ref(chan);
+
+       if (err) {
+               error("sco_connect_cb(): %s", err->message);
+               gateway_suspend_stream(dev);
+               return;
+       }
+
+       g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                               (GIOFunc) sco_io_cb, dev);
+
+       change_state(dev, GATEWAY_STATE_PLAYING);
+       run_connect_cb(dev, NULL);
+}
+
+static gboolean rfcomm_disconnect_cb(GIOChannel *chan, GIOCondition cond,
+                       struct audio_device *dev)
+{
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       gateway_close(dev);
+
+       return FALSE;
+}
+
+static void newconnection_reply(DBusPendingCall *call, void *data)
+{
+       struct audio_device *dev = data;
+       struct gateway *gw = dev->gateway;
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError derr;
+
+       if (!dev->gateway->rfcomm) {
+               DBG("RFCOMM disconnected from server before agent reply");
+               goto done;
+       }
+
+       dbus_error_init(&derr);
+       if (!dbus_set_error_from_message(&derr, reply)) {
+               DBG("Agent reply: file descriptor passed successfully");
+               g_io_add_watch(gw->rfcomm, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       (GIOFunc) rfcomm_disconnect_cb, dev);
+               change_state(dev, GATEWAY_STATE_CONNECTED);
+               goto done;
+       }
+
+       DBG("Agent reply: %s", derr.message);
+
+       dbus_error_free(&derr);
+       gateway_close(dev);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void rfcomm_connect_cb(GIOChannel *chan, GError *err,
+                               gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+       struct gateway *gw = dev->gateway;
+       DBusMessage *reply;
+       int sk, ret;
+
+       if (err) {
+               error("connect(): %s", err->message);
+               goto fail;
+       }
+
+       if (!gw->agent) {
+               error("Handsfree Agent not registered");
+               goto fail;
+       }
+
+       sk = g_io_channel_unix_get_fd(chan);
+
+       if (gw->rfcomm == NULL)
+               gw->rfcomm = g_io_channel_ref(chan);
+
+       ret = agent_sendfd(gw->agent, sk, newconnection_reply, dev);
+
+       if (!gw->msg)
+               return;
+
+       if (ret)
+               reply = dbus_message_new_method_return(gw->msg);
+       else
+               reply = btd_error_failed(gw->msg, "Can't pass file descriptor");
+
+       g_dbus_send_message(dev->conn, reply);
+
+       return;
+
+fail:
+       if (gw->msg) {
+               DBusMessage *reply;
+               reply = btd_error_failed(gw->msg, "Connect failed");
+               g_dbus_send_message(dev->conn, reply);
+       }
+
+       gateway_close(dev);
+}
+
+static int get_remote_profile_version(sdp_record_t *rec)
+{
+       uuid_t uuid;
+       sdp_list_t *profiles;
+       sdp_profile_desc_t *desc;
+       int ver = 0;
+
+       sdp_uuid16_create(&uuid, HANDSFREE_PROFILE_ID);
+
+       sdp_get_profile_descs(rec, &profiles);
+       if (profiles == NULL)
+               goto done;
+
+       desc = profiles->data;
+
+       if (sdp_uuid16_cmp(&desc->uuid, &uuid) == 0)
+               ver = desc->version;
+
+       sdp_list_free(profiles, free);
+
+done:
+       return ver;
+}
+
+static void get_incoming_record_cb(sdp_list_t *recs, int err,
+                                       gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+       struct gateway *gw = dev->gateway;
+       GError *gerr = NULL;
+
+       if (err < 0) {
+               error("Unable to get service record: %s (%d)", strerror(-err),
+                                       -err);
+               goto fail;
+       }
+
+       if (!recs || !recs->data) {
+               error("No records found");
+               goto fail;
+       }
+
+       gw->version = get_remote_profile_version(recs->data);
+       if (gw->version == 0)
+               goto fail;
+
+       rfcomm_connect_cb(gw->incoming, gerr, dev);
+       return;
+
+fail:
+       gateway_close(dev);
+}
+
+static void unregister_incoming(gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+       struct gateway *gw = dev->gateway;
+
+       if (gw->incoming) {
+               g_io_channel_unref(gw->incoming);
+               gw->incoming = NULL;
+       }
+}
+
+static void rfcomm_incoming_cb(GIOChannel *chan, GError *err,
+                               gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+       struct gateway *gw = dev->gateway;
+       uuid_t uuid;
+
+       gw->incoming = g_io_channel_ref(chan);
+
+       sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID);
+       if (bt_search_service(&dev->src, &dev->dst, &uuid,
+                                               get_incoming_record_cb, dev,
+                                               unregister_incoming) == 0)
+               return;
+
+       unregister_incoming(dev);
+       gateway_close(dev);
+}
+
+static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+       struct gateway *gw = dev->gateway;
+       int ch;
+       sdp_list_t *protos, *classes;
+       uuid_t uuid;
+       GIOChannel *io;
+       GError *gerr = NULL;
+
+       if (err < 0) {
+               error("Unable to get service record: %s (%d)", strerror(-err),
+                                       -err);
+               goto fail;
+       }
+
+       if (!recs || !recs->data) {
+               error("No records found");
+               err = -EIO;
+               goto fail;
+       }
+
+       if (sdp_get_service_classes(recs->data, &classes) < 0) {
+               error("Unable to get service classes from record");
+               err = -EINVAL;
+               goto fail;
+       }
+
+       if (sdp_get_access_protos(recs->data, &protos) < 0) {
+               error("Unable to get access protocols from record");
+               err = -ENODATA;
+               goto fail;
+       }
+
+       gw->version = get_remote_profile_version(recs->data);
+       if (gw->version == 0) {
+               error("Unable to get profile version from record");
+               err = -EINVAL;
+               goto fail;
+       }
+
+       memcpy(&uuid, classes->data, sizeof(uuid));
+       sdp_list_free(classes, free);
+
+       if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
+                       uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) {
+               sdp_list_free(protos, NULL);
+               error("Invalid service record or not HFP");
+               err = -EIO;
+               goto fail;
+       }
+
+       ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+       sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+       sdp_list_free(protos, NULL);
+       if (ch <= 0) {
+               error("Unable to extract RFCOMM channel from service record");
+               err = -EIO;
+               goto fail;
+       }
+
+       io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &dev->src,
+                               BT_IO_OPT_DEST_BDADDR, &dev->dst,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_CHANNEL, ch,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("Unable to connect: %s", gerr->message);
+               goto fail;
+       }
+
+       g_io_channel_unref(io);
+       return;
+
+fail:
+       if (gw->msg) {
+               DBusMessage *reply = btd_error_failed(gw->msg,
+                                       gerr ? gerr->message : strerror(-err));
+               g_dbus_send_message(dev->conn, reply);
+       }
+
+       gateway_close(dev);
+
+       if (gerr)
+               g_error_free(gerr);
+}
+
+static int get_records(struct audio_device *device)
+{
+       uuid_t uuid;
+
+       change_state(device, GATEWAY_STATE_CONNECTING);
+       sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID);
+       return bt_search_service(&device->src, &device->dst, &uuid,
+                               get_record_cb, device, NULL);
+}
+
+static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg,
+                               void *data)
+{
+       struct audio_device *au_dev = (struct audio_device *) data;
+       struct gateway *gw = au_dev->gateway;
+       int err;
+
+       if (!gw->agent)
+               return btd_error_agent_not_available(msg);
+
+       err = get_records(au_dev);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       gw->msg = dbus_message_ref(msg);
+
+       return NULL;
+}
+
+int gateway_close(struct audio_device *device)
+{
+       GError *gerr = NULL;
+       struct gateway *gw = device->gateway;
+       int sock;
+
+       if (gw->rfcomm) {
+               sock = g_io_channel_unix_get_fd(gw->rfcomm);
+               shutdown(sock, SHUT_RDWR);
+
+               g_io_channel_shutdown(gw->rfcomm, TRUE, NULL);
+               g_io_channel_unref(gw->rfcomm);
+               gw->rfcomm = NULL;
+       }
+
+       if (gw->sco) {
+               g_io_channel_shutdown(gw->sco, TRUE, NULL);
+               g_io_channel_unref(gw->sco);
+               gw->sco = NULL;
+       }
+
+       change_state(device, GATEWAY_STATE_DISCONNECTED);
+       g_set_error(&gerr, GATEWAY_ERROR,
+                       GATEWAY_ERROR_DISCONNECTED, "Disconnected");
+       run_connect_cb(device, gerr);
+       g_error_free(gerr);
+
+       return 0;
+}
+
+static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct gateway *gw = device->gateway;
+       DBusMessage *reply = NULL;
+       char gw_addr[18];
+
+       if (!device->conn)
+               return NULL;
+
+       if (!gw->rfcomm)
+               return btd_error_not_connected(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       gateway_close(device);
+       ba2str(&device->dst, gw_addr);
+       DBG("Disconnected from %s, %s", gw_addr, device->path);
+
+       return reply;
+}
+
+static void agent_exited(DBusConnection *conn, void *data)
+{
+       struct gateway *gateway = data;
+       struct hf_agent *agent = gateway->agent;
+
+       DBG("Agent %s exited", agent->name);
+
+       agent_free(agent);
+       gateway->agent = NULL;
+}
+
+static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct gateway *gw = device->gateway;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *value;
+
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       value = state2str(gw->state);
+       dict_append_entry(&dict, "State",
+                       DBUS_TYPE_STRING, &value);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct audio_device *device = data;
+       struct gateway *gw = device->gateway;
+       struct hf_agent *agent;
+       const char *path, *name;
+
+       if (gw->agent)
+               return btd_error_already_exists(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       name = dbus_message_get_sender(msg);
+       agent = g_new0(struct hf_agent, 1);
+
+       agent->name = g_strdup(name);
+       agent->path = g_strdup(path);
+
+       agent->watch = g_dbus_add_disconnect_watch(conn, name,
+                                               agent_exited, gw, NULL);
+
+       gw->agent = agent;
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct audio_device *device = data;
+       struct gateway *gw = device->gateway;
+       const char *path;
+
+       if (!gw->agent)
+               goto done;
+
+       if (strcmp(gw->agent->name, dbus_message_get_sender(msg)) != 0)
+               return btd_error_not_authorized(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (strcmp(gw->agent->path, path) != 0)
+               return btd_error_does_not_exist(msg);
+
+       g_dbus_remove_watch(device->conn, gw->agent->watch);
+
+       agent_free(gw->agent);
+       gw->agent = NULL;
+
+done:
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable gateway_methods[] = {
+       { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, ag_connect) },
+       { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, ag_disconnect) },
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       ag_get_properties) },
+       { GDBUS_METHOD("RegisterAgent",
+                       GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) },
+       { GDBUS_METHOD("UnregisterAgent",
+                       GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) },
+       { }
+};
+
+static const GDBusSignalTable gateway_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static void path_unregister(void *data)
+{
+       struct audio_device *dev = data;
+
+       DBG("Unregistered interface %s on path %s",
+               AUDIO_GATEWAY_INTERFACE, dev->path);
+
+       gateway_close(dev);
+
+       g_free(dev->gateway);
+       dev->gateway = NULL;
+}
+
+void gateway_unregister(struct audio_device *dev)
+{
+       if (dev->gateway->agent)
+               agent_disconnect(dev, dev->gateway->agent);
+
+       g_dbus_unregister_interface(dev->conn, dev->path,
+                                               AUDIO_GATEWAY_INTERFACE);
+}
+
+struct gateway *gateway_init(struct audio_device *dev)
+{
+       if (!g_dbus_register_interface(dev->conn, dev->path,
+                                       AUDIO_GATEWAY_INTERFACE,
+                                       gateway_methods, gateway_signals,
+                                       NULL, dev, path_unregister))
+               return NULL;
+
+       return g_new0(struct gateway, 1);
+}
+
+gboolean gateway_is_connected(struct audio_device *dev)
+{
+       struct gateway *gw = dev->gateway;
+
+       if (gw->state == GATEWAY_STATE_CONNECTED)
+               return TRUE;
+
+       return FALSE;
+}
+
+gboolean gateway_is_active(struct audio_device *dev)
+{
+       struct gateway *gw = dev->gateway;
+
+       if (gw->state != GATEWAY_STATE_DISCONNECTED)
+               return TRUE;
+
+       return FALSE;
+}
+
+int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
+{
+       if (!io)
+               return -EINVAL;
+
+       dev->gateway->rfcomm = g_io_channel_ref(io);
+
+       change_state(dev, GATEWAY_STATE_CONNECTING);
+
+       return 0;
+}
+
+int gateway_connect_sco(struct audio_device *dev, GIOChannel *io)
+{
+       struct gateway *gw = dev->gateway;
+
+       if (gw->sco)
+               return -EISCONN;
+
+       gw->sco = g_io_channel_ref(io);
+
+       g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                               (GIOFunc) sco_io_cb, dev);
+
+       change_state(dev, GATEWAY_STATE_PLAYING);
+
+       return 0;
+}
+
+void gateway_start_service(struct audio_device *dev)
+{
+       struct gateway *gw = dev->gateway;
+       GError *err = NULL;
+
+       if (gw->rfcomm == NULL)
+               return;
+
+       if (!bt_io_accept(gw->rfcomm, rfcomm_incoming_cb, dev, NULL, &err)) {
+               error("bt_io_accept: %s", err->message);
+               g_error_free(err);
+               gateway_close(dev);
+       }
+}
+
+static gboolean request_stream_cb(gpointer data)
+{
+       run_connect_cb(data, NULL);
+       return FALSE;
+}
+
+/* These are functions to be called from unix.c for audio system
+ * ifaces (alsa, gstreamer, etc.) */
+unsigned int gateway_request_stream(struct audio_device *dev,
+                               gateway_stream_cb_t cb, void *user_data)
+{
+       struct gateway *gw = dev->gateway;
+       GError *err = NULL;
+       GIOChannel *io;
+
+       if (!gw->rfcomm)
+               get_records(dev);
+       else if (!gw->sco) {
+               io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &dev->src,
+                               BT_IO_OPT_DEST_BDADDR, &dev->dst,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_INVALID);
+               if (!io) {
+                       error("%s", err->message);
+                       g_error_free(err);
+                       return 0;
+               }
+       } else
+               g_idle_add(request_stream_cb, dev);
+
+       return connect_cb_new(gw, cb, user_data);
+}
+
+int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t cb,
+                               void *user_data)
+{
+       struct gateway *gw = dev->gateway;
+       unsigned int id;
+
+       id = connect_cb_new(gw, cb, user_data);
+
+       if (!gw->rfcomm)
+               get_records(dev);
+       else if (cb)
+               g_idle_add(request_stream_cb, dev);
+
+       return id;
+}
+
+gboolean gateway_cancel_stream(struct audio_device *dev, unsigned int id)
+{
+       struct gateway *gw = dev->gateway;
+       GSList *l;
+       struct connect_cb *cb = NULL;
+
+       for (l = gw->callbacks; l != NULL; l = l->next) {
+               struct connect_cb *tmp = l->data;
+
+               if (tmp->id == id) {
+                       cb = tmp;
+                       break;
+               }
+       }
+
+       if (!cb)
+               return FALSE;
+
+       gw->callbacks = g_slist_remove(gw->callbacks, cb);
+       g_free(cb);
+
+       gateway_suspend_stream(dev);
+
+       return TRUE;
+}
+
+int gateway_get_sco_fd(struct audio_device *dev)
+{
+       struct gateway *gw = dev->gateway;
+
+       if (!gw || !gw->sco)
+               return -1;
+
+       return g_io_channel_unix_get_fd(gw->sco);
+}
+
+void gateway_suspend_stream(struct audio_device *dev)
+{
+       GError *gerr = NULL;
+       struct gateway *gw = dev->gateway;
+
+       if (!gw || !gw->sco)
+               return;
+
+       g_io_channel_shutdown(gw->sco, TRUE, NULL);
+       g_io_channel_unref(gw->sco);
+       gw->sco = NULL;
+       g_set_error(&gerr, GATEWAY_ERROR, GATEWAY_ERROR_SUSPENDED, "Suspended");
+       run_connect_cb(dev, gerr);
+       g_error_free(gerr);
+       change_state(dev, GATEWAY_STATE_CONNECTED);
+}
+
+unsigned int gateway_add_state_cb(gateway_state_cb cb, void *user_data)
+{
+       struct gateway_state_callback *state_cb;
+       static unsigned int id = 0;
+
+       state_cb = g_new(struct gateway_state_callback, 1);
+       state_cb->cb = cb;
+       state_cb->user_data = user_data;
+       state_cb->id = ++id;
+
+       gateway_callbacks = g_slist_append(gateway_callbacks, state_cb);
+
+       return state_cb->id;
+}
+
+gboolean gateway_remove_state_cb(unsigned int id)
+{
+       GSList *l;
+
+       for (l = gateway_callbacks; l != NULL; l = l->next) {
+               struct gateway_state_callback *cb = l->data;
+               if (cb && cb->id == id) {
+                       gateway_callbacks = g_slist_remove(gateway_callbacks,
+                                                                       cb);
+                       g_free(cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+gateway_lock_t gateway_get_lock(struct audio_device *dev)
+{
+       struct gateway *gw = dev->gateway;
+
+       return gw->lock;
+}
+
+gboolean gateway_lock(struct audio_device *dev, gateway_lock_t lock)
+{
+       struct gateway *gw = dev->gateway;
+
+       if (gw->lock & lock)
+               return FALSE;
+
+       gw->lock |= lock;
+
+       return TRUE;
+}
+
+gboolean gateway_unlock(struct audio_device *dev, gateway_lock_t lock)
+{
+       struct gateway *gw = dev->gateway;
+
+       if (!(gw->lock & lock))
+               return FALSE;
+
+       gw->lock &= ~lock;
+
+       if (gw->lock)
+               return TRUE;
+
+       if (gw->state == GATEWAY_STATE_PLAYING)
+               gateway_suspend_stream(dev);
+
+       return TRUE;
+}
diff --git a/audio/gateway.h b/audio/gateway.h
new file mode 100644 (file)
index 0000000..77f5787
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway"
+
+#define DEFAULT_HFP_HS_CHANNEL 7
+
+typedef enum {
+       GATEWAY_STATE_DISCONNECTED,
+       GATEWAY_STATE_CONNECTING,
+       GATEWAY_STATE_CONNECTED,
+       GATEWAY_STATE_PLAYING,
+} gateway_state_t;
+
+typedef enum {
+       GATEWAY_LOCK_READ = 1,
+       GATEWAY_LOCK_WRITE = 1 << 1,
+} gateway_lock_t;
+
+typedef enum {
+       GATEWAY_ERROR_DISCONNECTED,
+       GATEWAY_ERROR_SUSPENDED,
+} gateway_error_t;
+
+#define GATEWAY_ERROR gateway_error_quark()
+
+GQuark gateway_error_quark(void);
+
+typedef void (*gateway_state_cb) (struct audio_device *dev,
+                                       gateway_state_t old_state,
+                                       gateway_state_t new_state,
+                                       void *user_data);
+typedef void (*gateway_stream_cb_t) (struct audio_device *dev, GError *err,
+               void *user_data);
+
+void gateway_set_state(struct audio_device *dev, gateway_state_t new_state);
+void gateway_unregister(struct audio_device *dev);
+struct gateway *gateway_init(struct audio_device *device);
+gboolean gateway_is_active(struct audio_device *dev);
+gboolean gateway_is_connected(struct audio_device *dev);
+int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io);
+int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan);
+void gateway_start_service(struct audio_device *device);
+unsigned int gateway_request_stream(struct audio_device *dev,
+                       gateway_stream_cb_t cb, void *user_data);
+int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t cb,
+                       void *user_data);
+gboolean gateway_cancel_stream(struct audio_device *dev, unsigned int id);
+int gateway_get_sco_fd(struct audio_device *dev);
+void gateway_suspend_stream(struct audio_device *dev);
+unsigned int gateway_add_state_cb(gateway_state_cb cb, void *user_data);
+gboolean gateway_remove_state_cb(unsigned int id);
+gateway_lock_t gateway_get_lock(struct audio_device *dev);
+gboolean gateway_lock(struct audio_device *dev, gateway_lock_t lock);
+gboolean gateway_unlock(struct audio_device *dev, gateway_lock_t lock);
diff --git a/audio/gsta2dpsink.c b/audio/gsta2dpsink.c
new file mode 100644 (file)
index 0000000..c8f6346
--- /dev/null
@@ -0,0 +1,729 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <pthread.h>
+
+#include "gstpragma.h"
+#include "gsta2dpsink.h"
+
+GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug);
+#define GST_CAT_DEFAULT gst_a2dp_sink_debug
+
+#define A2DP_SBC_RTP_PAYLOAD_TYPE 1
+#define TEMPLATE_MAX_BITPOOL_STR "64"
+
+#define DEFAULT_AUTOCONNECT TRUE
+
+enum {
+       PROP_0,
+       PROP_DEVICE,
+       PROP_AUTOCONNECT,
+       PROP_TRANSPORT
+};
+
+GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN);
+
+static const GstElementDetails gst_a2dp_sink_details =
+       GST_ELEMENT_DETAILS("Bluetooth A2DP sink",
+                               "Sink/Audio",
+                               "Plays audio to an A2DP device",
+                               "Marcel Holtmann <marcel@holtmann.org>");
+
+static GstStaticPadTemplate gst_a2dp_sink_factory =
+       GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+                       GST_STATIC_CAPS("audio/x-sbc, "
+                               "rate = (int) { 16000, 32000, 44100, 48000 }, "
+                               "channels = (int) [ 1, 2 ], "
+                               "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
+                               "blocks = (int) { 4, 8, 12, 16 }, "
+                               "subbands = (int) { 4, 8 }, "
+                               "allocation = (string) { \"snr\", \"loudness\" }, "
+                               "bitpool = (int) [ 2, "
+                               TEMPLATE_MAX_BITPOOL_STR " ]; "
+                               "audio/mpeg"
+                               ));
+
+static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event);
+static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps);
+static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad);
+static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self);
+static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self);
+static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self);
+
+static void gst_a2dp_sink_finalize(GObject *obj)
+{
+       GstA2dpSink *self = GST_A2DP_SINK(obj);
+
+       g_mutex_free(self->cb_mutex);
+
+       G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static GstState gst_a2dp_sink_get_state(GstA2dpSink *self)
+{
+       GstState current, pending;
+
+       gst_element_get_state(GST_ELEMENT(self), &current, &pending, 0);
+       if (pending == GST_STATE_VOID_PENDING)
+               return current;
+
+       return pending;
+}
+
+/*
+ * Helper function to create elements, add to the bin and link it
+ * to another element.
+ */
+static GstElement *gst_a2dp_sink_init_element(GstA2dpSink *self,
+                       const gchar *elementname, const gchar *name,
+                       GstElement *link_to)
+{
+       GstElement *element;
+       GstState state;
+
+       GST_LOG_OBJECT(self, "Initializing %s", elementname);
+
+       element = gst_element_factory_make(elementname, name);
+       if (element == NULL) {
+               GST_DEBUG_OBJECT(self, "Couldn't create %s", elementname);
+               return NULL;
+       }
+
+       if (!gst_bin_add(GST_BIN(self), element)) {
+               GST_DEBUG_OBJECT(self, "failed to add %s to the bin",
+                                               elementname);
+               goto cleanup_and_fail;
+       }
+
+       state = gst_a2dp_sink_get_state(self);
+       if (gst_element_set_state(element, state) ==
+                       GST_STATE_CHANGE_FAILURE) {
+               GST_DEBUG_OBJECT(self, "%s failed to go to playing",
+                                               elementname);
+               goto remove_element_and_fail;
+       }
+
+       if (link_to != NULL)
+               if (!gst_element_link(link_to, element)) {
+                       GST_DEBUG_OBJECT(self, "couldn't link %s",
+                                       elementname);
+                       goto remove_element_and_fail;
+               }
+
+       return element;
+
+remove_element_and_fail:
+       gst_element_set_state(element, GST_STATE_NULL);
+       gst_bin_remove(GST_BIN(self), element);
+       return NULL;
+
+cleanup_and_fail:
+       g_object_unref(G_OBJECT(element));
+
+       return NULL;
+}
+
+static void gst_a2dp_sink_base_init(gpointer g_class)
+{
+       GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+       gst_element_class_set_details(element_class,
+               &gst_a2dp_sink_details);
+       gst_element_class_add_pad_template(element_class,
+               gst_static_pad_template_get(&gst_a2dp_sink_factory));
+}
+
+static void gst_a2dp_sink_set_property(GObject *object, guint prop_id,
+                                       const GValue *value, GParamSpec *pspec)
+{
+       GstA2dpSink *self = GST_A2DP_SINK(object);
+
+       switch (prop_id) {
+       case PROP_DEVICE:
+               if (self->sink != NULL)
+                       gst_avdtp_sink_set_device(self->sink,
+                               g_value_get_string(value));
+
+               if (self->device != NULL)
+                       g_free(self->device);
+               self->device = g_value_dup_string(value);
+               break;
+
+       case PROP_TRANSPORT:
+               if (self->sink != NULL)
+                       gst_avdtp_sink_set_transport(self->sink,
+                               g_value_get_string(value));
+
+               if (self->transport != NULL)
+                       g_free(self->transport);
+               self->transport = g_value_dup_string(value);
+               break;
+
+       case PROP_AUTOCONNECT:
+               self->autoconnect = g_value_get_boolean(value);
+
+               if (self->sink != NULL)
+                       g_object_set(G_OBJECT(self->sink), "auto-connect",
+                                       self->autoconnect, NULL);
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+               break;
+       }
+}
+
+static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
+                                       GValue *value, GParamSpec *pspec)
+{
+       GstA2dpSink *self = GST_A2DP_SINK(object);
+       gchar *device, *transport;
+
+       switch (prop_id) {
+       case PROP_DEVICE:
+               if (self->sink != NULL) {
+                       device = gst_avdtp_sink_get_device(self->sink);
+                       if (device != NULL)
+                               g_value_take_string(value, device);
+               }
+               break;
+       case PROP_AUTOCONNECT:
+               if (self->sink != NULL)
+                       g_object_get(G_OBJECT(self->sink), "auto-connect",
+                               &self->autoconnect, NULL);
+
+               g_value_set_boolean(value, self->autoconnect);
+               break;
+       case PROP_TRANSPORT:
+               if (self->sink != NULL) {
+                       transport = gst_avdtp_sink_get_transport(self->sink);
+                       if (transport != NULL)
+                               g_value_take_string(value, transport);
+               }
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+               break;
+       }
+}
+
+static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self)
+{
+       GstPad *capsfilter_pad;
+
+       /* we search for the capsfilter sinkpad */
+       capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink");
+
+       /* now we add a ghostpad */
+       self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink",
+               capsfilter_pad));
+       g_object_unref(capsfilter_pad);
+
+       /* the getcaps of our ghostpad must reflect the device caps */
+       gst_pad_set_getcaps_function(GST_PAD(self->ghostpad),
+                               gst_a2dp_sink_get_caps);
+       self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad);
+       gst_pad_set_setcaps_function(GST_PAD(self->ghostpad),
+                       GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps));
+
+       /* we need to handle events on our own and we also need the eventfunc
+        * of the ghostpad for forwarding calls */
+       self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad));
+       gst_pad_set_event_function(GST_PAD(self->ghostpad),
+                       gst_a2dp_sink_handle_event);
+
+       if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad)))
+               GST_ERROR_OBJECT(self, "failed to add ghostpad");
+
+       return TRUE;
+}
+
+static void gst_a2dp_sink_remove_dynamic_elements(GstA2dpSink *self)
+{
+       if (self->rtp) {
+               GST_LOG_OBJECT(self, "removing rtp element from the bin");
+               if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->rtp)))
+                       GST_WARNING_OBJECT(self, "failed to remove rtp "
+                                       "element from bin");
+               else
+                       self->rtp = NULL;
+       }
+}
+
+static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
+                       GstStateChange transition)
+{
+       GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+       GstA2dpSink *self = GST_A2DP_SINK(element);
+
+       switch (transition) {
+       case GST_STATE_CHANGE_READY_TO_PAUSED:
+               self->taglist = gst_tag_list_new();
+
+               gst_a2dp_sink_init_fakesink(self);
+               break;
+
+       case GST_STATE_CHANGE_NULL_TO_READY:
+               self->sink_is_in_bin = FALSE;
+               self->sink = GST_AVDTP_SINK(gst_element_factory_make(
+                               "avdtpsink", "avdtpsink"));
+               if (self->sink == NULL) {
+                       GST_WARNING_OBJECT(self, "failed to create avdtpsink");
+                       return GST_STATE_CHANGE_FAILURE;
+               }
+
+               if (self->device != NULL)
+                       gst_avdtp_sink_set_device(self->sink,
+                                       self->device);
+
+               if (self->transport != NULL)
+                       gst_avdtp_sink_set_transport(self->sink,
+                                       self->transport);
+
+               g_object_set(G_OBJECT(self->sink), "auto-connect",
+                                       self->autoconnect, NULL);
+
+               ret = gst_element_set_state(GST_ELEMENT(self->sink),
+                       GST_STATE_READY);
+               break;
+       default:
+               break;
+       }
+
+       if (ret == GST_STATE_CHANGE_FAILURE)
+               return ret;
+
+       ret = GST_ELEMENT_CLASS(parent_class)->change_state(element,
+                                                               transition);
+
+       switch (transition) {
+       case GST_STATE_CHANGE_PAUSED_TO_READY:
+               if (self->taglist) {
+                       gst_tag_list_free(self->taglist);
+                       self->taglist = NULL;
+               }
+               if (self->newseg_event != NULL) {
+                       gst_event_unref(self->newseg_event);
+                       self->newseg_event = NULL;
+               }
+               gst_a2dp_sink_remove_fakesink(self);
+               break;
+
+       case GST_STATE_CHANGE_READY_TO_NULL:
+               if (self->sink_is_in_bin) {
+                       if (!gst_bin_remove(GST_BIN(self),
+                                               GST_ELEMENT(self->sink)))
+                               GST_WARNING_OBJECT(self, "Failed to remove "
+                                               "avdtpsink from bin");
+               } else if (self->sink != NULL) {
+                       gst_element_set_state(GST_ELEMENT(self->sink),
+                                       GST_STATE_NULL);
+                       g_object_unref(G_OBJECT(self->sink));
+               }
+
+               self->sink = NULL;
+
+               gst_a2dp_sink_remove_dynamic_elements(self);
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS(klass);
+       GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+       parent_class = g_type_class_peek_parent(klass);
+
+       object_class->set_property = GST_DEBUG_FUNCPTR(
+                                       gst_a2dp_sink_set_property);
+       object_class->get_property = GST_DEBUG_FUNCPTR(
+                                       gst_a2dp_sink_get_property);
+
+       object_class->finalize = GST_DEBUG_FUNCPTR(
+                                       gst_a2dp_sink_finalize);
+
+       element_class->change_state = GST_DEBUG_FUNCPTR(
+                                       gst_a2dp_sink_change_state);
+
+       g_object_class_install_property(object_class, PROP_DEVICE,
+                       g_param_spec_string("device", "Device",
+                       "Bluetooth remote device address",
+                       NULL, G_PARAM_READWRITE));
+
+       g_object_class_install_property(object_class, PROP_AUTOCONNECT,
+                       g_param_spec_boolean("auto-connect", "Auto-connect",
+                       "Automatically attempt to connect to device",
+                       DEFAULT_AUTOCONNECT, G_PARAM_READWRITE));
+
+       g_object_class_install_property(object_class, PROP_TRANSPORT,
+                       g_param_spec_string("transport", "Transport",
+                       "Use configured transport",
+                       NULL, G_PARAM_READWRITE));
+
+       GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0,
+                               "A2DP sink element");
+}
+
+GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self)
+{
+       return gst_avdtp_sink_get_device_caps(self->sink);
+}
+
+static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad)
+{
+       GstCaps *caps;
+       GstCaps *caps_aux;
+       GstA2dpSink *self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
+
+       if (self->sink == NULL) {
+               GST_DEBUG_OBJECT(self, "a2dpsink isn't initialized "
+                       "returning template caps");
+               caps = gst_static_pad_template_get_caps(
+                               &gst_a2dp_sink_factory);
+       } else {
+               GST_LOG_OBJECT(self, "Getting device caps");
+               caps = gst_a2dp_sink_get_device_caps(self);
+               if (caps == NULL)
+                       caps = gst_static_pad_template_get_caps(
+                                       &gst_a2dp_sink_factory);
+       }
+       caps_aux = gst_caps_copy(caps);
+       g_object_set(self->capsfilter, "caps", caps_aux, NULL);
+       gst_caps_unref(caps_aux);
+       return caps;
+}
+
+static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self)
+{
+       GstElement *sink;
+
+       /* check if we don't need a new sink */
+       if (self->sink_is_in_bin)
+               return TRUE;
+
+       if (self->sink == NULL)
+               sink = gst_element_factory_make("avdtpsink", "avdtpsink");
+       else
+               sink = GST_ELEMENT(self->sink);
+
+       if (sink == NULL) {
+               GST_ERROR_OBJECT(self, "Couldn't create avdtpsink");
+               return FALSE;
+       }
+
+       if (!gst_bin_add(GST_BIN(self), sink)) {
+               GST_ERROR_OBJECT(self, "failed to add avdtpsink "
+                       "to the bin");
+               goto cleanup_and_fail;
+       }
+
+       if (gst_element_set_state(sink, GST_STATE_READY) ==
+                       GST_STATE_CHANGE_FAILURE) {
+               GST_ERROR_OBJECT(self, "avdtpsink failed to go to ready");
+               goto remove_element_and_fail;
+       }
+
+       if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) {
+               GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay "
+                       "to avdtpsink");
+               goto remove_element_and_fail;
+       }
+
+       self->sink = GST_AVDTP_SINK(sink);
+       self->sink_is_in_bin = TRUE;
+       g_object_set(G_OBJECT(self->sink), "device", self->device, NULL);
+       g_object_set(G_OBJECT(self->sink), "transport", self->transport, NULL);
+
+       gst_element_set_state(sink, GST_STATE_PAUSED);
+
+       return TRUE;
+
+remove_element_and_fail:
+       gst_element_set_state(sink, GST_STATE_NULL);
+       gst_bin_remove(GST_BIN(self), sink);
+       return FALSE;
+
+cleanup_and_fail:
+       if (sink != NULL)
+               g_object_unref(G_OBJECT(sink));
+
+       return FALSE;
+}
+
+static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self)
+{
+       GstElement *rtppay;
+
+       /* if we already have a rtp, we don't need a new one */
+       if (self->rtp != NULL)
+               return TRUE;
+
+       rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp",
+                                               self->capsfilter);
+       if (rtppay == NULL)
+               return FALSE;
+
+       self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
+       g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL);
+
+       gst_element_set_state(rtppay, GST_STATE_PAUSED);
+
+       return TRUE;
+}
+
+static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self)
+{
+       GstElement *rtppay;
+
+       /* check if we don't need a new rtp */
+       if (self->rtp)
+               return TRUE;
+
+       GST_LOG_OBJECT(self, "Initializing rtp mpeg element");
+       /* if capsfilter is not created then we can't have our rtp element */
+       if (self->capsfilter == NULL)
+               return FALSE;
+
+       rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp",
+                                       self->capsfilter);
+       if (rtppay == NULL)
+               return FALSE;
+
+       self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
+
+       gst_element_set_state(rtppay, GST_STATE_PAUSED);
+
+       return TRUE;
+}
+
+static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self,
+                                               GstCaps *caps)
+{
+       GstStructure *structure;
+       GstEvent *event;
+       GstPad *capsfilterpad;
+       gboolean crc;
+       gchar *mode = NULL;
+
+       structure = gst_caps_get_structure(caps, 0);
+
+       /* before everything we need to remove fakesink */
+       gst_a2dp_sink_remove_fakesink(self);
+
+       /* first, we need to create our rtp payloader */
+       if (gst_structure_has_name(structure, "audio/x-sbc")) {
+               GST_LOG_OBJECT(self, "sbc media received");
+               if (!gst_a2dp_sink_init_rtp_sbc_element(self))
+                       return FALSE;
+       } else if (gst_structure_has_name(structure, "audio/mpeg")) {
+               GST_LOG_OBJECT(self, "mp3 media received");
+               if (!gst_a2dp_sink_init_rtp_mpeg_element(self))
+                       return FALSE;
+       } else {
+               GST_ERROR_OBJECT(self, "Unexpected media type");
+               return FALSE;
+       }
+
+       if (!gst_a2dp_sink_init_avdtp_sink(self))
+               return FALSE;
+
+       /* check if we should push the taglist FIXME should we push this?
+        * we can send the tags directly if needed */
+       if (self->taglist != NULL &&
+                       gst_structure_has_name(structure, "audio/mpeg")) {
+
+               event = gst_event_new_tag(self->taglist);
+
+               /* send directly the crc */
+               if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc))
+                       gst_avdtp_sink_set_crc(self->sink, crc);
+
+               if (gst_tag_list_get_string(self->taglist, "channel-mode",
+                               &mode))
+                       gst_avdtp_sink_set_channel_mode(self->sink, mode);
+
+               capsfilterpad = gst_ghost_pad_get_target(self->ghostpad);
+               gst_pad_send_event(capsfilterpad, event);
+               self->taglist = NULL;
+               g_free(mode);
+       }
+
+       if (!gst_avdtp_sink_set_device_caps(self->sink, caps))
+               return FALSE;
+
+       g_object_set(G_OBJECT(self->rtp), "mtu",
+               gst_avdtp_sink_get_link_mtu(self->sink), NULL);
+
+       /* we forward our new segment here if we have one */
+       if (self->newseg_event) {
+               gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp),
+                                       self->newseg_event);
+               self->newseg_event = NULL;
+       }
+
+       return TRUE;
+}
+
+static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
+{
+       GstA2dpSink *self;
+
+       self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
+       GST_INFO_OBJECT(self, "setting caps");
+
+       /* now we know the caps */
+       gst_a2dp_sink_init_dynamic_elements(self, caps);
+
+       return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps);
+}
+
+/* used for catching newsegment events while we don't have a sink, for
+ * later forwarding it to the sink */
+static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event)
+{
+       GstA2dpSink *self;
+       GstTagList *taglist = NULL;
+       GstObject *parent;
+
+       self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
+       parent = gst_element_get_parent(GST_ELEMENT(self->sink));
+
+       if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT &&
+                       parent != GST_OBJECT_CAST(self)) {
+               if (self->newseg_event != NULL)
+                       gst_event_unref(self->newseg_event);
+               self->newseg_event = gst_event_ref(event);
+
+       } else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG &&
+                       parent != GST_OBJECT_CAST(self)) {
+               if (self->taglist == NULL)
+                       gst_event_parse_tag(event, &self->taglist);
+               else {
+                       gst_event_parse_tag(event, &taglist);
+                       gst_tag_list_insert(self->taglist, taglist,
+                                       GST_TAG_MERGE_REPLACE);
+               }
+       }
+
+       if (parent != NULL)
+               gst_object_unref(GST_OBJECT(parent));
+
+       return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event);
+}
+
+static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self)
+{
+       GstElement *element;
+
+       element = gst_element_factory_make("capsfilter", "filter");
+       if (element == NULL)
+               goto failed;
+
+       if (!gst_bin_add(GST_BIN(self), element))
+               goto failed;
+
+       self->capsfilter = element;
+       return TRUE;
+
+failed:
+       GST_ERROR_OBJECT(self, "Failed to initialize caps filter");
+       return FALSE;
+}
+
+static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self)
+{
+       if (self->fakesink != NULL)
+               return TRUE;
+
+       g_mutex_lock(self->cb_mutex);
+       self->fakesink = gst_a2dp_sink_init_element(self, "fakesink",
+                       "fakesink", self->capsfilter);
+       g_mutex_unlock(self->cb_mutex);
+
+       if (!self->fakesink)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self)
+{
+       g_mutex_lock(self->cb_mutex);
+
+       if (self->fakesink != NULL) {
+               gst_element_set_locked_state(self->fakesink, TRUE);
+               gst_element_set_state(self->fakesink, GST_STATE_NULL);
+
+               gst_bin_remove(GST_BIN(self), self->fakesink);
+               self->fakesink = NULL;
+       }
+
+       g_mutex_unlock(self->cb_mutex);
+
+       return TRUE;
+}
+
+static void gst_a2dp_sink_init(GstA2dpSink *self,
+                       GstA2dpSinkClass *klass)
+{
+       self->sink = NULL;
+       self->fakesink = NULL;
+       self->rtp = NULL;
+       self->device = NULL;
+       self->transport = NULL;
+       self->autoconnect = DEFAULT_AUTOCONNECT;
+       self->capsfilter = NULL;
+       self->newseg_event = NULL;
+       self->taglist = NULL;
+       self->ghostpad = NULL;
+       self->sink_is_in_bin = FALSE;
+
+       self->cb_mutex = g_mutex_new();
+
+       /* we initialize our capsfilter */
+       gst_a2dp_sink_init_caps_filter(self);
+       g_object_set(self->capsfilter, "caps",
+               gst_static_pad_template_get_caps(&gst_a2dp_sink_factory),
+               NULL);
+
+       gst_a2dp_sink_init_fakesink(self);
+
+       gst_a2dp_sink_init_ghost_pad(self);
+
+}
+
+gboolean gst_a2dp_sink_plugin_init(GstPlugin *plugin)
+{
+       return gst_element_register(plugin, "a2dpsink",
+                       GST_RANK_MARGINAL, GST_TYPE_A2DP_SINK);
+}
diff --git a/audio/gsta2dpsink.h b/audio/gsta2dpsink.h
new file mode 100644 (file)
index 0000000..1a591b2
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GST_A2DP_SINK_H__
+#define __GST_A2DP_SINK_H__
+
+#include <gst/gst.h>
+#include <gst/rtp/gstbasertppayload.h>
+#include "gstavdtpsink.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_A2DP_SINK \
+       (gst_a2dp_sink_get_type())
+#define GST_A2DP_SINK(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A2DP_SINK,GstA2dpSink))
+#define GST_A2DP_SINK_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_A2DP_SINK,GstA2dpSinkClass))
+#define GST_IS_A2DP_SINK(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A2DP_SINK))
+#define GST_IS_A2DP_SINK_CLASS(obj) \
+       (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SINK))
+
+typedef struct _GstA2dpSink GstA2dpSink;
+typedef struct _GstA2dpSinkClass GstA2dpSinkClass;
+
+struct _GstA2dpSink {
+       GstBin bin;
+
+       GstBaseRTPPayload *rtp;
+       GstAvdtpSink *sink;
+       GstElement *capsfilter;
+       GstElement *fakesink;
+
+       gchar *device;
+       gchar *transport;
+       gboolean autoconnect;
+       gboolean sink_is_in_bin;
+
+       GstGhostPad *ghostpad;
+       GstPadSetCapsFunction ghostpad_setcapsfunc;
+       GstPadEventFunction ghostpad_eventfunc;
+
+       GstEvent *newseg_event;
+       /* Store the tags received before the a2dpsender sink is created
+        * when it is created we forward this to it */
+       GstTagList *taglist;
+
+       GMutex *cb_mutex;
+};
+
+struct _GstA2dpSinkClass {
+       GstBinClass parent_class;
+};
+
+GType gst_a2dp_sink_get_type(void);
+
+gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin);
+
+GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self);
+
+G_END_DECLS
+
+#endif
diff --git a/audio/gstavdtpsink.c b/audio/gstavdtpsink.c
new file mode 100644 (file)
index 0000000..1f374fc
--- /dev/null
@@ -0,0 +1,2034 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+
+#include <dbus/dbus.h>
+
+#include "ipc.h"
+#include "rtp.h"
+#include "a2dp-codecs.h"
+
+#include "gstpragma.h"
+#include "gstavdtpsink.h"
+
+GST_DEBUG_CATEGORY_STATIC(avdtp_sink_debug);
+#define GST_CAT_DEFAULT avdtp_sink_debug
+
+#define BUFFER_SIZE 2048
+#define TEMPLATE_MAX_BITPOOL 64
+#define CRC_PROTECTED 1
+#define CRC_UNPROTECTED 0
+
+#define DEFAULT_AUTOCONNECT TRUE
+
+#define GST_AVDTP_SINK_MUTEX_LOCK(s) G_STMT_START {    \
+               g_mutex_lock(s->sink_lock);             \
+       } G_STMT_END
+
+#define GST_AVDTP_SINK_MUTEX_UNLOCK(s) G_STMT_START {  \
+               g_mutex_unlock(s->sink_lock);           \
+       } G_STMT_END
+
+struct bluetooth_data {
+       struct bt_get_capabilities_rsp *caps; /* Bluetooth device caps */
+       guint link_mtu;
+
+       DBusConnection *conn;
+       guint8 codec; /* Bluetooth transport configuration */
+       gchar *uuid;
+       guint8 *config;
+       gint config_size;
+
+       gchar buffer[BUFFER_SIZE];      /* Codec transfer buffer */
+};
+
+#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0)
+#define IS_MPEG_AUDIO(n) (strcmp((n), "audio/mpeg") == 0)
+
+enum {
+       PROP_0,
+       PROP_DEVICE,
+       PROP_AUTOCONNECT,
+       PROP_TRANSPORT
+};
+
+GST_BOILERPLATE(GstAvdtpSink, gst_avdtp_sink, GstBaseSink,
+                       GST_TYPE_BASE_SINK);
+
+static const GstElementDetails avdtp_sink_details =
+       GST_ELEMENT_DETAILS("Bluetooth AVDTP sink",
+                               "Sink/Audio",
+                               "Plays audio to an A2DP device",
+                               "Marcel Holtmann <marcel@holtmann.org>");
+
+static GstStaticPadTemplate avdtp_sink_factory =
+       GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+               GST_STATIC_CAPS("application/x-rtp, "
+                               "media = (string) \"audio\","
+                               "payload = (int) "
+                                       GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+                               "clock-rate = (int) { 16000, 32000, "
+                                       "44100, 48000 }, "
+                               "encoding-name = (string) \"SBC\"; "
+                               "application/x-rtp, "
+                               "media = (string) \"audio\", "
+                               "payload = (int) "
+                               GST_RTP_PAYLOAD_MPA_STRING ", "
+                               "clock-rate = (int) 90000; "
+                               "application/x-rtp, "
+                               "media = (string) \"audio\", "
+                               "payload = (int) "
+                               GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+                               "clock-rate = (int) 90000, "
+                               "encoding-name = (string) \"MPA\""
+                               ));
+
+static int gst_avdtp_sink_audioservice_send(GstAvdtpSink *self,
+                                       const bt_audio_msg_header_t *msg);
+static int gst_avdtp_sink_audioservice_expect(GstAvdtpSink *self,
+                                       bt_audio_msg_header_t *outmsg,
+                                       guint8 expected_name);
+
+
+static void gst_avdtp_sink_base_init(gpointer g_class)
+{
+       GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+       gst_element_class_add_pad_template(element_class,
+               gst_static_pad_template_get(&avdtp_sink_factory));
+
+       gst_element_class_set_details(element_class, &avdtp_sink_details);
+}
+
+static void gst_avdtp_sink_transport_release(GstAvdtpSink *self)
+{
+       DBusMessage *msg;
+       const char *access_type = "w";
+
+       msg = dbus_message_new_method_call("org.bluez", self->transport,
+                                               "org.bluez.MediaTransport",
+                                               "Release");
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &access_type,
+                                       DBUS_TYPE_INVALID);
+
+       dbus_connection_send(self->data->conn, msg, NULL);
+
+       dbus_message_unref(msg);
+}
+
+static gboolean gst_avdtp_sink_stop(GstBaseSink *basesink)
+{
+       GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+
+       GST_INFO_OBJECT(self, "stop");
+
+       if (self->watch_id != 0) {
+               g_source_remove(self->watch_id);
+               self->watch_id = 0;
+       }
+
+       if (self->server) {
+               bt_audio_service_close(g_io_channel_unix_get_fd(self->server));
+               g_io_channel_unref(self->server);
+               self->server = NULL;
+       }
+
+       if (self->stream) {
+               g_io_channel_shutdown(self->stream, TRUE, NULL);
+               g_io_channel_unref(self->stream);
+               self->stream = NULL;
+       }
+
+       if (self->data) {
+               if (self->transport)
+                       gst_avdtp_sink_transport_release(self);
+               if (self->data->conn)
+                       dbus_connection_unref(self->data->conn);
+               g_free(self->data);
+               self->data = NULL;
+       }
+
+       if (self->stream_caps) {
+               gst_caps_unref(self->stream_caps);
+               self->stream_caps = NULL;
+       }
+
+       if (self->dev_caps) {
+               gst_caps_unref(self->dev_caps);
+               self->dev_caps = NULL;
+       }
+
+       return TRUE;
+}
+
+static void gst_avdtp_sink_finalize(GObject *object)
+{
+       GstAvdtpSink *self = GST_AVDTP_SINK(object);
+
+       if (self->data)
+               gst_avdtp_sink_stop(GST_BASE_SINK(self));
+
+       if (self->device)
+               g_free(self->device);
+
+       if (self->transport)
+               g_free(self->transport);
+
+       g_mutex_free(self->sink_lock);
+
+       G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void gst_avdtp_sink_set_property(GObject *object, guint prop_id,
+                                       const GValue *value, GParamSpec *pspec)
+{
+       GstAvdtpSink *sink = GST_AVDTP_SINK(object);
+
+       switch (prop_id) {
+       case PROP_DEVICE:
+               if (sink->device)
+                       g_free(sink->device);
+               sink->device = g_value_dup_string(value);
+               break;
+
+       case PROP_AUTOCONNECT:
+               sink->autoconnect = g_value_get_boolean(value);
+               break;
+
+       case PROP_TRANSPORT:
+               if (sink->transport)
+                       g_free(sink->transport);
+               sink->transport = g_value_dup_string(value);
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+               break;
+       }
+}
+
+static void gst_avdtp_sink_get_property(GObject *object, guint prop_id,
+                                       GValue *value, GParamSpec *pspec)
+{
+       GstAvdtpSink *sink = GST_AVDTP_SINK(object);
+
+       switch (prop_id) {
+       case PROP_DEVICE:
+               g_value_set_string(value, sink->device);
+               break;
+
+       case PROP_AUTOCONNECT:
+               g_value_set_boolean(value, sink->autoconnect);
+               break;
+
+       case PROP_TRANSPORT:
+               g_value_set_string(value, sink->transport);
+               break;
+
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+               break;
+       }
+}
+
+static gint gst_avdtp_sink_bluetooth_recvmsg_fd(GstAvdtpSink *sink)
+{
+       int err, ret;
+
+       ret = bt_audio_service_get_data_fd(
+                       g_io_channel_unix_get_fd(sink->server));
+
+       if (ret < 0) {
+               err = -errno;
+               GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)",
+                               strerror(-err), -err);
+               return err;
+       }
+
+       sink->stream = g_io_channel_unix_new(ret);
+       g_io_channel_set_encoding(sink->stream, NULL, NULL);
+       GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret);
+
+       return 0;
+}
+
+static codec_capabilities_t *gst_avdtp_find_caps(GstAvdtpSink *sink,
+                                               uint8_t codec_type)
+{
+       struct bt_get_capabilities_rsp *rsp = sink->data->caps;
+       codec_capabilities_t *codec = (void *) rsp->data;
+       int bytes_left = rsp->h.length - sizeof(*rsp);
+
+       while (bytes_left > 0) {
+               if ((codec->type == codec_type) &&
+                               !(codec->lock & BT_WRITE_LOCK))
+                       break;
+
+               bytes_left -= codec->length;
+               codec = (void *) codec + codec->length;
+       }
+
+       if (bytes_left <= 0)
+               return NULL;
+
+       return codec;
+}
+
+static gboolean gst_avdtp_sink_init_sbc_pkt_conf(GstAvdtpSink *sink,
+                                       GstCaps *caps,
+                                       sbc_capabilities_t *pkt)
+{
+       sbc_capabilities_t *cfg;
+       const GValue *value = NULL;
+       const char *pref, *name;
+       gint rate, subbands, blocks;
+       GstStructure *structure = gst_caps_get_structure(caps, 0);
+
+       cfg = (void *) gst_avdtp_find_caps(sink, BT_A2DP_SBC_SINK);
+       name = gst_structure_get_name(structure);
+
+       if (!(IS_SBC(name))) {
+               GST_ERROR_OBJECT(sink, "Unexpected format %s, "
+                               "was expecting sbc", name);
+               return FALSE;
+       }
+
+       value = gst_structure_get_value(structure, "rate");
+       rate = g_value_get_int(value);
+       if (rate == 44100)
+               cfg->frequency = BT_SBC_SAMPLING_FREQ_44100;
+       else if (rate == 48000)
+               cfg->frequency = BT_SBC_SAMPLING_FREQ_48000;
+       else if (rate == 32000)
+               cfg->frequency = BT_SBC_SAMPLING_FREQ_32000;
+       else if (rate == 16000)
+               cfg->frequency = BT_SBC_SAMPLING_FREQ_16000;
+       else {
+               GST_ERROR_OBJECT(sink, "Invalid rate while setting caps");
+               return FALSE;
+       }
+
+       value = gst_structure_get_value(structure, "mode");
+       pref = g_value_get_string(value);
+       if (strcmp(pref, "mono") == 0)
+               cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+       else if (strcmp(pref, "dual") == 0)
+               cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+       else if (strcmp(pref, "stereo") == 0)
+               cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+       else if (strcmp(pref, "joint") == 0)
+               cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+       else {
+               GST_ERROR_OBJECT(sink, "Invalid mode %s", pref);
+               return FALSE;
+       }
+
+       value = gst_structure_get_value(structure, "allocation");
+       pref = g_value_get_string(value);
+       if (strcmp(pref, "loudness") == 0)
+               cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+       else if (strcmp(pref, "snr") == 0)
+               cfg->allocation_method = BT_A2DP_ALLOCATION_SNR;
+       else {
+               GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref);
+               return FALSE;
+       }
+
+       value = gst_structure_get_value(structure, "subbands");
+       subbands = g_value_get_int(value);
+       if (subbands == 8)
+               cfg->subbands = BT_A2DP_SUBBANDS_8;
+       else if (subbands == 4)
+               cfg->subbands = BT_A2DP_SUBBANDS_4;
+       else {
+               GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands);
+               return FALSE;
+       }
+
+       value = gst_structure_get_value(structure, "blocks");
+       blocks = g_value_get_int(value);
+       if (blocks == 16)
+               cfg->block_length = BT_A2DP_BLOCK_LENGTH_16;
+       else if (blocks == 12)
+               cfg->block_length = BT_A2DP_BLOCK_LENGTH_12;
+       else if (blocks == 8)
+               cfg->block_length = BT_A2DP_BLOCK_LENGTH_8;
+       else if (blocks == 4)
+               cfg->block_length = BT_A2DP_BLOCK_LENGTH_4;
+       else {
+               GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks);
+               return FALSE;
+       }
+
+       value = gst_structure_get_value(structure, "bitpool");
+       cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value);
+
+       memcpy(pkt, cfg, sizeof(*pkt));
+
+       return TRUE;
+}
+
+static gboolean gst_avdtp_sink_conf_recv_stream_fd(
+                                       GstAvdtpSink *self)
+{
+       struct bluetooth_data *data = self->data;
+       gint ret;
+       GError *gerr = NULL;
+       GIOStatus status;
+       GIOFlags flags;
+       int fd;
+
+       /* Proceed if stream was already acquired */
+       if (self->stream != NULL)
+               goto proceed;
+
+       ret = gst_avdtp_sink_bluetooth_recvmsg_fd(self);
+       if (ret < 0)
+               return FALSE;
+
+       if (!self->stream) {
+               GST_ERROR_OBJECT(self, "Error while configuring device: "
+                               "could not acquire audio socket");
+               return FALSE;
+       }
+
+proceed:
+       /* set stream socket to nonblock */
+       GST_LOG_OBJECT(self, "setting stream socket to nonblock");
+       flags = g_io_channel_get_flags(self->stream);
+       flags |= G_IO_FLAG_NONBLOCK;
+       status = g_io_channel_set_flags(self->stream, flags, &gerr);
+       if (status != G_IO_STATUS_NORMAL) {
+               if (gerr)
+                       GST_WARNING_OBJECT(self, "Error while "
+                               "setting server socket to nonblock: "
+                               "%s", gerr->message);
+               else
+                       GST_WARNING_OBJECT(self, "Error while "
+                                       "setting server "
+                                       "socket to nonblock");
+       }
+
+       fd = g_io_channel_unix_get_fd(self->stream);
+
+       /* It is possible there is some outstanding
+       data in the pipe - we have to empty it */
+       GST_LOG_OBJECT(self, "emptying stream pipe");
+       while (1) {
+               ssize_t bread = read(fd, data->buffer, data->link_mtu);
+               if (bread <= 0)
+                       break;
+       }
+
+       /* set stream socket to block */
+       GST_LOG_OBJECT(self, "setting stream socket to block");
+       flags = g_io_channel_get_flags(self->stream);
+       flags &= ~G_IO_FLAG_NONBLOCK;
+       status = g_io_channel_set_flags(self->stream, flags, &gerr);
+       if (status != G_IO_STATUS_NORMAL) {
+               if (gerr)
+                       GST_WARNING_OBJECT(self, "Error while "
+                               "setting server socket to block:"
+                               "%s", gerr->message);
+               else
+                       GST_WARNING_OBJECT(self, "Error while "
+                               "setting server "
+                               "socket to block");
+       }
+
+       memset(data->buffer, 0, sizeof(data->buffer));
+
+       return TRUE;
+}
+
+static gboolean server_callback(GIOChannel *chan,
+                                       GIOCondition cond, gpointer data)
+{
+       if (cond & G_IO_HUP || cond & G_IO_NVAL)
+               return FALSE;
+       else if (cond & G_IO_ERR)
+               GST_WARNING_OBJECT(GST_AVDTP_SINK(data),
+                                       "Untreated callback G_IO_ERR");
+
+       return TRUE;
+}
+
+static GstStructure *gst_avdtp_sink_parse_sbc_caps(
+                       GstAvdtpSink *self, sbc_capabilities_t *sbc)
+{
+       GstStructure *structure;
+       GValue *value;
+       GValue *list;
+       gboolean mono, stereo;
+
+       if (sbc == NULL)
+               return NULL;
+
+       structure = gst_structure_empty_new("audio/x-sbc");
+       value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
+
+       /* mode */
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+               g_value_set_static_string(value, "mono");
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) {
+               g_value_set_static_string(value, "stereo");
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) {
+               g_value_set_static_string(value, "dual");
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) {
+               g_value_set_static_string(value, "joint");
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "mode", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* subbands */
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       value = g_value_init(value, G_TYPE_INT);
+       if (sbc->subbands & BT_A2DP_SUBBANDS_4) {
+               g_value_set_int(value, 4);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->subbands & BT_A2DP_SUBBANDS_8) {
+               g_value_set_int(value, 8);
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "subbands", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* blocks */
+       value = g_value_init(value, G_TYPE_INT);
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) {
+               g_value_set_int(value, 16);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) {
+               g_value_set_int(value, 12);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) {
+               g_value_set_int(value, 8);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) {
+               g_value_set_int(value, 4);
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "blocks", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* allocation */
+       g_value_init(value, G_TYPE_STRING);
+       list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST);
+       if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) {
+               g_value_set_static_string(value, "loudness");
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) {
+               g_value_set_static_string(value, "snr");
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "allocation", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* rate */
+       g_value_init(value, G_TYPE_INT);
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) {
+               g_value_set_int(value, 48000);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) {
+               g_value_set_int(value, 44100);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) {
+               g_value_set_int(value, 32000);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) {
+               g_value_set_int(value, 16000);
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "rate", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* bitpool */
+       value = g_value_init(value, GST_TYPE_INT_RANGE);
+       gst_value_set_int_range(value,
+                       MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL),
+                       MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL));
+       gst_structure_set_value(structure, "bitpool", value);
+       g_value_unset(value);
+
+       /* channels */
+       mono = FALSE;
+       stereo = FALSE;
+       if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+               mono = TRUE;
+       if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+                       (sbc->channel_mode &
+                       BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+                       (sbc->channel_mode &
+                       BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+               stereo = TRUE;
+
+       if (mono && stereo) {
+               g_value_init(value, GST_TYPE_INT_RANGE);
+               gst_value_set_int_range(value, 1, 2);
+       } else {
+               g_value_init(value, G_TYPE_INT);
+               if (mono)
+                       g_value_set_int(value, 1);
+               else if (stereo)
+                       g_value_set_int(value, 2);
+               else {
+                       GST_ERROR_OBJECT(self,
+                               "Unexpected number of channels");
+                       g_value_set_int(value, 0);
+               }
+       }
+
+       gst_structure_set_value(structure, "channels", value);
+       g_free(value);
+
+       return structure;
+}
+
+static GstStructure *gst_avdtp_sink_parse_mpeg_caps(
+                       GstAvdtpSink *self, mpeg_capabilities_t *mpeg)
+{
+       GstStructure *structure;
+       GValue *value;
+       GValue *list;
+       gboolean valid_layer = FALSE;
+       gboolean mono, stereo;
+
+       if (!mpeg)
+               return NULL;
+
+       GST_LOG_OBJECT(self, "parsing mpeg caps");
+
+       structure = gst_structure_empty_new("audio/mpeg");
+       value = g_new0(GValue, 1);
+       g_value_init(value, G_TYPE_INT);
+
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       g_value_set_int(value, 1);
+       gst_value_list_prepend_value(list, value);
+       g_value_set_int(value, 2);
+       gst_value_list_prepend_value(list, value);
+       gst_structure_set_value(structure, "mpegversion", list);
+       g_free(list);
+
+       /* layer */
+       GST_LOG_OBJECT(self, "setting mpeg layer");
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       if (mpeg->layer & BT_MPEG_LAYER_1) {
+               g_value_set_int(value, 1);
+               gst_value_list_prepend_value(list, value);
+               valid_layer = TRUE;
+       }
+       if (mpeg->layer & BT_MPEG_LAYER_2) {
+               g_value_set_int(value, 2);
+               gst_value_list_prepend_value(list, value);
+               valid_layer = TRUE;
+       }
+       if (mpeg->layer & BT_MPEG_LAYER_3) {
+               g_value_set_int(value, 3);
+               gst_value_list_prepend_value(list, value);
+               valid_layer = TRUE;
+       }
+       if (list) {
+               gst_structure_set_value(structure, "layer", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       if (!valid_layer) {
+               gst_structure_free(structure);
+               g_free(value);
+               return NULL;
+       }
+
+       /* rate */
+       GST_LOG_OBJECT(self, "setting mpeg rate");
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) {
+               g_value_set_int(value, 48000);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) {
+               g_value_set_int(value, 44100);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) {
+               g_value_set_int(value, 32000);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) {
+               g_value_set_int(value, 24000);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) {
+               g_value_set_int(value, 22050);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) {
+               g_value_set_int(value, 16000);
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "rate", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* channels */
+       GST_LOG_OBJECT(self, "setting mpeg channels");
+       mono = FALSE;
+       stereo = FALSE;
+       if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+               mono = TRUE;
+       if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+                       (mpeg->channel_mode &
+                       BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+                       (mpeg->channel_mode &
+                       BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+               stereo = TRUE;
+
+       if (mono && stereo) {
+               g_value_init(value, GST_TYPE_INT_RANGE);
+               gst_value_set_int_range(value, 1, 2);
+       } else {
+               g_value_init(value, G_TYPE_INT);
+               if (mono)
+                       g_value_set_int(value, 1);
+               else if (stereo)
+                       g_value_set_int(value, 2);
+               else {
+                       GST_ERROR_OBJECT(self,
+                               "Unexpected number of channels");
+                       g_value_set_int(value, 0);
+               }
+       }
+       gst_structure_set_value(structure, "channels", value);
+       g_free(value);
+
+       return structure;
+}
+
+static GstStructure *gst_avdtp_sink_parse_sbc_raw(GstAvdtpSink *self)
+{
+       a2dp_sbc_t *sbc = (a2dp_sbc_t *) self->data->config;
+       GstStructure *structure;
+       GValue *value;
+       GValue *list;
+       gboolean mono, stereo;
+
+       structure = gst_structure_empty_new("audio/x-sbc");
+       value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
+
+       /* mode */
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+               g_value_set_static_string(value, "mono");
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) {
+               g_value_set_static_string(value, "stereo");
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) {
+               g_value_set_static_string(value, "dual");
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) {
+               g_value_set_static_string(value, "joint");
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "mode", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* subbands */
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       value = g_value_init(value, G_TYPE_INT);
+       if (sbc->subbands & BT_A2DP_SUBBANDS_4) {
+               g_value_set_int(value, 4);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->subbands & BT_A2DP_SUBBANDS_8) {
+               g_value_set_int(value, 8);
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "subbands", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* blocks */
+       value = g_value_init(value, G_TYPE_INT);
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) {
+               g_value_set_int(value, 16);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) {
+               g_value_set_int(value, 12);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) {
+               g_value_set_int(value, 8);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) {
+               g_value_set_int(value, 4);
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "blocks", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* allocation */
+       g_value_init(value, G_TYPE_STRING);
+       list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST);
+       if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) {
+               g_value_set_static_string(value, "loudness");
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) {
+               g_value_set_static_string(value, "snr");
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "allocation", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* rate */
+       g_value_init(value, G_TYPE_INT);
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) {
+               g_value_set_int(value, 48000);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) {
+               g_value_set_int(value, 44100);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) {
+               g_value_set_int(value, 32000);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) {
+               g_value_set_int(value, 16000);
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "rate", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* bitpool */
+       value = g_value_init(value, GST_TYPE_INT_RANGE);
+       gst_value_set_int_range(value,
+                       MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL),
+                       MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL));
+       gst_structure_set_value(structure, "bitpool", value);
+       g_value_unset(value);
+
+       /* channels */
+       mono = FALSE;
+       stereo = FALSE;
+       if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+               mono = TRUE;
+       if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+                       (sbc->channel_mode &
+                       BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+                       (sbc->channel_mode &
+                       BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+               stereo = TRUE;
+
+       if (mono && stereo) {
+               g_value_init(value, GST_TYPE_INT_RANGE);
+               gst_value_set_int_range(value, 1, 2);
+       } else {
+               g_value_init(value, G_TYPE_INT);
+               if (mono)
+                       g_value_set_int(value, 1);
+               else if (stereo)
+                       g_value_set_int(value, 2);
+               else {
+                       GST_ERROR_OBJECT(self,
+                               "Unexpected number of channels");
+                       g_value_set_int(value, 0);
+               }
+       }
+
+       gst_structure_set_value(structure, "channels", value);
+       g_free(value);
+
+       return structure;
+}
+
+static GstStructure *gst_avdtp_sink_parse_mpeg_raw(GstAvdtpSink *self)
+{
+       a2dp_mpeg_t *mpeg = (a2dp_mpeg_t *) self->data->config;
+       GstStructure *structure;
+       GValue *value;
+       GValue *list;
+       gboolean valid_layer = FALSE;
+       gboolean mono, stereo;
+
+       GST_LOG_OBJECT(self, "parsing mpeg caps");
+
+       structure = gst_structure_empty_new("audio/mpeg");
+       value = g_new0(GValue, 1);
+       g_value_init(value, G_TYPE_INT);
+
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       g_value_set_int(value, 1);
+       gst_value_list_prepend_value(list, value);
+       g_value_set_int(value, 2);
+       gst_value_list_prepend_value(list, value);
+       gst_structure_set_value(structure, "mpegversion", list);
+       g_free(list);
+
+       /* layer */
+       GST_LOG_OBJECT(self, "setting mpeg layer");
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       if (mpeg->layer & BT_MPEG_LAYER_1) {
+               g_value_set_int(value, 1);
+               gst_value_list_prepend_value(list, value);
+               valid_layer = TRUE;
+       }
+       if (mpeg->layer & BT_MPEG_LAYER_2) {
+               g_value_set_int(value, 2);
+               gst_value_list_prepend_value(list, value);
+               valid_layer = TRUE;
+       }
+       if (mpeg->layer & BT_MPEG_LAYER_3) {
+               g_value_set_int(value, 3);
+               gst_value_list_prepend_value(list, value);
+               valid_layer = TRUE;
+       }
+       if (list) {
+               gst_structure_set_value(structure, "layer", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       if (!valid_layer) {
+               gst_structure_free(structure);
+               g_free(value);
+               return NULL;
+       }
+
+       /* rate */
+       GST_LOG_OBJECT(self, "setting mpeg rate");
+       list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST);
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) {
+               g_value_set_int(value, 48000);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) {
+               g_value_set_int(value, 44100);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) {
+               g_value_set_int(value, 32000);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) {
+               g_value_set_int(value, 24000);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) {
+               g_value_set_int(value, 22050);
+               gst_value_list_prepend_value(list, value);
+       }
+       if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) {
+               g_value_set_int(value, 16000);
+               gst_value_list_prepend_value(list, value);
+       }
+       g_value_unset(value);
+       if (list) {
+               gst_structure_set_value(structure, "rate", list);
+               g_free(list);
+               list = NULL;
+       }
+
+       /* channels */
+       GST_LOG_OBJECT(self, "setting mpeg channels");
+       mono = FALSE;
+       stereo = FALSE;
+       if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+               mono = TRUE;
+       if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) ||
+                       (mpeg->channel_mode &
+                       BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) ||
+                       (mpeg->channel_mode &
+                       BT_A2DP_CHANNEL_MODE_JOINT_STEREO))
+               stereo = TRUE;
+
+       if (mono && stereo) {
+               g_value_init(value, GST_TYPE_INT_RANGE);
+               gst_value_set_int_range(value, 1, 2);
+       } else {
+               g_value_init(value, G_TYPE_INT);
+               if (mono)
+                       g_value_set_int(value, 1);
+               else if (stereo)
+                       g_value_set_int(value, 2);
+               else {
+                       GST_ERROR_OBJECT(self,
+                               "Unexpected number of channels");
+                       g_value_set_int(value, 0);
+               }
+       }
+       gst_structure_set_value(structure, "channels", value);
+       g_free(value);
+
+       return structure;
+}
+
+static gboolean gst_avdtp_sink_update_config(GstAvdtpSink *self)
+{
+       GstStructure *structure;
+       gchar *tmp;
+
+       switch (self->data->codec) {
+       case A2DP_CODEC_SBC:
+               structure = gst_avdtp_sink_parse_sbc_raw(self);
+               break;
+       case A2DP_CODEC_MPEG12:
+               structure = gst_avdtp_sink_parse_mpeg_raw(self);
+               break;
+       default:
+               GST_ERROR_OBJECT(self, "Unsupported configuration");
+               return FALSE;
+       }
+
+       if (structure == NULL)
+               return FALSE;
+
+       if (self->dev_caps != NULL)
+               gst_caps_unref(self->dev_caps);
+
+       self->dev_caps = gst_caps_new_full(structure, NULL);
+
+       tmp = gst_caps_to_string(self->dev_caps);
+       GST_DEBUG_OBJECT(self, "Transport configuration: %s", tmp);
+       g_free(tmp);
+
+       return TRUE;
+}
+
+static gboolean gst_avdtp_sink_update_caps(GstAvdtpSink *self)
+{
+       sbc_capabilities_t *sbc;
+       mpeg_capabilities_t *mpeg;
+       GstStructure *sbc_structure;
+       GstStructure *mpeg_structure;
+       gchar *tmp;
+
+       GST_LOG_OBJECT(self, "updating device caps");
+
+       if (self->data->config_size != 0 && self->data->config != NULL)
+               return gst_avdtp_sink_update_config(self);
+
+       sbc = (void *) gst_avdtp_find_caps(self, BT_A2DP_SBC_SINK);
+       mpeg = (void *) gst_avdtp_find_caps(self, BT_A2DP_MPEG12_SINK);
+
+       if (!sbc) {
+               GST_ERROR_OBJECT(self, "Failed to find mandatory SBC sink");
+               return FALSE;
+       }
+
+       sbc_structure = gst_avdtp_sink_parse_sbc_caps(self, sbc);
+       mpeg_structure = gst_avdtp_sink_parse_mpeg_caps(self, mpeg);
+
+       if (self->dev_caps != NULL)
+               gst_caps_unref(self->dev_caps);
+       self->dev_caps = gst_caps_new_full(sbc_structure, NULL);
+       if (mpeg_structure != NULL)
+               gst_caps_append_structure(self->dev_caps, mpeg_structure);
+
+       tmp = gst_caps_to_string(self->dev_caps);
+       GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp);
+       g_free(tmp);
+
+       return TRUE;
+}
+
+static gboolean gst_avdtp_sink_get_capabilities(GstAvdtpSink *self)
+{
+       gchar buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_get_capabilities_req *req = (void *) buf;
+       struct bt_get_capabilities_rsp *rsp = (void *) buf;
+       int err;
+
+       memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+
+       req->h.type = BT_REQUEST;
+       req->h.name = BT_GET_CAPABILITIES;
+       req->h.length = sizeof(*req);
+
+       if (self->device == NULL)
+               return FALSE;
+       strncpy(req->destination, self->device, 18);
+       if (self->autoconnect)
+               req->flags |= BT_FLAG_AUTOCONNECT;
+
+       err = gst_avdtp_sink_audioservice_send(self, &req->h);
+       if (err < 0) {
+               GST_ERROR_OBJECT(self, "Error while asking device caps");
+               return FALSE;
+       }
+
+       rsp->h.length = 0;
+       err = gst_avdtp_sink_audioservice_expect(self,
+                                       &rsp->h, BT_GET_CAPABILITIES);
+       if (err < 0) {
+               GST_ERROR_OBJECT(self, "Error while getting device caps");
+               return FALSE;
+       }
+
+       self->data->caps = g_malloc0(rsp->h.length);
+       memcpy(self->data->caps, rsp, rsp->h.length);
+       if (!gst_avdtp_sink_update_caps(self)) {
+               GST_WARNING_OBJECT(self, "failed to update capabilities");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gint gst_avdtp_sink_get_channel_mode(const gchar *mode)
+{
+       if (strcmp(mode, "stereo") == 0)
+               return BT_A2DP_CHANNEL_MODE_STEREO;
+       else if (strcmp(mode, "joint-stereo") == 0)
+               return BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+       else if (strcmp(mode, "dual-channel") == 0)
+               return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+       else if (strcmp(mode, "mono") == 0)
+               return BT_A2DP_CHANNEL_MODE_MONO;
+       else
+               return -1;
+}
+
+static void gst_avdtp_sink_tag(const GstTagList *taglist,
+                       const gchar *tag, gpointer user_data)
+{
+       gboolean crc;
+       gchar *channel_mode = NULL;
+       GstAvdtpSink *self = GST_AVDTP_SINK(user_data);
+
+       if (strcmp(tag, "has-crc") == 0) {
+
+               if (!gst_tag_list_get_boolean(taglist, tag, &crc)) {
+                       GST_WARNING_OBJECT(self, "failed to get crc tag");
+                       return;
+               }
+
+               gst_avdtp_sink_set_crc(self, crc);
+
+       } else if (strcmp(tag, "channel-mode") == 0) {
+
+               if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) {
+                       GST_WARNING_OBJECT(self,
+                               "failed to get channel-mode tag");
+                       return;
+               }
+
+               self->channel_mode = gst_avdtp_sink_get_channel_mode(
+                                       channel_mode);
+               if (self->channel_mode == -1)
+                       GST_WARNING_OBJECT(self, "Received invalid channel "
+                                       "mode: %s", channel_mode);
+               g_free(channel_mode);
+
+       } else
+               GST_DEBUG_OBJECT(self, "received unused tag: %s", tag);
+}
+
+static gboolean gst_avdtp_sink_event(GstBaseSink *basesink,
+                       GstEvent *event)
+{
+       GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+       GstTagList *taglist = NULL;
+
+       if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) {
+               /* we check the tags, mp3 has tags that are importants and
+                * are outside caps */
+               gst_event_parse_tag(event, &taglist);
+               gst_tag_list_foreach(taglist, gst_avdtp_sink_tag, self);
+       }
+
+       return TRUE;
+}
+
+static gboolean gst_avdtp_sink_transport_parse_property(GstAvdtpSink *self,
+                                                       DBusMessageIter *i)
+{
+       const char *key;
+       DBusMessageIter variant_i;
+
+       if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+               GST_ERROR_OBJECT(self, "Property name not a string.");
+               return FALSE;
+       }
+
+       dbus_message_iter_get_basic(i, &key);
+
+       if (!dbus_message_iter_next(i))  {
+               GST_ERROR_OBJECT(self, "Property value missing");
+               return FALSE;
+       }
+
+       if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+               GST_ERROR_OBJECT(self, "Property value not a variant.");
+               return FALSE;
+       }
+
+       dbus_message_iter_recurse(i, &variant_i);
+
+       switch (dbus_message_iter_get_arg_type(&variant_i)) {
+       case DBUS_TYPE_BYTE: {
+               uint8_t value;
+               dbus_message_iter_get_basic(&variant_i, &value);
+
+               if (g_str_equal(key, "Codec") == TRUE)
+                       self->data->codec = value;
+
+               break;
+       }
+       case DBUS_TYPE_STRING: {
+               const char *value;
+               dbus_message_iter_get_basic(&variant_i, &value);
+
+               if (g_str_equal(key, "UUID") == TRUE) {
+                       g_free(self->data->uuid);
+                       self->data->uuid = g_strdup(value);
+               }
+
+               break;
+       }
+       case DBUS_TYPE_ARRAY: {
+               DBusMessageIter array_i;
+               char *value;
+               int size;
+
+               dbus_message_iter_recurse(&variant_i, &array_i);
+               dbus_message_iter_get_fixed_array(&array_i, &value, &size);
+
+               if (g_str_equal(key, "Configuration")) {
+                       g_free(self->data->config);
+                       self->data->config = g_new0(guint8, size);
+                       self->data->config_size = size;
+                       memcpy(self->data->config, value, size);
+               }
+
+               break;
+       }
+       }
+
+       return TRUE;
+}
+
+static gboolean gst_avdtp_sink_transport_acquire(GstAvdtpSink *self)
+{
+       DBusMessage *msg, *reply;
+       DBusError err;
+       const char *access_type = "w";
+       int fd;
+       uint16_t imtu, omtu;
+
+       dbus_error_init(&err);
+
+       if (self->data->conn == NULL)
+               self->data->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+
+       msg = dbus_message_new_method_call("org.bluez", self->transport,
+                                               "org.bluez.MediaTransport",
+                                               "Acquire");
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &access_type,
+                                       DBUS_TYPE_INVALID);
+
+       reply = dbus_connection_send_with_reply_and_block(self->data->conn,
+                                                       msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (dbus_error_is_set(&err))
+               goto fail;
+
+       if (dbus_message_get_args(reply, &err, DBUS_TYPE_UNIX_FD, &fd,
+                                               DBUS_TYPE_UINT16, &imtu,
+                                               DBUS_TYPE_UINT16, &omtu,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               goto fail;
+
+       dbus_message_unref(reply);
+
+       self->stream = g_io_channel_unix_new(fd);
+       g_io_channel_set_encoding(self->stream, NULL, NULL);
+       g_io_channel_set_close_on_unref(self->stream, TRUE);
+       self->data->link_mtu = omtu;
+       GST_DEBUG_OBJECT(self, "stream_fd=%d mtu=%d", fd, omtu);
+
+       return TRUE;
+
+fail:
+       GST_ERROR_OBJECT(self, "Failed to acquire transport stream: %s",
+                               err.message);
+
+       dbus_error_free(&err);
+
+       if (reply)
+               dbus_message_unref(reply);
+
+       return FALSE;
+}
+
+static gboolean gst_avdtp_sink_transport_get_properties(GstAvdtpSink *self)
+{
+       DBusMessage *msg, *reply;
+       DBusMessageIter arg_i, ele_i;
+       DBusError err;
+
+       dbus_error_init(&err);
+
+       /* Transport need to be acquire first to make sure the MTUs are
+          available */
+       if (gst_avdtp_sink_transport_acquire(self) == FALSE)
+               return FALSE;
+
+       msg = dbus_message_new_method_call("org.bluez", self->transport,
+                                               "org.bluez.MediaTransport",
+                                               "GetProperties");
+       reply = dbus_connection_send_with_reply_and_block(self->data->conn,
+                                                       msg, -1, &err);
+
+       if (dbus_error_is_set(&err) || reply == NULL) {
+               GST_ERROR_OBJECT(self, "Failed to get transport properties: %s",
+                                       err.message);
+               goto fail;
+       }
+
+       if (!dbus_message_iter_init(reply, &arg_i)) {
+               GST_ERROR_OBJECT(self, "GetProperties reply has no arguments.");
+               goto fail;
+       }
+
+       if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+               GST_ERROR_OBJECT(self, "GetProperties argument is not an array.");
+               goto fail;
+       }
+
+       dbus_message_iter_recurse(&arg_i, &ele_i);
+       while (dbus_message_iter_get_arg_type(&ele_i) != DBUS_TYPE_INVALID) {
+
+               if (dbus_message_iter_get_arg_type(&ele_i) ==
+                               DBUS_TYPE_DICT_ENTRY) {
+                       DBusMessageIter dict_i;
+
+                       dbus_message_iter_recurse(&ele_i, &dict_i);
+
+                       gst_avdtp_sink_transport_parse_property(self, &dict_i);
+               }
+
+               if (!dbus_message_iter_next(&ele_i))
+                       break;
+       }
+
+       return gst_avdtp_sink_update_caps(self);
+
+fail:
+       dbus_message_unref(msg);
+       dbus_message_unref(reply);
+       return FALSE;
+
+}
+
+static gboolean gst_avdtp_sink_start(GstBaseSink *basesink)
+{
+       GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+       gint sk;
+       gint err;
+
+       GST_INFO_OBJECT(self, "start");
+
+       self->data = g_new0(struct bluetooth_data, 1);
+
+       self->stream = NULL;
+       self->stream_caps = NULL;
+       self->mp3_using_crc = -1;
+       self->channel_mode = -1;
+
+       if (self->transport != NULL)
+               return gst_avdtp_sink_transport_get_properties(self);
+
+       self->watch_id = 0;
+
+       sk = bt_audio_service_open();
+       if (sk < 0) {
+               err = -errno;
+               GST_ERROR_OBJECT(self, "Cannot open connection to bt "
+                       "audio service: %s %d", strerror(-err), -err);
+               return FALSE;
+       }
+
+       self->server = g_io_channel_unix_new(sk);
+       g_io_channel_set_encoding(self->server, NULL, NULL);
+       self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR |
+                                       G_IO_NVAL, server_callback, self);
+
+       if (!gst_avdtp_sink_get_capabilities(self)) {
+               GST_ERROR_OBJECT(self, "failed to get capabilities "
+                               "from device");
+               goto failed;
+       }
+
+       return TRUE;
+
+failed:
+       bt_audio_service_close(sk);
+       return FALSE;
+}
+
+static gboolean gst_avdtp_sink_stream_start(GstAvdtpSink *self)
+{
+       gchar buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_start_stream_req *req = (void *) buf;
+       struct bt_start_stream_rsp *rsp = (void *) buf;
+       struct bt_new_stream_ind *ind = (void *) buf;
+       int err;
+
+       if (self->transport != NULL)
+               return gst_avdtp_sink_conf_recv_stream_fd(self);
+
+       memset(req, 0, sizeof(buf));
+       req->h.type = BT_REQUEST;
+       req->h.name = BT_START_STREAM;
+       req->h.length = sizeof(*req);
+
+       err = gst_avdtp_sink_audioservice_send(self, &req->h);
+       if (err < 0) {
+               GST_ERROR_OBJECT(self, "Error occurred while sending "
+                                       "start packet");
+               return FALSE;
+       }
+
+       rsp->h.length = sizeof(*rsp);
+       err = gst_avdtp_sink_audioservice_expect(self, &rsp->h,
+                                                       BT_START_STREAM);
+       if (err < 0) {
+               GST_ERROR_OBJECT(self, "Error while stream "
+                       "start confirmation");
+               return FALSE;
+       }
+
+       ind->h.length = sizeof(*ind);
+       err = gst_avdtp_sink_audioservice_expect(self, &ind->h,
+                                                       BT_NEW_STREAM);
+       if (err < 0) {
+               GST_ERROR_OBJECT(self, "Error while receiving "
+                       "stream filedescriptor");
+               return FALSE;
+       }
+
+       if (!gst_avdtp_sink_conf_recv_stream_fd(self))
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean gst_avdtp_sink_init_mp3_pkt_conf(
+               GstAvdtpSink *self, GstCaps *caps,
+               mpeg_capabilities_t *pkt)
+{
+       const GValue *value = NULL;
+       gint rate, layer;
+       const gchar *name;
+       GstStructure *structure = gst_caps_get_structure(caps, 0);
+
+       name = gst_structure_get_name(structure);
+
+       if (!(IS_MPEG_AUDIO(name))) {
+               GST_ERROR_OBJECT(self, "Unexpected format %s, "
+                               "was expecting mp3", name);
+               return FALSE;
+       }
+
+       /* layer */
+       value = gst_structure_get_value(structure, "layer");
+       layer = g_value_get_int(value);
+       if (layer == 1)
+               pkt->layer = BT_MPEG_LAYER_1;
+       else if (layer == 2)
+               pkt->layer = BT_MPEG_LAYER_2;
+       else if (layer == 3)
+               pkt->layer = BT_MPEG_LAYER_3;
+       else {
+               GST_ERROR_OBJECT(self, "Unexpected layer: %d", layer);
+               return FALSE;
+       }
+
+       /* crc */
+       if (self->mp3_using_crc != -1)
+               pkt->crc = self->mp3_using_crc;
+       else {
+               GST_ERROR_OBJECT(self, "No info about crc was received, "
+                               " can't proceed");
+               return FALSE;
+       }
+
+       /* channel mode */
+       if (self->channel_mode != -1)
+               pkt->channel_mode = self->channel_mode;
+       else {
+               GST_ERROR_OBJECT(self, "No info about channel mode "
+                               "received, can't proceed");
+               return FALSE;
+       }
+
+       /* mpf - we will only use the mandatory one */
+       pkt->mpf = 0;
+
+       value = gst_structure_get_value(structure, "rate");
+       rate = g_value_get_int(value);
+       if (rate == 44100)
+               pkt->frequency = BT_MPEG_SAMPLING_FREQ_44100;
+       else if (rate == 48000)
+               pkt->frequency = BT_MPEG_SAMPLING_FREQ_48000;
+       else if (rate == 32000)
+               pkt->frequency = BT_MPEG_SAMPLING_FREQ_32000;
+       else if (rate == 24000)
+               pkt->frequency = BT_MPEG_SAMPLING_FREQ_24000;
+       else if (rate == 22050)
+               pkt->frequency = BT_MPEG_SAMPLING_FREQ_22050;
+       else if (rate == 16000)
+               pkt->frequency = BT_MPEG_SAMPLING_FREQ_16000;
+       else {
+               GST_ERROR_OBJECT(self, "Invalid rate while setting caps");
+               return FALSE;
+       }
+
+       /* vbr - we always say its vbr, we don't have how to know it */
+       pkt->bitrate = 0x8000;
+
+       return TRUE;
+}
+
+static gboolean gst_avdtp_sink_configure(GstAvdtpSink *self,
+                       GstCaps *caps)
+{
+       gchar buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_open_req *open_req = (void *) buf;
+       struct bt_open_rsp *open_rsp = (void *) buf;
+       struct bt_set_configuration_req *req = (void *) buf;
+       struct bt_set_configuration_rsp *rsp = (void *) buf;
+       gboolean ret;
+       gchar *temp;
+       GstStructure *structure;
+       codec_capabilities_t *codec = NULL;
+       int err;
+
+       temp = gst_caps_to_string(caps);
+       GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp);
+       g_free(temp);
+
+       /* Transport already configured */
+       if (self->transport != NULL)
+               return TRUE;
+
+       structure = gst_caps_get_structure(caps, 0);
+
+       if (gst_structure_has_name(structure, "audio/x-sbc"))
+               codec = (void *) gst_avdtp_find_caps(self, BT_A2DP_SBC_SINK);
+       else if (gst_structure_has_name(structure, "audio/mpeg"))
+               codec = (void *) gst_avdtp_find_caps(self, BT_A2DP_MPEG12_SINK);
+
+       if (codec == NULL) {
+               GST_ERROR_OBJECT(self, "Couldn't parse caps "
+                               "to packet configuration");
+               return FALSE;
+       }
+
+       memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+       open_req->h.type = BT_REQUEST;
+       open_req->h.name = BT_OPEN;
+       open_req->h.length = sizeof(*open_req);
+
+       strncpy(open_req->destination, self->device, 18);
+       open_req->seid = codec->seid;
+       open_req->lock = BT_WRITE_LOCK;
+
+       err = gst_avdtp_sink_audioservice_send(self, &open_req->h);
+       if (err < 0) {
+               GST_ERROR_OBJECT(self, "Error occurred while sending "
+                                       "open packet");
+               return FALSE;
+       }
+
+       open_rsp->h.length = sizeof(*open_rsp);
+       err = gst_avdtp_sink_audioservice_expect(self, &open_rsp->h,
+                                                               BT_OPEN);
+       if (err < 0) {
+               GST_ERROR_OBJECT(self, "Error while receiving device "
+                                       "confirmation");
+               return FALSE;
+       }
+
+       memset(req, 0, sizeof(buf));
+       req->h.type = BT_REQUEST;
+       req->h.name = BT_SET_CONFIGURATION;
+       req->h.length = sizeof(*req);
+       memcpy(&req->codec, codec, sizeof(req->codec));
+
+       if (codec->type == BT_A2DP_SBC_SINK)
+               ret = gst_avdtp_sink_init_sbc_pkt_conf(self, caps,
+                               (void *) &req->codec);
+       else
+               ret = gst_avdtp_sink_init_mp3_pkt_conf(self, caps,
+                               (void *) &req->codec);
+
+       if (!ret) {
+               GST_ERROR_OBJECT(self, "Couldn't parse caps "
+                               "to packet configuration");
+               return FALSE;
+       }
+
+       req->h.length += req->codec.length - sizeof(req->codec);
+       err = gst_avdtp_sink_audioservice_send(self, &req->h);
+       if (err < 0) {
+               GST_ERROR_OBJECT(self, "Error occurred while sending "
+                                       "configurarion packet");
+               return FALSE;
+       }
+
+       rsp->h.length = sizeof(*rsp);
+       err = gst_avdtp_sink_audioservice_expect(self, &rsp->h,
+                                                       BT_SET_CONFIGURATION);
+       if (err < 0) {
+               GST_ERROR_OBJECT(self, "Error while receiving device "
+                                       "confirmation");
+               return FALSE;
+       }
+
+       self->data->link_mtu = rsp->link_mtu;
+
+       return TRUE;
+}
+
+static GstFlowReturn gst_avdtp_sink_preroll(GstBaseSink *basesink,
+                                       GstBuffer *buffer)
+{
+       GstAvdtpSink *sink = GST_AVDTP_SINK(basesink);
+       gboolean ret;
+
+       GST_AVDTP_SINK_MUTEX_LOCK(sink);
+
+       ret = gst_avdtp_sink_stream_start(sink);
+
+       GST_AVDTP_SINK_MUTEX_UNLOCK(sink);
+
+       if (!ret)
+               return GST_FLOW_ERROR;
+
+       return GST_FLOW_OK;
+}
+
+static GstFlowReturn gst_avdtp_sink_render(GstBaseSink *basesink,
+                                       GstBuffer *buffer)
+{
+       GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+       ssize_t ret;
+       int fd;
+
+       fd = g_io_channel_unix_get_fd(self->stream);
+
+       ret = write(fd, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
+       if (ret < 0) {
+               GST_ERROR_OBJECT(self, "Error while writting to socket: %s",
+                                                       strerror(errno));
+               return GST_FLOW_ERROR;
+       }
+
+       return GST_FLOW_OK;
+}
+
+static gboolean gst_avdtp_sink_unlock(GstBaseSink *basesink)
+{
+       GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+
+       if (self->stream != NULL)
+               g_io_channel_flush(self->stream, NULL);
+
+       return TRUE;
+}
+
+static GstFlowReturn gst_avdtp_sink_buffer_alloc(GstBaseSink *basesink,
+                               guint64 offset, guint size, GstCaps *caps,
+                               GstBuffer **buf)
+{
+       GstAvdtpSink *self = GST_AVDTP_SINK(basesink);
+
+       *buf = gst_buffer_new_and_alloc(size);
+       if (!(*buf)) {
+               GST_ERROR_OBJECT(self, "buffer allocation failed");
+               return GST_FLOW_ERROR;
+       }
+
+       gst_buffer_set_caps(*buf, caps);
+
+       GST_BUFFER_OFFSET(*buf) = offset;
+
+       return GST_FLOW_OK;
+}
+
+static void gst_avdtp_sink_class_init(GstAvdtpSinkClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS(klass);
+       GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass);
+
+       parent_class = g_type_class_peek_parent(klass);
+
+       object_class->finalize = GST_DEBUG_FUNCPTR(
+                                       gst_avdtp_sink_finalize);
+       object_class->set_property = GST_DEBUG_FUNCPTR(
+                                       gst_avdtp_sink_set_property);
+       object_class->get_property = GST_DEBUG_FUNCPTR(
+                                       gst_avdtp_sink_get_property);
+
+       basesink_class->start = GST_DEBUG_FUNCPTR(gst_avdtp_sink_start);
+       basesink_class->stop = GST_DEBUG_FUNCPTR(gst_avdtp_sink_stop);
+       basesink_class->render = GST_DEBUG_FUNCPTR(
+                                       gst_avdtp_sink_render);
+       basesink_class->preroll = GST_DEBUG_FUNCPTR(
+                                       gst_avdtp_sink_preroll);
+       basesink_class->unlock = GST_DEBUG_FUNCPTR(
+                                       gst_avdtp_sink_unlock);
+       basesink_class->event = GST_DEBUG_FUNCPTR(
+                                       gst_avdtp_sink_event);
+
+       basesink_class->buffer_alloc =
+               GST_DEBUG_FUNCPTR(gst_avdtp_sink_buffer_alloc);
+
+       g_object_class_install_property(object_class, PROP_DEVICE,
+                                       g_param_spec_string("device", "Device",
+                                       "Bluetooth remote device address",
+                                       NULL, G_PARAM_READWRITE));
+
+       g_object_class_install_property(object_class, PROP_AUTOCONNECT,
+                                       g_param_spec_boolean("auto-connect",
+                                       "Auto-connect",
+                                       "Automatically attempt to connect "
+                                       "to device", DEFAULT_AUTOCONNECT,
+                                       G_PARAM_READWRITE));
+
+       g_object_class_install_property(object_class, PROP_TRANSPORT,
+                                       g_param_spec_string("transport",
+                                       "Transport",
+                                       "Use configured transport",
+                                       NULL, G_PARAM_READWRITE));
+
+       GST_DEBUG_CATEGORY_INIT(avdtp_sink_debug, "avdtpsink", 0,
+                               "A2DP headset sink element");
+}
+
+static void gst_avdtp_sink_init(GstAvdtpSink *self,
+                       GstAvdtpSinkClass *klass)
+{
+       self->device = NULL;
+       self->transport = NULL;
+       self->data = NULL;
+
+       self->stream = NULL;
+
+       self->dev_caps = NULL;
+
+       self->autoconnect = DEFAULT_AUTOCONNECT;
+
+       self->sink_lock = g_mutex_new();
+
+       /* FIXME this is for not synchronizing with clock, should be tested
+        * with devices to see the behaviour
+       gst_base_sink_set_sync(GST_BASE_SINK(self), FALSE);
+       */
+}
+
+static int gst_avdtp_sink_audioservice_send(GstAvdtpSink *self,
+                                       const bt_audio_msg_header_t *msg)
+{
+       ssize_t written;
+       const char *type, *name;
+       uint16_t length;
+       int fd, err;
+
+       length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
+
+       fd = g_io_channel_unix_get_fd(self->server);
+
+       written = write(fd, msg, length);
+       if (written < 0) {
+               err = -errno;
+               GST_ERROR_OBJECT(self, "Error sending data to audio service:"
+                       " %s", strerror(-err));
+               return err;
+       }
+
+       type = bt_audio_strtype(msg->type);
+       name = bt_audio_strname(msg->name);
+
+       GST_DEBUG_OBJECT(self, "sent: %s -> %s", type, name);
+
+       return 0;
+}
+
+static int gst_avdtp_sink_audioservice_recv(GstAvdtpSink *self,
+                                               bt_audio_msg_header_t *inmsg)
+{
+       ssize_t bytes_read;
+       const char *type, *name;
+       uint16_t length;
+       int fd, err = 0;
+
+       length = inmsg->length ? inmsg->length : BT_SUGGESTED_BUFFER_SIZE;
+
+       fd = g_io_channel_unix_get_fd(self->server);
+
+       bytes_read = read(fd, inmsg, length);
+       if (bytes_read < 0) {
+               err = -errno;
+               GST_ERROR_OBJECT(self, "Error receiving data from "
+                               "audio service: %s", strerror(-err));
+               return err;
+       }
+
+       type = bt_audio_strtype(inmsg->type);
+       if (!type) {
+               err = -EINVAL;
+               GST_ERROR_OBJECT(self, "Bogus message type %d "
+                               "received from audio service",
+                               inmsg->type);
+       }
+
+       name = bt_audio_strname(inmsg->name);
+       if (!name) {
+               err = -EINVAL;
+               GST_ERROR_OBJECT(self, "Bogus message name %d "
+                               "received from audio service",
+                               inmsg->name);
+       }
+
+       if (inmsg->type == BT_ERROR) {
+               bt_audio_error_t *msg = (void *) inmsg;
+               err = -EINVAL;
+               GST_ERROR_OBJECT(self, "%s failed : "
+                                       "%s(%d)",
+                                       name,
+                                       strerror(msg->posix_errno),
+                                       msg->posix_errno);
+       }
+
+       GST_DEBUG_OBJECT(self, "received: %s <- %s", type, name);
+
+       return err;
+}
+
+static int gst_avdtp_sink_audioservice_expect(GstAvdtpSink *self,
+                                               bt_audio_msg_header_t *outmsg,
+                                               guint8 expected_name)
+{
+       int err;
+
+       err = gst_avdtp_sink_audioservice_recv(self, outmsg);
+       if (err < 0)
+               return err;
+
+       if (outmsg->name != expected_name)
+               return -EINVAL;
+
+       return 0;
+}
+
+gboolean gst_avdtp_sink_plugin_init(GstPlugin *plugin)
+{
+       return gst_element_register(plugin, "avdtpsink", GST_RANK_NONE,
+                                                       GST_TYPE_AVDTP_SINK);
+}
+
+
+/* public functions */
+GstCaps *gst_avdtp_sink_get_device_caps(GstAvdtpSink *sink)
+{
+       if (sink->dev_caps == NULL)
+               return NULL;
+
+       return gst_caps_copy(sink->dev_caps);
+}
+
+gboolean gst_avdtp_sink_set_device_caps(GstAvdtpSink *self,
+                       GstCaps *caps)
+{
+       gboolean ret;
+
+       GST_DEBUG_OBJECT(self, "setting device caps");
+       GST_AVDTP_SINK_MUTEX_LOCK(self);
+       ret = gst_avdtp_sink_configure(self, caps);
+
+       if (self->stream_caps)
+               gst_caps_unref(self->stream_caps);
+       self->stream_caps = gst_caps_ref(caps);
+
+       GST_AVDTP_SINK_MUTEX_UNLOCK(self);
+
+       return ret;
+}
+
+guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink)
+{
+       return sink->data->link_mtu;
+}
+
+void gst_avdtp_sink_set_device(GstAvdtpSink *self, const gchar *dev)
+{
+       if (self->device != NULL)
+               g_free(self->device);
+
+       GST_LOG_OBJECT(self, "Setting device: %s", dev);
+       self->device = g_strdup(dev);
+}
+
+void gst_avdtp_sink_set_transport(GstAvdtpSink *self, const gchar *trans)
+{
+       if (self->transport != NULL)
+               g_free(self->transport);
+
+       GST_LOG_OBJECT(self, "Setting transport: %s", trans);
+       self->transport = g_strdup(trans);
+}
+
+gchar *gst_avdtp_sink_get_device(GstAvdtpSink *self)
+{
+       return g_strdup(self->device);
+}
+
+gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *self)
+{
+       return g_strdup(self->transport);
+}
+
+void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc)
+{
+       gint new_crc;
+
+       new_crc = crc ? CRC_PROTECTED : CRC_UNPROTECTED;
+
+       /* test if we already received a different crc */
+       if (self->mp3_using_crc != -1 && new_crc != self->mp3_using_crc) {
+               GST_WARNING_OBJECT(self, "crc changed during stream");
+               return;
+       }
+       self->mp3_using_crc = new_crc;
+
+}
+
+void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self,
+                       const gchar *mode)
+{
+       gint new_mode;
+
+       new_mode = gst_avdtp_sink_get_channel_mode(mode);
+
+       if (self->channel_mode != -1 && new_mode != self->channel_mode) {
+               GST_WARNING_OBJECT(self, "channel mode changed during stream");
+               return;
+       }
+
+       self->channel_mode = new_mode;
+       if (self->channel_mode == -1)
+               GST_WARNING_OBJECT(self, "Received invalid channel "
+                               "mode: %s", mode);
+}
diff --git a/audio/gstavdtpsink.h b/audio/gstavdtpsink.h
new file mode 100644 (file)
index 0000000..c4e5645
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GST_AVDTP_SINK_H
+#define __GST_AVDTP_SINK_H
+
+#include <gst/gst.h>
+#include <gst/base/gstbasesink.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_AVDTP_SINK \
+       (gst_avdtp_sink_get_type())
+#define GST_AVDTP_SINK(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVDTP_SINK,\
+               GstAvdtpSink))
+#define GST_AVDTP_SINK_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVDTP_SINK,\
+               GstAvdtpSinkClass))
+#define GST_IS_AVDTP_SINK(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVDTP_SINK))
+#define GST_IS_AVDTP_SINK_CLASS(obj) \
+       (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVDTP_SINK))
+
+typedef struct _GstAvdtpSink GstAvdtpSink;
+typedef struct _GstAvdtpSinkClass GstAvdtpSinkClass;
+
+struct bluetooth_data;
+
+struct _GstAvdtpSink {
+       GstBaseSink sink;
+
+       gchar *device;
+       gchar *transport;
+       GIOChannel *stream;
+
+       struct bluetooth_data *data;
+       gboolean autoconnect;
+       GIOChannel *server;
+
+       /* mp3 stream data (outside caps data)*/
+       gint mp3_using_crc;
+       gint channel_mode;
+
+       /* stream connection data */
+       GstCaps *stream_caps;
+
+       GstCaps *dev_caps;
+
+       GMutex *sink_lock;
+
+       guint watch_id;
+};
+
+struct _GstAvdtpSinkClass {
+       GstBaseSinkClass parent_class;
+};
+
+GType gst_avdtp_sink_get_type(void);
+
+GstCaps *gst_avdtp_sink_get_device_caps(GstAvdtpSink *sink);
+gboolean gst_avdtp_sink_set_device_caps(GstAvdtpSink *sink,
+                       GstCaps *caps);
+
+guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink);
+
+void gst_avdtp_sink_set_device(GstAvdtpSink *sink,
+               const gchar* device);
+
+void gst_avdtp_sink_set_transport(GstAvdtpSink *sink,
+               const gchar *transport);
+
+gchar *gst_avdtp_sink_get_device(GstAvdtpSink *sink);
+
+gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *sink);
+
+gboolean gst_avdtp_sink_plugin_init(GstPlugin *plugin);
+
+void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc);
+
+void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self,
+                       const gchar *mode);
+
+
+G_END_DECLS
+
+#endif /* __GST_AVDTP_SINK_H */
diff --git a/audio/gstbluetooth.c b/audio/gstbluetooth.c
new file mode 100644 (file)
index 0000000..9930820
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <gst/gst.h>
+
+#include "gstsbcutil.h"
+#include <sbc.h>
+
+#include "gstsbcenc.h"
+#include "gstsbcdec.h"
+#include "gstsbcparse.h"
+#include "gstavdtpsink.h"
+#include "gsta2dpsink.h"
+#include "gstrtpsbcpay.h"
+
+static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc");
+
+#define SBC_CAPS (gst_static_caps_get(&sbc_caps))
+
+static void sbc_typefind(GstTypeFind *tf, gpointer ignore)
+{
+       GstCaps *caps;
+       guint8 *aux;
+       sbc_t sbc;
+       guint8 *data = gst_type_find_peek(tf, 0, 32);
+
+       if (data == NULL)
+               return;
+
+       if (sbc_init(&sbc, 0) < 0)
+               return;
+
+       aux = g_new(guint8, 32);
+       memcpy(aux, data, 32);
+       if (sbc_parse(&sbc, aux, 32) < 0)
+               goto done;
+
+       caps = gst_sbc_parse_caps_from_sbc(&sbc);
+       gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, caps);
+       gst_caps_unref(caps);
+
+done:
+       g_free(aux);
+       sbc_finish(&sbc);
+}
+
+static gchar *sbc_exts[] = { "sbc", NULL };
+
+static gboolean plugin_init(GstPlugin *plugin)
+{
+       GST_INFO("Bluetooth plugin %s", VERSION);
+
+       if (gst_type_find_register(plugin, "sbc",
+                       GST_RANK_PRIMARY, sbc_typefind, sbc_exts,
+                                       SBC_CAPS, NULL, NULL) == FALSE)
+               return FALSE;
+
+       if (!gst_sbc_enc_plugin_init(plugin))
+               return FALSE;
+
+       if (!gst_sbc_dec_plugin_init(plugin))
+               return FALSE;
+
+       if (!gst_sbc_parse_plugin_init(plugin))
+               return FALSE;
+
+       if (!gst_avdtp_sink_plugin_init(plugin))
+               return FALSE;
+
+       if (!gst_a2dp_sink_plugin_init(plugin))
+               return FALSE;
+
+       if (!gst_rtp_sbc_pay_plugin_init(plugin))
+               return FALSE;
+
+       return TRUE;
+}
+
+extern GstPluginDesc gst_plugin_desc __attribute__ ((visibility("default")));
+
+GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR,
+       "bluetooth", "Bluetooth plugin library",
+       plugin_init, VERSION, "LGPL", "BlueZ", "http://www.bluez.org/")
diff --git a/audio/gstpragma.h b/audio/gstpragma.h
new file mode 100644 (file)
index 0000000..0cb08d2
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/* #pragma GCC diagnostic warning "-Wmissing-declarations" */
diff --git a/audio/gstrtpsbcpay.c b/audio/gstrtpsbcpay.c
new file mode 100644 (file)
index 0000000..11aa733
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gstpragma.h"
+#include "gstrtpsbcpay.h"
+#include <math.h>
+#include <string.h>
+
+#define RTP_SBC_PAYLOAD_HEADER_SIZE 1
+#define DEFAULT_MIN_FRAMES 0
+#define RTP_SBC_HEADER_TOTAL (12 + RTP_SBC_PAYLOAD_HEADER_SIZE)
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_payload {
+       guint8 frame_count:4;
+       guint8 rfa0:1;
+       guint8 is_last_fragment:1;
+       guint8 is_first_fragment:1;
+       guint8 is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_payload {
+       guint8 is_fragmented:1;
+       guint8 is_first_fragment:1;
+       guint8 is_last_fragment:1;
+       guint8 rfa0:1;
+       guint8 frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+enum {
+       PROP_0,
+       PROP_MIN_FRAMES
+};
+
+GST_DEBUG_CATEGORY_STATIC(gst_rtp_sbc_pay_debug);
+#define GST_CAT_DEFAULT gst_rtp_sbc_pay_debug
+
+GST_BOILERPLATE(GstRtpSBCPay, gst_rtp_sbc_pay, GstBaseRTPPayload,
+               GST_TYPE_BASE_RTP_PAYLOAD);
+
+static const GstElementDetails gst_rtp_sbc_pay_details =
+       GST_ELEMENT_DETAILS("RTP packet payloader",
+                               "Codec/Payloader/Network",
+                               "Payload SBC audio as RTP packets",
+                               "Thiago Sousa Santos "
+                               "<thiagoss@lcc.ufcg.edu.br>");
+
+static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory =
+       GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+               GST_STATIC_CAPS("audio/x-sbc, "
+                               "rate = (int) { 16000, 32000, 44100, 48000 }, "
+                               "channels = (int) [ 1, 2 ], "
+                               "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
+                               "blocks = (int) { 4, 8, 12, 16 }, "
+                               "subbands = (int) { 4, 8 }, "
+                               "allocation = (string) { \"snr\", \"loudness\" }, "
+                               "bitpool = (int) [ 2, 64 ]")
+       );
+
+static GstStaticPadTemplate gst_rtp_sbc_pay_src_factory =
+       GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+               GST_STATIC_CAPS(
+                       "application/x-rtp, "
+                       "media = (string) \"audio\","
+                       "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+                       "clock-rate = (int) { 16000, 32000, 44100, 48000 },"
+                       "encoding-name = (string) \"SBC\"")
+       );
+
+static void gst_rtp_sbc_pay_set_property(GObject *object, guint prop_id,
+                               const GValue *value, GParamSpec *pspec);
+static void gst_rtp_sbc_pay_get_property(GObject *object, guint prop_id,
+                               GValue *value, GParamSpec *pspec);
+
+static gint gst_rtp_sbc_pay_get_frame_len(gint subbands, gint channels,
+               gint blocks, gint bitpool, const gchar *channel_mode)
+{
+       gint len;
+       gint join;
+
+       len = 4 + (4 * subbands * channels)/8;
+
+       if (strcmp(channel_mode, "mono") == 0 ||
+               strcmp(channel_mode, "dual") == 0)
+               len += ((blocks * channels * bitpool) + 7) / 8;
+       else {
+               join = strcmp(channel_mode, "joint") == 0 ? 1 : 0;
+               len += ((join * subbands + blocks * bitpool) + 7) / 8;
+       }
+
+       return len;
+}
+
+static gboolean gst_rtp_sbc_pay_set_caps(GstBaseRTPPayload *payload,
+                       GstCaps *caps)
+{
+       GstRtpSBCPay *sbcpay;
+       gint rate, subbands, channels, blocks, bitpool;
+       gint frame_len;
+       const gchar *channel_mode;
+       GstStructure *structure;
+
+       sbcpay = GST_RTP_SBC_PAY(payload);
+
+       structure = gst_caps_get_structure(caps, 0);
+       if (!gst_structure_get_int(structure, "rate", &rate))
+               return FALSE;
+       if (!gst_structure_get_int(structure, "channels", &channels))
+               return FALSE;
+       if (!gst_structure_get_int(structure, "blocks", &blocks))
+               return FALSE;
+       if (!gst_structure_get_int(structure, "bitpool", &bitpool))
+               return FALSE;
+       if (!gst_structure_get_int(structure, "subbands", &subbands))
+               return FALSE;
+
+       channel_mode = gst_structure_get_string(structure, "mode");
+       if (!channel_mode)
+               return FALSE;
+
+       frame_len = gst_rtp_sbc_pay_get_frame_len(subbands, channels, blocks,
+                               bitpool, channel_mode);
+
+       sbcpay->frame_length = frame_len;
+
+       gst_basertppayload_set_options(payload, "audio", TRUE, "SBC", rate);
+
+       GST_DEBUG_OBJECT(payload, "calculated frame length: %d ", frame_len);
+
+       return gst_basertppayload_set_outcaps(payload, NULL);
+}
+
+static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay)
+{
+       guint available;
+       guint max_payload;
+       GstBuffer *outbuf;
+       guint8 *payload_data;
+       guint frame_count;
+       guint payload_length;
+       struct rtp_payload *payload;
+
+       if (sbcpay->frame_length == 0) {
+               GST_ERROR_OBJECT(sbcpay, "Frame length is 0");
+               return GST_FLOW_ERROR;
+       }
+
+       available = gst_adapter_available(sbcpay->adapter);
+
+       max_payload = gst_rtp_buffer_calc_payload_len(
+               GST_BASE_RTP_PAYLOAD_MTU(sbcpay) - RTP_SBC_PAYLOAD_HEADER_SIZE,
+               0, 0);
+
+       max_payload = MIN(max_payload, available);
+       frame_count = max_payload / sbcpay->frame_length;
+       payload_length = frame_count * sbcpay->frame_length;
+       if (payload_length == 0) /* Nothing to send */
+               return GST_FLOW_OK;
+
+       outbuf = gst_rtp_buffer_new_allocate(payload_length +
+                       RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0);
+
+       gst_rtp_buffer_set_payload_type(outbuf,
+                       GST_BASE_RTP_PAYLOAD_PT(sbcpay));
+
+       payload_data = gst_rtp_buffer_get_payload(outbuf);
+       payload = (struct rtp_payload *) payload_data;
+       memset(payload, 0, sizeof(struct rtp_payload));
+       payload->frame_count = frame_count;
+
+       gst_adapter_copy(sbcpay->adapter, payload_data +
+                       RTP_SBC_PAYLOAD_HEADER_SIZE, 0, payload_length);
+       gst_adapter_flush(sbcpay->adapter, payload_length);
+
+       GST_BUFFER_TIMESTAMP(outbuf) = sbcpay->timestamp;
+       GST_DEBUG_OBJECT(sbcpay, "Pushing %d bytes", payload_length);
+
+       return gst_basertppayload_push(GST_BASE_RTP_PAYLOAD(sbcpay), outbuf);
+}
+
+static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload,
+                       GstBuffer *buffer)
+{
+       GstRtpSBCPay *sbcpay;
+       guint available;
+
+       /* FIXME check for negotiation */
+
+       sbcpay = GST_RTP_SBC_PAY(payload);
+       sbcpay->timestamp = GST_BUFFER_TIMESTAMP(buffer);
+
+       gst_adapter_push(sbcpay->adapter, buffer);
+
+       available = gst_adapter_available(sbcpay->adapter);
+       if (available + RTP_SBC_HEADER_TOTAL >=
+                               GST_BASE_RTP_PAYLOAD_MTU(sbcpay) ||
+                       (available >
+                               (sbcpay->min_frames * sbcpay->frame_length)))
+               return gst_rtp_sbc_pay_flush_buffers(sbcpay);
+
+       return GST_FLOW_OK;
+}
+
+static gboolean gst_rtp_sbc_pay_handle_event(GstPad *pad,
+                               GstEvent *event)
+{
+       GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(GST_PAD_PARENT(pad));
+
+       switch (GST_EVENT_TYPE(event)) {
+       case GST_EVENT_EOS:
+               gst_rtp_sbc_pay_flush_buffers(sbcpay);
+               break;
+       default:
+               break;
+       }
+
+       return FALSE;
+}
+
+static void gst_rtp_sbc_pay_base_init(gpointer g_class)
+{
+       GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+       gst_element_class_add_pad_template(element_class,
+               gst_static_pad_template_get(&gst_rtp_sbc_pay_sink_factory));
+       gst_element_class_add_pad_template(element_class,
+               gst_static_pad_template_get(&gst_rtp_sbc_pay_src_factory));
+
+       gst_element_class_set_details(element_class, &gst_rtp_sbc_pay_details);
+}
+
+static void gst_rtp_sbc_pay_finalize(GObject *object)
+{
+       GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(object);
+       g_object_unref(sbcpay->adapter);
+
+       GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
+}
+
+static void gst_rtp_sbc_pay_class_init(GstRtpSBCPayClass *klass)
+{
+       GObjectClass *gobject_class;
+       GstBaseRTPPayloadClass *payload_class =
+               GST_BASE_RTP_PAYLOAD_CLASS(klass);
+
+       gobject_class = G_OBJECT_CLASS(klass);
+       parent_class = g_type_class_peek_parent(klass);
+
+       gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_finalize);
+       gobject_class->set_property = GST_DEBUG_FUNCPTR(
+                       gst_rtp_sbc_pay_set_property);
+       gobject_class->get_property = GST_DEBUG_FUNCPTR(
+                       gst_rtp_sbc_pay_get_property);
+
+       payload_class->set_caps = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_set_caps);
+       payload_class->handle_buffer = GST_DEBUG_FUNCPTR(
+                       gst_rtp_sbc_pay_handle_buffer);
+       payload_class->handle_event = GST_DEBUG_FUNCPTR(
+                       gst_rtp_sbc_pay_handle_event);
+
+       /* properties */
+       g_object_class_install_property(G_OBJECT_CLASS(klass),
+               PROP_MIN_FRAMES,
+               g_param_spec_int("min-frames", "minimum frame number",
+               "Minimum quantity of frames to send in one packet "
+               "(-1 for maximum allowed by the mtu)",
+               -1, G_MAXINT, DEFAULT_MIN_FRAMES, G_PARAM_READWRITE));
+
+       GST_DEBUG_CATEGORY_INIT(gst_rtp_sbc_pay_debug, "rtpsbcpay", 0,
+                               "RTP SBC payloader");
+}
+
+static void gst_rtp_sbc_pay_set_property(GObject *object, guint prop_id,
+                                       const GValue *value, GParamSpec *pspec)
+{
+       GstRtpSBCPay *sbcpay;
+
+       sbcpay = GST_RTP_SBC_PAY(object);
+
+       switch (prop_id) {
+       case PROP_MIN_FRAMES:
+               sbcpay->min_frames = g_value_get_int(value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+       break;
+       }
+}
+
+static void gst_rtp_sbc_pay_get_property(GObject *object, guint prop_id,
+                                       GValue *value, GParamSpec *pspec)
+{
+       GstRtpSBCPay *sbcpay;
+
+       sbcpay = GST_RTP_SBC_PAY(object);
+
+       switch (prop_id) {
+       case PROP_MIN_FRAMES:
+               g_value_set_int(value, sbcpay->min_frames);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+       break;
+       }
+}
+
+static void gst_rtp_sbc_pay_init(GstRtpSBCPay *self, GstRtpSBCPayClass *klass)
+{
+       self->adapter = gst_adapter_new();
+       self->frame_length = 0;
+       self->timestamp = 0;
+
+       self->min_frames = DEFAULT_MIN_FRAMES;
+}
+
+gboolean gst_rtp_sbc_pay_plugin_init(GstPlugin *plugin)
+{
+       return gst_element_register(plugin, "rtpsbcpay", GST_RANK_NONE,
+                                                       GST_TYPE_RTP_SBC_PAY);
+}
diff --git a/audio/gstrtpsbcpay.h b/audio/gstrtpsbcpay.h
new file mode 100644 (file)
index 0000000..398de82
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <gst/gst.h>
+#include <gst/rtp/gstbasertppayload.h>
+#include <gst/base/gstadapter.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_RTP_SBC_PAY \
+       (gst_rtp_sbc_pay_get_type())
+#define GST_RTP_SBC_PAY(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SBC_PAY,\
+               GstRtpSBCPay))
+#define GST_RTP_SBC_PAY_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SBC_PAY,\
+               GstRtpSBCPayClass))
+#define GST_IS_RTP_SBC_PAY(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SBC_PAY))
+#define GST_IS_RTP_SBC_PAY_CLASS(obj) \
+       (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SBC_PAY))
+
+typedef struct _GstRtpSBCPay GstRtpSBCPay;
+typedef struct _GstRtpSBCPayClass GstRtpSBCPayClass;
+
+struct _GstRtpSBCPay {
+       GstBaseRTPPayload base;
+
+       GstAdapter *adapter;
+       GstClockTime timestamp;
+
+       guint frame_length;
+
+       guint min_frames;
+};
+
+struct _GstRtpSBCPayClass {
+       GstBaseRTPPayloadClass parent_class;
+};
+
+GType gst_rtp_sbc_pay_get_type(void);
+
+gboolean gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin);
+
+G_END_DECLS
diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c
new file mode 100644 (file)
index 0000000..c52834e
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "gstpragma.h"
+#include "gstsbcutil.h"
+#include "gstsbcdec.h"
+
+GST_DEBUG_CATEGORY_STATIC(sbc_dec_debug);
+#define GST_CAT_DEFAULT sbc_dec_debug
+
+GST_BOILERPLATE(GstSbcDec, gst_sbc_dec, GstElement, GST_TYPE_ELEMENT);
+
+static const GstElementDetails sbc_dec_details =
+       GST_ELEMENT_DETAILS("Bluetooth SBC decoder",
+                               "Codec/Decoder/Audio",
+                               "Decode a SBC audio stream",
+                               "Marcel Holtmann <marcel@holtmann.org>");
+
+static GstStaticPadTemplate sbc_dec_sink_factory =
+       GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+               GST_STATIC_CAPS("audio/x-sbc"));
+
+static GstStaticPadTemplate sbc_dec_src_factory =
+       GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+               GST_STATIC_CAPS("audio/x-raw-int, "
+                               "rate = (int) { 16000, 32000, 44100, 48000 }, "
+                               "channels = (int) [ 1, 2 ], "
+                               "endianness = (int) BYTE_ORDER, "
+                               "signed = (boolean) true, "
+                               "width = (int) 16, "
+                               "depth = (int) 16"));
+
+static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer)
+{
+       GstSbcDec *dec = GST_SBC_DEC(gst_pad_get_parent(pad));
+       GstFlowReturn res = GST_FLOW_OK;
+       guint size, codesize, offset = 0;
+       guint8 *data;
+
+       codesize = sbc_get_codesize(&dec->sbc);
+
+       if (dec->buffer) {
+               GstBuffer *temp = buffer;
+               buffer = gst_buffer_span(dec->buffer, 0, buffer,
+                       GST_BUFFER_SIZE(dec->buffer) + GST_BUFFER_SIZE(buffer));
+               gst_buffer_unref(temp);
+               gst_buffer_unref(dec->buffer);
+               dec->buffer = NULL;
+       }
+
+       data = GST_BUFFER_DATA(buffer);
+       size = GST_BUFFER_SIZE(buffer);
+
+       while (offset < size) {
+               GstBuffer *output;
+               GstPadTemplate *template;
+               GstCaps *caps;
+               int consumed;
+
+               res = gst_pad_alloc_buffer_and_set_caps(dec->srcpad,
+                                               GST_BUFFER_OFFSET_NONE,
+                                               codesize, NULL, &output);
+
+               if (res != GST_FLOW_OK)
+                       goto done;
+
+               consumed = sbc_decode(&dec->sbc, data + offset, size - offset,
+                                       GST_BUFFER_DATA(output), codesize,
+                                       NULL);
+               if (consumed <= 0)
+                       break;
+
+               /* we will reuse the same caps object */
+               if (dec->outcaps == NULL) {
+                       caps = gst_caps_new_simple("audio/x-raw-int",
+                                       "rate", G_TYPE_INT,
+                                       gst_sbc_parse_rate_from_sbc(
+                                               dec->sbc.frequency),
+                                       "channels", G_TYPE_INT,
+                                       gst_sbc_get_channel_number(
+                                               dec->sbc.mode),
+                                       NULL);
+
+                       template = gst_static_pad_template_get(&sbc_dec_src_factory);
+
+                       dec->outcaps = gst_caps_intersect(caps,
+                                               gst_pad_template_get_caps(template));
+
+                       gst_caps_unref(caps);
+               }
+
+               gst_buffer_set_caps(output, dec->outcaps);
+
+               /* FIXME get a real timestamp */
+               GST_BUFFER_TIMESTAMP(output) = GST_CLOCK_TIME_NONE;
+
+               res = gst_pad_push(dec->srcpad, output);
+               if (res != GST_FLOW_OK)
+                       goto done;
+
+               offset += consumed;
+       }
+
+       if (offset < size)
+               dec->buffer = gst_buffer_create_sub(buffer,
+                                                       offset, size - offset);
+
+done:
+       gst_buffer_unref(buffer);
+       gst_object_unref(dec);
+
+       return res;
+}
+
+static GstStateChangeReturn sbc_dec_change_state(GstElement *element,
+                                               GstStateChange transition)
+{
+       GstSbcDec *dec = GST_SBC_DEC(element);
+
+       switch (transition) {
+       case GST_STATE_CHANGE_READY_TO_PAUSED:
+               GST_DEBUG("Setup subband codec");
+               if (dec->buffer) {
+                       gst_buffer_unref(dec->buffer);
+                       dec->buffer = NULL;
+               }
+               sbc_init(&dec->sbc, 0);
+               dec->outcaps = NULL;
+               break;
+
+       case GST_STATE_CHANGE_PAUSED_TO_READY:
+               GST_DEBUG("Finish subband codec");
+               if (dec->buffer) {
+                       gst_buffer_unref(dec->buffer);
+                       dec->buffer = NULL;
+               }
+               sbc_finish(&dec->sbc);
+               if (dec->outcaps) {
+                       gst_caps_unref(dec->outcaps);
+                       dec->outcaps = NULL;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       return parent_class->change_state(element, transition);
+}
+
+static void gst_sbc_dec_base_init(gpointer g_class)
+{
+       GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+       gst_element_class_add_pad_template(element_class,
+               gst_static_pad_template_get(&sbc_dec_sink_factory));
+
+       gst_element_class_add_pad_template(element_class,
+               gst_static_pad_template_get(&sbc_dec_src_factory));
+
+       gst_element_class_set_details(element_class, &sbc_dec_details);
+}
+
+static void gst_sbc_dec_class_init(GstSbcDecClass *klass)
+{
+       GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+       parent_class = g_type_class_peek_parent(klass);
+
+       element_class->change_state = GST_DEBUG_FUNCPTR(sbc_dec_change_state);
+
+       GST_DEBUG_CATEGORY_INIT(sbc_dec_debug, "sbcdec", 0,
+                                               "SBC decoding element");
+}
+
+static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass)
+{
+       self->sinkpad = gst_pad_new_from_static_template(
+                       &sbc_dec_sink_factory, "sink");
+       gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR(
+                       sbc_dec_chain));
+       gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
+
+       self->srcpad = gst_pad_new_from_static_template(
+                       &sbc_dec_src_factory, "src");
+       gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
+
+       self->outcaps = NULL;
+}
+
+gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin)
+{
+       return gst_element_register(plugin, "sbcdec", GST_RANK_PRIMARY,
+                                                       GST_TYPE_SBC_DEC);
+}
diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h
new file mode 100644 (file)
index 0000000..c77feae
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <gst/gst.h>
+
+#include "sbc.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SBC_DEC \
+       (gst_sbc_dec_get_type())
+#define GST_SBC_DEC(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_DEC,GstSbcDec))
+#define GST_SBC_DEC_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_DEC,GstSbcDecClass))
+#define GST_IS_SBC_DEC(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_DEC))
+#define GST_IS_SBC_DEC_CLASS(obj) \
+       (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_DEC))
+
+typedef struct _GstSbcDec GstSbcDec;
+typedef struct _GstSbcDecClass GstSbcDecClass;
+
+struct _GstSbcDec {
+       GstElement element;
+
+       GstPad *sinkpad;
+       GstPad *srcpad;
+
+       GstBuffer *buffer;
+
+       /* caps for outgoing buffers */
+       GstCaps *outcaps;
+
+       sbc_t sbc;
+};
+
+struct _GstSbcDecClass {
+       GstElementClass parent_class;
+};
+
+GType gst_sbc_dec_get_type(void);
+
+gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin);
+
+G_END_DECLS
diff --git a/audio/gstsbcenc.c b/audio/gstsbcenc.c
new file mode 100644 (file)
index 0000000..8948371
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "gstpragma.h"
+#include "gstsbcutil.h"
+#include "gstsbcenc.h"
+
+#define SBC_ENC_DEFAULT_MODE SBC_MODE_AUTO
+#define SBC_ENC_DEFAULT_BLOCKS 0
+#define SBC_ENC_DEFAULT_SUB_BANDS 0
+#define SBC_ENC_DEFAULT_ALLOCATION SBC_AM_AUTO
+#define SBC_ENC_DEFAULT_RATE 0
+#define SBC_ENC_DEFAULT_CHANNELS 0
+
+#define SBC_ENC_BITPOOL_AUTO 1
+#define SBC_ENC_BITPOOL_MIN 2
+#define SBC_ENC_BITPOOL_MIN_STR "2"
+#define SBC_ENC_BITPOOL_MAX 64
+#define SBC_ENC_BITPOOL_MAX_STR "64"
+
+GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug);
+#define GST_CAT_DEFAULT sbc_enc_debug
+
+#define GST_TYPE_SBC_MODE (gst_sbc_mode_get_type())
+
+static GType gst_sbc_mode_get_type(void)
+{
+       static GType sbc_mode_type = 0;
+       static GEnumValue sbc_modes[] = {
+               {  SBC_MODE_MONO,               "Mono",         "mono"  },
+               {  SBC_MODE_DUAL_CHANNEL,       "Dual Channel", "dual"  },
+               {  SBC_MODE_STEREO,             "Stereo",       "stereo"},
+               {  SBC_MODE_JOINT_STEREO,       "Joint Stereo", "joint" },
+               {  SBC_MODE_AUTO,               "Auto",         "auto"  },
+               { -1, NULL, NULL}
+       };
+
+       if (!sbc_mode_type)
+               sbc_mode_type = g_enum_register_static("GstSbcMode", sbc_modes);
+
+       return sbc_mode_type;
+}
+
+#define GST_TYPE_SBC_ALLOCATION (gst_sbc_allocation_get_type())
+
+static GType gst_sbc_allocation_get_type(void)
+{
+       static GType sbc_allocation_type = 0;
+       static GEnumValue sbc_allocations[] = {
+               { SBC_AM_LOUDNESS,      "Loudness",     "loudness" },
+               { SBC_AM_SNR,           "SNR",          "snr" },
+               { SBC_AM_AUTO,          "Auto",         "auto" },
+               { -1, NULL, NULL}
+       };
+
+       if (!sbc_allocation_type)
+               sbc_allocation_type = g_enum_register_static(
+                               "GstSbcAllocation", sbc_allocations);
+
+       return sbc_allocation_type;
+}
+
+#define GST_TYPE_SBC_BLOCKS (gst_sbc_blocks_get_type())
+
+static GType gst_sbc_blocks_get_type(void)
+{
+       static GType sbc_blocks_type = 0;
+       static GEnumValue sbc_blocks[] = {
+               { 0,    "Auto",         "auto" },
+               { 4,    "4",            "4" },
+               { 8,    "8",            "8" },
+               { 12,   "12",           "12" },
+               { 16,   "16",           "16" },
+               { -1, NULL, NULL}
+       };
+
+       if (!sbc_blocks_type)
+               sbc_blocks_type = g_enum_register_static(
+                               "GstSbcBlocks", sbc_blocks);
+
+       return sbc_blocks_type;
+}
+
+#define GST_TYPE_SBC_SUBBANDS (gst_sbc_subbands_get_type())
+
+static GType gst_sbc_subbands_get_type(void)
+{
+       static GType sbc_subbands_type = 0;
+       static GEnumValue sbc_subbands[] = {
+               { 0,    "Auto",         "auto" },
+               { 4,    "4 subbands",   "4" },
+               { 8,    "8 subbands",   "8" },
+               { -1, NULL, NULL}
+       };
+
+       if (!sbc_subbands_type)
+               sbc_subbands_type = g_enum_register_static(
+                               "GstSbcSubbands", sbc_subbands);
+
+       return sbc_subbands_type;
+}
+
+enum {
+       PROP_0,
+       PROP_MODE,
+       PROP_ALLOCATION,
+       PROP_BLOCKS,
+       PROP_SUBBANDS,
+       PROP_BITPOOL
+};
+
+GST_BOILERPLATE(GstSbcEnc, gst_sbc_enc, GstElement, GST_TYPE_ELEMENT);
+
+static const GstElementDetails sbc_enc_details =
+       GST_ELEMENT_DETAILS("Bluetooth SBC encoder",
+                               "Codec/Encoder/Audio",
+                               "Encode a SBC audio stream",
+                               "Marcel Holtmann <marcel@holtmann.org>");
+
+static GstStaticPadTemplate sbc_enc_sink_factory =
+       GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+               GST_STATIC_CAPS("audio/x-raw-int, "
+                               "rate = (int) { 16000, 32000, 44100, 48000 }, "
+                               "channels = (int) [ 1, 2 ], "
+                               "endianness = (int) BYTE_ORDER, "
+                               "signed = (boolean) true, "
+                               "width = (int) 16, "
+                               "depth = (int) 16"));
+
+static GstStaticPadTemplate sbc_enc_src_factory =
+       GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+               GST_STATIC_CAPS("audio/x-sbc, "
+                               "rate = (int) { 16000, 32000, 44100, 48000 }, "
+                               "channels = (int) [ 1, 2 ], "
+                               "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
+                               "blocks = (int) { 4, 8, 12, 16 }, "
+                               "subbands = (int) { 4, 8 }, "
+                               "allocation = (string) { \"snr\", \"loudness\" }, "
+                               "bitpool = (int) [ " SBC_ENC_BITPOOL_MIN_STR
+                               ", " SBC_ENC_BITPOOL_MAX_STR " ]"));
+
+gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps);
+
+static GstCaps *sbc_enc_generate_srcpad_caps(GstSbcEnc *enc)
+{
+       GstCaps *src_caps;
+       GstStructure *structure;
+       GEnumValue *enum_value;
+       GEnumClass *enum_class;
+       GValue *value;
+
+       src_caps = gst_caps_copy(gst_pad_get_pad_template_caps(enc->srcpad));
+       structure = gst_caps_get_structure(src_caps, 0);
+
+       value = g_new0(GValue, 1);
+
+       if (enc->rate != 0)
+               gst_sbc_util_set_structure_int_param(structure, "rate",
+                       enc->rate, value);
+
+       if (enc->channels != 0)
+               gst_sbc_util_set_structure_int_param(structure, "channels",
+                       enc->channels, value);
+
+       if (enc->subbands != 0)
+               gst_sbc_util_set_structure_int_param(structure, "subbands",
+                       enc->subbands, value);
+
+       if (enc->blocks != 0)
+               gst_sbc_util_set_structure_int_param(structure, "blocks",
+                       enc->blocks, value);
+
+       if (enc->bitpool != SBC_ENC_BITPOOL_AUTO)
+               gst_sbc_util_set_structure_int_param(structure, "bitpool",
+                       enc->bitpool, value);
+
+       if (enc->mode != SBC_ENC_DEFAULT_MODE) {
+               enum_class = g_type_class_ref(GST_TYPE_SBC_MODE);
+               enum_value = g_enum_get_value(enum_class, enc->mode);
+               gst_sbc_util_set_structure_string_param(structure, "mode",
+                       enum_value->value_nick, value);
+               g_type_class_unref(enum_class);
+       }
+
+       if (enc->allocation != SBC_AM_AUTO) {
+               enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION);
+               enum_value = g_enum_get_value(enum_class, enc->allocation);
+               gst_sbc_util_set_structure_string_param(structure, "allocation",
+                       enum_value->value_nick, value);
+               g_type_class_unref(enum_class);
+       }
+
+       g_free(value);
+
+       return src_caps;
+}
+
+static GstCaps *sbc_enc_src_getcaps(GstPad *pad)
+{
+       GstSbcEnc *enc;
+
+       enc = GST_SBC_ENC(GST_PAD_PARENT(pad));
+
+       return sbc_enc_generate_srcpad_caps(enc);
+}
+
+static gboolean sbc_enc_src_setcaps(GstPad *pad, GstCaps *caps)
+{
+       GstSbcEnc *enc = GST_SBC_ENC(GST_PAD_PARENT(pad));
+
+       GST_LOG_OBJECT(enc, "setting srcpad caps");
+
+       return gst_sbc_enc_fill_sbc_params(enc, caps);
+}
+
+static GstCaps *sbc_enc_src_caps_fixate(GstSbcEnc *enc, GstCaps *caps)
+{
+       gchar *error_message = NULL;
+       GstCaps *result;
+
+       result = gst_sbc_util_caps_fixate(caps, &error_message);
+
+       if (!result) {
+               GST_WARNING_OBJECT(enc, "Invalid input caps caused parsing "
+                               "error: %s", error_message);
+               g_free(error_message);
+               return NULL;
+       }
+
+       return result;
+}
+
+static GstCaps *sbc_enc_get_fixed_srcpad_caps(GstSbcEnc *enc)
+{
+       GstCaps *caps;
+       gboolean res = TRUE;
+       GstCaps *result_caps = NULL;
+
+       caps = gst_pad_get_allowed_caps(enc->srcpad);
+       if (caps == NULL)
+               caps = sbc_enc_src_getcaps(enc->srcpad);
+
+       if (caps == GST_CAPS_NONE || gst_caps_is_empty(caps)) {
+               res = FALSE;
+               goto done;
+       }
+
+       result_caps = sbc_enc_src_caps_fixate(enc, caps);
+
+done:
+       gst_caps_unref(caps);
+
+       if (!res)
+               return NULL;
+
+       return result_caps;
+}
+
+static gboolean sbc_enc_sink_setcaps(GstPad *pad, GstCaps *caps)
+{
+       GstSbcEnc *enc;
+       GstStructure *structure;
+       GstCaps *src_caps;
+       gint rate, channels;
+       gboolean res;
+
+       enc = GST_SBC_ENC(GST_PAD_PARENT(pad));
+       structure = gst_caps_get_structure(caps, 0);
+
+       if (!gst_structure_get_int(structure, "rate", &rate))
+               return FALSE;
+       if (!gst_structure_get_int(structure, "channels", &channels))
+               return FALSE;
+
+       enc->rate = rate;
+       enc->channels = channels;
+
+       src_caps = sbc_enc_get_fixed_srcpad_caps(enc);
+       if (!src_caps)
+               return FALSE;
+       res = gst_pad_set_caps(enc->srcpad, src_caps);
+       gst_caps_unref(src_caps);
+
+       return res;
+}
+
+gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps)
+{
+       if (!gst_caps_is_fixed(caps)) {
+               GST_DEBUG_OBJECT(enc, "didn't receive fixed caps, "
+                               "returning false");
+               return FALSE;
+       }
+
+       if (!gst_sbc_util_fill_sbc_params(&enc->sbc, caps))
+               return FALSE;
+
+       if (enc->rate != 0 && gst_sbc_parse_rate_from_sbc(enc->sbc.frequency)
+                                != enc->rate)
+               goto fail;
+
+       if (enc->channels != 0 && gst_sbc_get_channel_number(enc->sbc.mode)
+                               != enc->channels)
+               goto fail;
+
+       if (enc->blocks != 0 && gst_sbc_parse_blocks_from_sbc(enc->sbc.blocks)
+                                != enc->blocks)
+               goto fail;
+
+       if (enc->subbands != 0 && gst_sbc_parse_subbands_from_sbc(
+                               enc->sbc.subbands) != enc->subbands)
+               goto fail;
+
+       if (enc->mode != SBC_ENC_DEFAULT_MODE && enc->sbc.mode != enc->mode)
+               goto fail;
+
+       if (enc->allocation != SBC_AM_AUTO &&
+                               enc->sbc.allocation != enc->allocation)
+               goto fail;
+
+       if (enc->bitpool != SBC_ENC_BITPOOL_AUTO &&
+                               enc->sbc.bitpool != enc->bitpool)
+               goto fail;
+
+       enc->codesize = sbc_get_codesize(&enc->sbc);
+       enc->frame_length = sbc_get_frame_length(&enc->sbc);
+       enc->frame_duration = sbc_get_frame_duration(&enc->sbc);
+
+       GST_DEBUG_OBJECT(enc, "codesize: %d, frame_length: %d, frame_duration:"
+                       " %d", enc->codesize, enc->frame_length,
+                       enc->frame_duration);
+
+       return TRUE;
+
+fail:
+       memset(&enc->sbc, 0, sizeof(sbc_t));
+       return FALSE;
+}
+
+static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer)
+{
+       GstSbcEnc *enc = GST_SBC_ENC(gst_pad_get_parent(pad));
+       GstAdapter *adapter = enc->adapter;
+       GstFlowReturn res = GST_FLOW_OK;
+
+       gst_adapter_push(adapter, buffer);
+
+       while (gst_adapter_available(adapter) >= enc->codesize &&
+                                                       res == GST_FLOW_OK) {
+               GstBuffer *output;
+               GstCaps *caps;
+               const guint8 *data;
+               gint consumed;
+
+               caps = GST_PAD_CAPS(enc->srcpad);
+               res = gst_pad_alloc_buffer_and_set_caps(enc->srcpad,
+                                               GST_BUFFER_OFFSET_NONE,
+                                               enc->frame_length, caps,
+                                               &output);
+               if (res != GST_FLOW_OK)
+                       goto done;
+
+               data = gst_adapter_peek(adapter, enc->codesize);
+
+               consumed = sbc_encode(&enc->sbc, (gpointer) data,
+                                       enc->codesize,
+                                       GST_BUFFER_DATA(output),
+                                       GST_BUFFER_SIZE(output), NULL);
+               if (consumed <= 0) {
+                       GST_DEBUG_OBJECT(enc, "comsumed < 0, codesize: %d",
+                                       enc->codesize);
+                       break;
+               }
+               gst_adapter_flush(adapter, consumed);
+
+               GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer);
+               /* we have only 1 frame */
+               GST_BUFFER_DURATION(output) = enc->frame_duration;
+
+               res = gst_pad_push(enc->srcpad, output);
+
+               if (res != GST_FLOW_OK)
+                       goto done;
+       }
+
+done:
+       gst_object_unref(enc);
+
+       return res;
+}
+
+static GstStateChangeReturn sbc_enc_change_state(GstElement *element,
+                                               GstStateChange transition)
+{
+       GstSbcEnc *enc = GST_SBC_ENC(element);
+
+       switch (transition) {
+       case GST_STATE_CHANGE_READY_TO_PAUSED:
+               GST_DEBUG("Setup subband codec");
+               sbc_init(&enc->sbc, 0);
+               break;
+
+       case GST_STATE_CHANGE_PAUSED_TO_READY:
+               GST_DEBUG("Finish subband codec");
+               sbc_finish(&enc->sbc);
+               break;
+
+       default:
+               break;
+       }
+
+       return parent_class->change_state(element, transition);
+}
+
+static void gst_sbc_enc_dispose(GObject *object)
+{
+       GstSbcEnc *enc = GST_SBC_ENC(object);
+
+       if (enc->adapter != NULL)
+               g_object_unref(G_OBJECT(enc->adapter));
+
+       enc->adapter = NULL;
+}
+
+static void gst_sbc_enc_base_init(gpointer g_class)
+{
+       GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+       gst_element_class_add_pad_template(element_class,
+               gst_static_pad_template_get(&sbc_enc_sink_factory));
+
+       gst_element_class_add_pad_template(element_class,
+               gst_static_pad_template_get(&sbc_enc_src_factory));
+
+       gst_element_class_set_details(element_class, &sbc_enc_details);
+}
+
+static void gst_sbc_enc_set_property(GObject *object, guint prop_id,
+                                       const GValue *value, GParamSpec *pspec)
+{
+       GstSbcEnc *enc = GST_SBC_ENC(object);
+
+       /* changes to those properties will only happen on the next caps
+        * negotiation */
+
+       switch (prop_id) {
+       case PROP_MODE:
+               enc->mode = g_value_get_enum(value);
+               break;
+       case PROP_ALLOCATION:
+               enc->allocation = g_value_get_enum(value);
+               break;
+       case PROP_BLOCKS:
+               enc->blocks = g_value_get_enum(value);
+               break;
+       case PROP_SUBBANDS:
+               enc->subbands = g_value_get_enum(value);
+               break;
+       case PROP_BITPOOL:
+               enc->bitpool = g_value_get_int(value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+               break;
+       }
+}
+
+static void gst_sbc_enc_get_property(GObject *object, guint prop_id,
+                                       GValue *value, GParamSpec *pspec)
+{
+       GstSbcEnc *enc = GST_SBC_ENC(object);
+
+       switch (prop_id) {
+       case PROP_MODE:
+               g_value_set_enum(value, enc->mode);
+               break;
+       case PROP_ALLOCATION:
+               g_value_set_enum(value, enc->allocation);
+               break;
+       case PROP_BLOCKS:
+               g_value_set_enum(value, enc->blocks);
+               break;
+       case PROP_SUBBANDS:
+               g_value_set_enum(value, enc->subbands);
+               break;
+       case PROP_BITPOOL:
+               g_value_set_int(value, enc->bitpool);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+               break;
+       }
+}
+
+static void gst_sbc_enc_class_init(GstSbcEncClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS(klass);
+       GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+       parent_class = g_type_class_peek_parent(klass);
+
+       object_class->set_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_set_property);
+       object_class->get_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_get_property);
+       object_class->dispose = GST_DEBUG_FUNCPTR(gst_sbc_enc_dispose);
+
+       element_class->change_state = GST_DEBUG_FUNCPTR(sbc_enc_change_state);
+
+       g_object_class_install_property(object_class, PROP_MODE,
+                       g_param_spec_enum("mode", "Mode",
+                               "Encoding mode", GST_TYPE_SBC_MODE,
+                               SBC_ENC_DEFAULT_MODE, G_PARAM_READWRITE));
+
+       g_object_class_install_property(object_class, PROP_ALLOCATION,
+                       g_param_spec_enum("allocation", "Allocation",
+                               "Allocation method", GST_TYPE_SBC_ALLOCATION,
+                               SBC_ENC_DEFAULT_ALLOCATION, G_PARAM_READWRITE));
+
+       g_object_class_install_property(object_class, PROP_BLOCKS,
+                       g_param_spec_enum("blocks", "Blocks",
+                               "Blocks", GST_TYPE_SBC_BLOCKS,
+                               SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE));
+
+       g_object_class_install_property(object_class, PROP_SUBBANDS,
+                       g_param_spec_enum("subbands", "Sub bands",
+                               "Number of sub bands", GST_TYPE_SBC_SUBBANDS,
+                               SBC_ENC_DEFAULT_SUB_BANDS, G_PARAM_READWRITE));
+
+       g_object_class_install_property(object_class, PROP_BITPOOL,
+                       g_param_spec_int("bitpool", "Bitpool",
+                               "Bitpool (use 1 for automatic selection)",
+                               SBC_ENC_BITPOOL_AUTO, SBC_ENC_BITPOOL_MAX,
+                               SBC_ENC_BITPOOL_AUTO, G_PARAM_READWRITE));
+
+       GST_DEBUG_CATEGORY_INIT(sbc_enc_debug, "sbcenc", 0,
+                                               "SBC encoding element");
+}
+
+static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass)
+{
+       self->sinkpad = gst_pad_new_from_static_template(
+               &sbc_enc_sink_factory, "sink");
+       gst_pad_set_setcaps_function(self->sinkpad,
+                       GST_DEBUG_FUNCPTR(sbc_enc_sink_setcaps));
+       gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
+
+       self->srcpad = gst_pad_new_from_static_template(
+               &sbc_enc_src_factory, "src");
+       gst_pad_set_getcaps_function(self->srcpad,
+               GST_DEBUG_FUNCPTR(sbc_enc_src_getcaps));
+       gst_pad_set_setcaps_function(self->srcpad,
+               GST_DEBUG_FUNCPTR(sbc_enc_src_setcaps));
+       gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
+
+       gst_pad_set_chain_function(self->sinkpad,
+               GST_DEBUG_FUNCPTR(sbc_enc_chain));
+
+       self->subbands = SBC_ENC_DEFAULT_SUB_BANDS;
+       self->blocks = SBC_ENC_DEFAULT_BLOCKS;
+       self->mode = SBC_ENC_DEFAULT_MODE;
+       self->allocation = SBC_ENC_DEFAULT_ALLOCATION;
+       self->rate = SBC_ENC_DEFAULT_RATE;
+       self->channels = SBC_ENC_DEFAULT_CHANNELS;
+       self->bitpool = SBC_ENC_BITPOOL_AUTO;
+
+       self->frame_length = 0;
+       self->frame_duration = 0;
+
+       self->adapter = gst_adapter_new();
+}
+
+gboolean gst_sbc_enc_plugin_init(GstPlugin *plugin)
+{
+       return gst_element_register(plugin, "sbcenc",
+                       GST_RANK_NONE, GST_TYPE_SBC_ENC);
+}
diff --git a/audio/gstsbcenc.h b/audio/gstsbcenc.h
new file mode 100644 (file)
index 0000000..0329351
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <gst/gst.h>
+#include <gst/base/gstadapter.h>
+
+#include "sbc.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SBC_ENC \
+       (gst_sbc_enc_get_type())
+#define GST_SBC_ENC(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_ENC,GstSbcEnc))
+#define GST_SBC_ENC_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_ENC,GstSbcEncClass))
+#define GST_IS_SBC_ENC(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_ENC))
+#define GST_IS_SBC_ENC_CLASS(obj) \
+       (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_ENC))
+
+typedef struct _GstSbcEnc GstSbcEnc;
+typedef struct _GstSbcEncClass GstSbcEncClass;
+
+struct _GstSbcEnc {
+       GstElement element;
+
+       GstPad *sinkpad;
+       GstPad *srcpad;
+       GstAdapter *adapter;
+
+       gint rate;
+       gint channels;
+       gint mode;
+       gint blocks;
+       gint allocation;
+       gint subbands;
+       gint bitpool;
+
+       guint codesize;
+       gint frame_length;
+       gint frame_duration;
+
+       sbc_t sbc;
+};
+
+struct _GstSbcEncClass {
+       GstElementClass parent_class;
+};
+
+GType gst_sbc_enc_get_type(void);
+
+gboolean gst_sbc_enc_plugin_init(GstPlugin *plugin);
+
+G_END_DECLS
diff --git a/audio/gstsbcparse.c b/audio/gstsbcparse.c
new file mode 100644 (file)
index 0000000..bc4c0c7
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "gstpragma.h"
+#include "gstsbcutil.h"
+#include "gstsbcparse.h"
+
+GST_DEBUG_CATEGORY_STATIC(sbc_parse_debug);
+#define GST_CAT_DEFAULT sbc_parse_debug
+
+GST_BOILERPLATE(GstSbcParse, gst_sbc_parse, GstElement, GST_TYPE_ELEMENT);
+
+static const GstElementDetails sbc_parse_details =
+       GST_ELEMENT_DETAILS("Bluetooth SBC parser",
+                               "Codec/Parser/Audio",
+                               "Parse a SBC audio stream",
+                               "Marcel Holtmann <marcel@holtmann.org>");
+
+static GstStaticPadTemplate sbc_parse_sink_factory =
+       GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
+               GST_STATIC_CAPS("audio/x-sbc,"
+                               "parsed = (boolean) false"));
+
+static GstStaticPadTemplate sbc_parse_src_factory =
+       GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS,
+               GST_STATIC_CAPS("audio/x-sbc, "
+                               "rate = (int) { 16000, 32000, 44100, 48000 }, "
+                               "channels = (int) [ 1, 2 ], "
+                               "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
+                               "blocks = (int) { 4, 8, 12, 16 }, "
+                               "subbands = (int) { 4, 8 }, "
+                               "allocation = (string) { \"snr\", \"loudness\" },"
+                               "bitpool = (int) [ 2, 64 ],"
+                               "parsed = (boolean) true"));
+
+static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer)
+{
+       GstSbcParse *parse = GST_SBC_PARSE(gst_pad_get_parent(pad));
+       GstFlowReturn res = GST_FLOW_OK;
+       guint size, offset = 0;
+       guint8 *data;
+
+       /* FIXME use a gstadpter */
+       if (parse->buffer) {
+               GstBuffer *temp;
+               temp = buffer;
+               buffer = gst_buffer_span(parse->buffer, 0, buffer,
+                       GST_BUFFER_SIZE(parse->buffer)
+                       + GST_BUFFER_SIZE(buffer));
+               gst_buffer_unref(parse->buffer);
+               gst_buffer_unref(temp);
+               parse->buffer = NULL;
+       }
+
+       data = GST_BUFFER_DATA(buffer);
+       size = GST_BUFFER_SIZE(buffer);
+
+       while (offset < size) {
+               GstBuffer *output;
+               int consumed;
+
+               consumed = sbc_parse(&parse->new_sbc, data + offset,
+                                       size - offset);
+               if (consumed <= 0)
+                       break;
+
+               if (parse->first_parsing || (memcmp(&parse->sbc,
+                               &parse->new_sbc, sizeof(sbc_t)) != 0)) {
+
+                       memcpy(&parse->sbc, &parse->new_sbc, sizeof(sbc_t));
+                       if (parse->outcaps != NULL)
+                               gst_caps_unref(parse->outcaps);
+
+                       parse->outcaps = gst_sbc_parse_caps_from_sbc(
+                                               &parse->sbc);
+
+                       parse->first_parsing = FALSE;
+               }
+
+               res = gst_pad_alloc_buffer_and_set_caps(parse->srcpad,
+                                               GST_BUFFER_OFFSET_NONE,
+                                               consumed, parse->outcaps, &output);
+
+               if (res != GST_FLOW_OK)
+                       goto done;
+
+               memcpy(GST_BUFFER_DATA(output), data + offset, consumed);
+
+               res = gst_pad_push(parse->srcpad, output);
+               if (res != GST_FLOW_OK)
+                       goto done;
+
+               offset += consumed;
+       }
+
+       if (offset < size)
+               parse->buffer = gst_buffer_create_sub(buffer,
+                                                       offset, size - offset);
+
+done:
+       gst_buffer_unref(buffer);
+       gst_object_unref(parse);
+
+       return res;
+}
+
+static GstStateChangeReturn sbc_parse_change_state(GstElement *element,
+                                               GstStateChange transition)
+{
+       GstSbcParse *parse = GST_SBC_PARSE(element);
+
+       switch (transition) {
+       case GST_STATE_CHANGE_READY_TO_PAUSED:
+               GST_DEBUG("Setup subband codec");
+
+               parse->channels = -1;
+               parse->rate = -1;
+               parse->first_parsing = TRUE;
+
+               sbc_init(&parse->sbc, 0);
+               break;
+
+       case GST_STATE_CHANGE_PAUSED_TO_READY:
+               GST_DEBUG("Finish subband codec");
+
+               if (parse->buffer) {
+                       gst_buffer_unref(parse->buffer);
+                       parse->buffer = NULL;
+               }
+               if (parse->outcaps != NULL) {
+                       gst_caps_unref(parse->outcaps);
+                       parse->outcaps = NULL;
+               }
+
+               sbc_finish(&parse->sbc);
+               break;
+
+       default:
+               break;
+       }
+
+       return parent_class->change_state(element, transition);
+}
+
+static void gst_sbc_parse_base_init(gpointer g_class)
+{
+       GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
+
+       gst_element_class_add_pad_template(element_class,
+               gst_static_pad_template_get(&sbc_parse_sink_factory));
+
+       gst_element_class_add_pad_template(element_class,
+               gst_static_pad_template_get(&sbc_parse_src_factory));
+
+       gst_element_class_set_details(element_class, &sbc_parse_details);
+}
+
+static void gst_sbc_parse_class_init(GstSbcParseClass *klass)
+{
+       GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
+
+       parent_class = g_type_class_peek_parent(klass);
+
+       element_class->change_state = GST_DEBUG_FUNCPTR(sbc_parse_change_state);
+
+       GST_DEBUG_CATEGORY_INIT(sbc_parse_debug, "sbcparse", 0,
+                               "SBC parsing element");
+}
+
+static void gst_sbc_parse_init(GstSbcParse *self, GstSbcParseClass *klass)
+{
+       self->sinkpad = gst_pad_new_from_static_template(
+               &sbc_parse_sink_factory, "sink");
+       gst_pad_set_chain_function(self->sinkpad,
+               GST_DEBUG_FUNCPTR(sbc_parse_chain));
+       gst_element_add_pad(GST_ELEMENT(self), self->sinkpad);
+
+       self->srcpad = gst_pad_new_from_static_template(
+               &sbc_parse_src_factory, "src");
+       gst_element_add_pad(GST_ELEMENT(self), self->srcpad);
+
+       self->outcaps = NULL;
+       self->buffer = NULL;
+       self->channels = -1;
+       self->rate = -1;
+       self->first_parsing = TRUE;
+}
+
+gboolean gst_sbc_parse_plugin_init(GstPlugin *plugin)
+{
+       return gst_element_register(plugin, "sbcparse", GST_RANK_NONE,
+                                                       GST_TYPE_SBC_PARSE);
+}
diff --git a/audio/gstsbcparse.h b/audio/gstsbcparse.h
new file mode 100644 (file)
index 0000000..ecb8be4
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <gst/gst.h>
+
+#include "sbc.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_SBC_PARSE \
+       (gst_sbc_parse_get_type())
+#define GST_SBC_PARSE(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_PARSE,GstSbcParse))
+#define GST_SBC_PARSE_CLASS(klass) \
+       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_PARSE,GstSbcParseClass))
+#define GST_IS_SBC_PARSE(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_PARSE))
+#define GST_IS_SBC_PARSE_CLASS(obj) \
+       (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_PARSE))
+
+typedef struct _GstSbcParse GstSbcParse;
+typedef struct _GstSbcParseClass GstSbcParseClass;
+
+struct _GstSbcParse {
+       GstElement element;
+
+       GstPad *sinkpad;
+       GstPad *srcpad;
+
+       GstBuffer *buffer;
+
+       sbc_t sbc;
+       sbc_t new_sbc;
+       GstCaps *outcaps;
+       gboolean first_parsing;
+
+       gint channels;
+       gint rate;
+};
+
+struct _GstSbcParseClass {
+       GstElementClass parent_class;
+};
+
+GType gst_sbc_parse_get_type(void);
+
+gboolean gst_sbc_parse_plugin_init(GstPlugin *plugin);
+
+G_END_DECLS
diff --git a/audio/gstsbcutil.c b/audio/gstsbcutil.c
new file mode 100644 (file)
index 0000000..f596b41
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include "gstsbcutil.h"
+
+/*
+ * Selects one rate from a list of possible rates
+ * TODO - use a better approach to this (it is selecting the last element)
+ */
+gint gst_sbc_select_rate_from_list(const GValue *value)
+{
+       guint size = gst_value_list_get_size(value);
+       return g_value_get_int(gst_value_list_get_value(value, size-1));
+}
+
+/*
+ * Selects one number of channels option from a range of possible numbers
+ * TODO - use a better approach to this (it is selecting the maximum value)
+ */
+gint gst_sbc_select_channels_from_range(const GValue *value)
+{
+       return gst_value_get_int_range_max(value);
+}
+
+/*
+ * Selects one number of blocks from a list of possible blocks
+ * TODO - use a better approach to this (it is selecting the last element)
+ */
+gint gst_sbc_select_blocks_from_list(const GValue *value)
+{
+       guint size = gst_value_list_get_size(value);
+       return g_value_get_int(gst_value_list_get_value(value, size-1));
+}
+
+/*
+ * Selects one number of subbands from a list
+ * TODO - use a better approach to this (it is selecting the last element)
+ */
+gint gst_sbc_select_subbands_from_list(const GValue *value)
+{
+       guint size = gst_value_list_get_size(value);
+       return g_value_get_int(gst_value_list_get_value(value, size-1));
+}
+
+/*
+ * Selects one bitpool option from a range
+ * TODO - use a better approach to this (it is selecting the maximum value)
+ */
+gint gst_sbc_select_bitpool_from_range(const GValue *value)
+{
+       return gst_value_get_int_range_max(value);
+}
+
+/*
+ * Selects one allocation mode from the ones on the list
+ * TODO - use a better approach
+ */
+const gchar *gst_sbc_get_allocation_from_list(const GValue *value)
+{
+       guint size = gst_value_list_get_size(value);
+       return g_value_get_string(gst_value_list_get_value(value, size-1));
+}
+
+/*
+ * Selects one mode from the ones on the list
+ */
+const gchar *gst_sbc_get_mode_from_list(const GValue *list, gint channels)
+{
+       unsigned int i;
+       const GValue *value;
+       const gchar *aux;
+       gboolean joint, stereo, dual, mono;
+       guint size = gst_value_list_get_size(list);
+
+       joint = stereo = dual = mono = FALSE;
+
+       for (i = 0; i < size; i++) {
+               value = gst_value_list_get_value(list, i);
+               aux = g_value_get_string(value);
+               if (strcmp("joint", aux) == 0)
+                       joint = TRUE;
+               else if (strcmp("stereo", aux) == 0)
+                       stereo = TRUE;
+               else if (strcmp("dual", aux) == 0)
+                       dual = TRUE;
+               else if (strcmp("mono", aux) == 0)
+                       mono = TRUE;
+       }
+
+       if (channels == 1 && mono)
+               return "mono";
+       else if (channels == 2) {
+               if (joint)
+                       return "joint";
+               else if (stereo)
+                       return "stereo";
+               else if (dual)
+                       return "dual";
+       }
+
+       return NULL;
+}
+
+gint gst_sbc_parse_rate_from_sbc(gint frequency)
+{
+       switch (frequency) {
+       case SBC_FREQ_16000:
+               return 16000;
+       case SBC_FREQ_32000:
+               return 32000;
+       case SBC_FREQ_44100:
+               return 44100;
+       case SBC_FREQ_48000:
+               return 48000;
+       default:
+               return 0;
+       }
+}
+
+gint gst_sbc_parse_rate_to_sbc(gint rate)
+{
+       switch (rate) {
+       case 16000:
+               return SBC_FREQ_16000;
+       case 32000:
+               return SBC_FREQ_32000;
+       case 44100:
+               return SBC_FREQ_44100;
+       case 48000:
+               return SBC_FREQ_48000;
+       default:
+               return -1;
+       }
+}
+
+gint gst_sbc_get_channel_number(gint mode)
+{
+       switch (mode) {
+       case SBC_MODE_JOINT_STEREO:
+       case SBC_MODE_STEREO:
+       case SBC_MODE_DUAL_CHANNEL:
+               return 2;
+       case SBC_MODE_MONO:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+gint gst_sbc_parse_subbands_from_sbc(gint subbands)
+{
+       switch (subbands) {
+       case SBC_SB_4:
+               return 4;
+       case SBC_SB_8:
+               return 8;
+       default:
+               return 0;
+       }
+}
+
+gint gst_sbc_parse_subbands_to_sbc(gint subbands)
+{
+       switch (subbands) {
+       case 4:
+               return SBC_SB_4;
+       case 8:
+               return SBC_SB_8;
+       default:
+               return -1;
+       }
+}
+
+gint gst_sbc_parse_blocks_from_sbc(gint blocks)
+{
+       switch (blocks) {
+       case SBC_BLK_4:
+               return 4;
+       case SBC_BLK_8:
+               return 8;
+       case SBC_BLK_12:
+               return 12;
+       case SBC_BLK_16:
+               return 16;
+       default:
+               return 0;
+       }
+}
+
+gint gst_sbc_parse_blocks_to_sbc(gint blocks)
+{
+       switch (blocks) {
+       case 4:
+               return SBC_BLK_4;
+       case 8:
+               return SBC_BLK_8;
+       case 12:
+               return SBC_BLK_12;
+       case 16:
+               return SBC_BLK_16;
+       default:
+               return -1;
+       }
+}
+
+const gchar *gst_sbc_parse_mode_from_sbc(gint mode)
+{
+       switch (mode) {
+       case SBC_MODE_MONO:
+               return "mono";
+       case SBC_MODE_DUAL_CHANNEL:
+               return "dual";
+       case SBC_MODE_STEREO:
+               return "stereo";
+       case SBC_MODE_JOINT_STEREO:
+       case SBC_MODE_AUTO:
+               return "joint";
+       default:
+               return NULL;
+       }
+}
+
+gint gst_sbc_parse_mode_to_sbc(const gchar *mode)
+{
+       if (g_ascii_strcasecmp(mode, "joint") == 0)
+               return SBC_MODE_JOINT_STEREO;
+       else if (g_ascii_strcasecmp(mode, "stereo") == 0)
+               return SBC_MODE_STEREO;
+       else if (g_ascii_strcasecmp(mode, "dual") == 0)
+               return SBC_MODE_DUAL_CHANNEL;
+       else if (g_ascii_strcasecmp(mode, "mono") == 0)
+               return SBC_MODE_MONO;
+       else if (g_ascii_strcasecmp(mode, "auto") == 0)
+               return SBC_MODE_JOINT_STEREO;
+       else
+               return -1;
+}
+
+const gchar *gst_sbc_parse_allocation_from_sbc(gint alloc)
+{
+       switch (alloc) {
+       case SBC_AM_LOUDNESS:
+               return "loudness";
+       case SBC_AM_SNR:
+               return "snr";
+       case SBC_AM_AUTO:
+               return "loudness";
+       default:
+               return NULL;
+       }
+}
+
+gint gst_sbc_parse_allocation_to_sbc(const gchar *allocation)
+{
+       if (g_ascii_strcasecmp(allocation, "loudness") == 0)
+               return SBC_AM_LOUDNESS;
+       else if (g_ascii_strcasecmp(allocation, "snr") == 0)
+               return SBC_AM_SNR;
+       else
+               return SBC_AM_LOUDNESS;
+}
+
+GstCaps *gst_sbc_parse_caps_from_sbc(sbc_t *sbc)
+{
+       GstCaps *caps;
+       const gchar *mode_str;
+       const gchar *allocation_str;
+
+       mode_str = gst_sbc_parse_mode_from_sbc(sbc->mode);
+       allocation_str = gst_sbc_parse_allocation_from_sbc(sbc->allocation);
+       caps = gst_caps_new_simple("audio/x-sbc",
+                               "rate", G_TYPE_INT,
+                               gst_sbc_parse_rate_from_sbc(sbc->frequency),
+                               "channels", G_TYPE_INT,
+                               gst_sbc_get_channel_number(sbc->mode),
+                               "mode", G_TYPE_STRING, mode_str,
+                               "subbands", G_TYPE_INT,
+                               gst_sbc_parse_subbands_from_sbc(sbc->subbands),
+                               "blocks", G_TYPE_INT,
+                               gst_sbc_parse_blocks_from_sbc(sbc->blocks),
+                               "allocation", G_TYPE_STRING, allocation_str,
+                               "bitpool", G_TYPE_INT, sbc->bitpool,
+                               NULL);
+
+       return caps;
+}
+
+/*
+ * Given a GstCaps, this will return a fixed GstCaps on successful conversion.
+ * If an error occurs, it will return NULL and error_message will contain the
+ * error message.
+ *
+ * error_message must be passed NULL, if an error occurs, the caller has the
+ * ownership of the error_message, it must be freed after use.
+ */
+GstCaps *gst_sbc_util_caps_fixate(GstCaps *caps, gchar **error_message)
+{
+       GstCaps *result;
+       GstStructure *structure;
+       const GValue *value;
+       gboolean error = FALSE;
+       gint temp, rate, channels, blocks, subbands, bitpool;
+       const gchar *allocation = NULL;
+       const gchar *mode = NULL;
+
+       g_assert(*error_message == NULL);
+
+       structure = gst_caps_get_structure(caps, 0);
+
+       if (!gst_structure_has_field(structure, "rate")) {
+               error = TRUE;
+               *error_message = g_strdup("no rate");
+               goto error;
+       } else {
+               value = gst_structure_get_value(structure, "rate");
+               if (GST_VALUE_HOLDS_LIST(value))
+                       temp = gst_sbc_select_rate_from_list(value);
+               else
+                       temp = g_value_get_int(value);
+               rate = temp;
+       }
+
+       if (!gst_structure_has_field(structure, "channels")) {
+               error = TRUE;
+               *error_message = g_strdup("no channels");
+               goto error;
+       } else {
+               value = gst_structure_get_value(structure, "channels");
+               if (GST_VALUE_HOLDS_INT_RANGE(value))
+                       temp = gst_sbc_select_channels_from_range(value);
+               else
+                       temp = g_value_get_int(value);
+               channels = temp;
+       }
+
+       if (!gst_structure_has_field(structure, "blocks")) {
+               error = TRUE;
+               *error_message = g_strdup("no blocks.");
+               goto error;
+       } else {
+               value = gst_structure_get_value(structure, "blocks");
+               if (GST_VALUE_HOLDS_LIST(value))
+                       temp = gst_sbc_select_blocks_from_list(value);
+               else
+                       temp = g_value_get_int(value);
+               blocks = temp;
+       }
+
+       if (!gst_structure_has_field(structure, "subbands")) {
+               error = TRUE;
+               *error_message = g_strdup("no subbands");
+               goto error;
+       } else {
+               value = gst_structure_get_value(structure, "subbands");
+               if (GST_VALUE_HOLDS_LIST(value))
+                       temp = gst_sbc_select_subbands_from_list(value);
+               else
+                       temp = g_value_get_int(value);
+               subbands = temp;
+       }
+
+       if (!gst_structure_has_field(structure, "bitpool")) {
+               error = TRUE;
+               *error_message = g_strdup("no bitpool");
+               goto error;
+       } else {
+               value = gst_structure_get_value(structure, "bitpool");
+               if (GST_VALUE_HOLDS_INT_RANGE(value))
+                       temp = gst_sbc_select_bitpool_from_range(value);
+               else
+                       temp = g_value_get_int(value);
+               bitpool = temp;
+       }
+
+       if (!gst_structure_has_field(structure, "allocation")) {
+               error = TRUE;
+               *error_message = g_strdup("no allocation");
+               goto error;
+       } else {
+               value = gst_structure_get_value(structure, "allocation");
+               if (GST_VALUE_HOLDS_LIST(value))
+                       allocation = gst_sbc_get_allocation_from_list(value);
+               else
+                       allocation = g_value_get_string(value);
+       }
+
+       if (!gst_structure_has_field(structure, "mode")) {
+               error = TRUE;
+               *error_message = g_strdup("no mode");
+               goto error;
+       } else {
+               value = gst_structure_get_value(structure, "mode");
+               if (GST_VALUE_HOLDS_LIST(value)) {
+                       mode = gst_sbc_get_mode_from_list(value, channels);
+               } else
+                       mode = g_value_get_string(value);
+       }
+
+       /* perform validation
+        * if channels is 1, we must have channel mode = mono
+        * if channels is 2, we can't have channel mode = mono */
+       if ( (channels == 1 && (strcmp(mode, "mono") != 0) ) ||
+                       ( channels == 2 && ( strcmp(mode, "mono") == 0))) {
+               *error_message = g_strdup_printf("Invalid combination of "
+                                       "channels (%d) and channel mode (%s)",
+                                       channels, mode);
+               error = TRUE;
+       }
+
+error:
+       if (error)
+               return NULL;
+
+       result = gst_caps_new_simple("audio/x-sbc",
+                                       "rate", G_TYPE_INT, rate,
+                                       "channels", G_TYPE_INT, channels,
+                                       "mode", G_TYPE_STRING, mode,
+                                       "blocks", G_TYPE_INT, blocks,
+                                       "subbands", G_TYPE_INT, subbands,
+                                       "allocation", G_TYPE_STRING, allocation,
+                                       "bitpool", G_TYPE_INT, bitpool,
+                                       NULL);
+
+       return result;
+}
+
+/**
+ * Sets the int field_value to the  param "field" on the structure.
+ * value is used to do the operation, it must be a uninitialized (zero-filled)
+ * GValue, it will be left unitialized at the end of the function.
+ */
+void gst_sbc_util_set_structure_int_param(GstStructure *structure,
+                       const gchar *field, gint field_value,
+                       GValue *value)
+{
+       value = g_value_init(value, G_TYPE_INT);
+       g_value_set_int(value, field_value);
+       gst_structure_set_value(structure, field, value);
+       g_value_unset(value);
+}
+
+/**
+ * Sets the string field_value to the  param "field" on the structure.
+ * value is used to do the operation, it must be a uninitialized (zero-filled)
+ * GValue, it will be left unitialized at the end of the function.
+ */
+void gst_sbc_util_set_structure_string_param(GstStructure *structure,
+                       const gchar *field, const gchar *field_value,
+                       GValue *value)
+{
+       value = g_value_init(value, G_TYPE_STRING);
+       g_value_set_string(value, field_value);
+       gst_structure_set_value(structure, field, value);
+       g_value_unset(value);
+}
+
+gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps)
+{
+       GstStructure *structure;
+       gint rate, channels, subbands, blocks, bitpool;
+       const gchar *mode;
+       const gchar *allocation;
+
+       g_assert(gst_caps_is_fixed(caps));
+
+       structure = gst_caps_get_structure(caps, 0);
+
+       if (!gst_structure_get_int(structure, "rate", &rate))
+               return FALSE;
+       if (!gst_structure_get_int(structure, "channels", &channels))
+               return FALSE;
+       if (!gst_structure_get_int(structure, "subbands", &subbands))
+               return FALSE;
+       if (!gst_structure_get_int(structure, "blocks", &blocks))
+               return FALSE;
+       if (!gst_structure_get_int(structure, "bitpool", &bitpool))
+               return FALSE;
+
+       if (!(mode = gst_structure_get_string(structure, "mode")))
+               return FALSE;
+       if (!(allocation = gst_structure_get_string(structure, "allocation")))
+               return FALSE;
+
+       if (channels == 1 && strcmp(mode, "mono") != 0)
+               return FALSE;
+
+       sbc->frequency = gst_sbc_parse_rate_to_sbc(rate);
+       sbc->blocks = gst_sbc_parse_blocks_to_sbc(blocks);
+       sbc->subbands = gst_sbc_parse_subbands_to_sbc(subbands);
+       sbc->bitpool = bitpool;
+       sbc->mode = gst_sbc_parse_mode_to_sbc(mode);
+       sbc->allocation = gst_sbc_parse_allocation_to_sbc(allocation);
+
+       return TRUE;
+}
diff --git a/audio/gstsbcutil.h b/audio/gstsbcutil.h
new file mode 100644 (file)
index 0000000..5e47119
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <gst/gst.h>
+
+#include "sbc.h"
+#include <string.h>
+
+#define SBC_AM_AUTO 0x02
+#define SBC_MODE_AUTO 0x04
+
+gint gst_sbc_select_rate_from_list(const GValue *value);
+
+gint gst_sbc_select_channels_from_range(const GValue *value);
+
+gint gst_sbc_select_blocks_from_list(const GValue *value);
+
+gint gst_sbc_select_subbands_from_list(const GValue *value);
+
+gint gst_sbc_select_bitpool_from_range(const GValue *value);
+
+const gchar *gst_sbc_get_allocation_from_list(const GValue *value);
+
+const gchar *gst_sbc_get_mode_from_list(const GValue *value, gint channels);
+
+gint gst_sbc_get_channel_number(gint mode);
+gint gst_sbc_parse_rate_from_sbc(gint frequency);
+gint gst_sbc_parse_rate_to_sbc(gint rate);
+
+gint gst_sbc_parse_subbands_from_sbc(gint subbands);
+gint gst_sbc_parse_subbands_to_sbc(gint subbands);
+
+gint gst_sbc_parse_blocks_from_sbc(gint blocks);
+gint gst_sbc_parse_blocks_to_sbc(gint blocks);
+
+const gchar *gst_sbc_parse_mode_from_sbc(gint mode);
+gint gst_sbc_parse_mode_to_sbc(const gchar *mode);
+
+const gchar *gst_sbc_parse_allocation_from_sbc(gint alloc);
+gint gst_sbc_parse_allocation_to_sbc(const gchar *allocation);
+
+GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc);
+
+GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message);
+
+void gst_sbc_util_set_structure_int_param(GstStructure *structure,
+                       const gchar* field, gint field_value,
+                       GValue *value);
+
+void gst_sbc_util_set_structure_string_param(GstStructure *structure,
+                       const gchar* field, const gchar* field_value,
+                       GValue *value);
+
+gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps);
diff --git a/audio/headset.c b/audio/headset.c
new file mode 100644 (file)
index 0000000..729e4dc
--- /dev/null
@@ -0,0 +1,3007 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <assert.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "device.h"
+#include "manager.h"
+#include "error.h"
+#include "telephony.h"
+#include "headset.h"
+#include "sdp-client.h"
+#include "btio.h"
+#include "dbus-common.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#define DC_TIMEOUT 3
+
+#define RING_INTERVAL 3
+
+#define BUF_SIZE 1024
+
+#define HEADSET_GAIN_SPEAKER 'S'
+#define HEADSET_GAIN_MICROPHONE 'M'
+
+static struct {
+       gboolean telephony_ready;       /* Telephony plugin initialized */
+       uint32_t features;              /* HFP AG features */
+       const struct indicator *indicators;     /* Available HFP indicators */
+       int er_mode;                    /* Event reporting mode */
+       int er_ind;                     /* Event reporting for indicators */
+       int rh;                         /* Response and Hold state */
+       char *number;                   /* Incoming phone number */
+       int number_type;                /* Incoming number type */
+       guint ring_timer;               /* For incoming call indication */
+       const char *chld;               /* Response to AT+CHLD=? */
+} ag = {
+       .telephony_ready = FALSE,
+       .features = 0,
+       .er_mode = 3,
+       .er_ind = 0,
+       .rh = BTRH_NOT_SUPPORTED,
+       .number = NULL,
+       .number_type = 0,
+       .ring_timer = 0,
+};
+
+static gboolean sco_hci = TRUE;
+static gboolean fast_connectable = FALSE;
+
+static GSList *active_devices = NULL;
+
+static char *str_state[] = {
+       "HEADSET_STATE_DISCONNECTED",
+       "HEADSET_STATE_CONNECTING",
+       "HEADSET_STATE_CONNECTED",
+       "HEADSET_STATE_PLAY_IN_PROGRESS",
+       "HEADSET_STATE_PLAYING",
+};
+
+struct headset_state_callback {
+       headset_state_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+struct headset_nrec_callback {
+       unsigned int id;
+       headset_nrec_cb cb;
+       void *user_data;
+};
+
+struct connect_cb {
+       unsigned int id;
+       headset_stream_cb_t cb;
+       void *cb_data;
+};
+
+struct pending_connect {
+       DBusMessage *msg;
+       DBusPendingCall *call;
+       GIOChannel *io;
+       int err;
+       headset_state_t target_state;
+       GSList *callbacks;
+       uint16_t svclass;
+};
+
+struct headset_slc {
+       char buf[BUF_SIZE];
+       int data_start;
+       int data_length;
+
+       gboolean cli_active;
+       gboolean cme_enabled;
+       gboolean cwa_enabled;
+       gboolean pending_ring;
+       gboolean inband_ring;
+       gboolean nrec;
+       gboolean nrec_req;
+
+       int sp_gain;
+       int mic_gain;
+
+       unsigned int hf_features;
+};
+
+struct headset {
+       uint32_t hsp_handle;
+       uint32_t hfp_handle;
+
+       int rfcomm_ch;
+
+       GIOChannel *rfcomm;
+       GIOChannel *tmp_rfcomm;
+       GIOChannel *sco;
+       guint sco_id;
+
+       gboolean auto_dc;
+
+       guint dc_timer;
+
+       gboolean hfp_active;
+       gboolean search_hfp;
+       gboolean rfcomm_initiator;
+
+       headset_state_t state;
+       struct pending_connect *pending;
+
+       headset_lock_t lock;
+       struct headset_slc *slc;
+       GSList *nrec_cbs;
+};
+
+struct event {
+       const char *cmd;
+       int (*callback) (struct audio_device *device, const char *buf);
+};
+
+static GSList *headset_callbacks = NULL;
+
+static void error_connect_failed(DBusConnection *conn, DBusMessage *msg,
+                                                               int err)
+{
+       DBusMessage *reply = btd_error_failed(msg,
+                       err < 0 ? strerror(-err) : "Connect failed");
+       g_dbus_send_message(conn, reply);
+}
+
+static int rfcomm_connect(struct audio_device *device, headset_stream_cb_t cb,
+                               void *user_data, unsigned int *cb_id);
+static int get_records(struct audio_device *device, headset_stream_cb_t cb,
+                       void *user_data, unsigned int *cb_id);
+
+static void print_ag_features(uint32_t features)
+{
+       GString *gstr;
+       char *str;
+
+       if (features == 0) {
+               DBG("HFP AG features: (none)");
+               return;
+       }
+
+       gstr = g_string_new("HFP AG features: ");
+
+       if (features & AG_FEATURE_THREE_WAY_CALLING)
+               g_string_append(gstr, "\"Three-way calling\" ");
+       if (features & AG_FEATURE_EC_ANDOR_NR)
+               g_string_append(gstr, "\"EC and/or NR function\" ");
+       if (features & AG_FEATURE_VOICE_RECOGNITION)
+               g_string_append(gstr, "\"Voice recognition function\" ");
+       if (features & AG_FEATURE_INBAND_RINGTONE)
+               g_string_append(gstr, "\"In-band ring tone capability\" ");
+       if (features & AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG)
+               g_string_append(gstr, "\"Attach a number to a voice tag\" ");
+       if (features & AG_FEATURE_REJECT_A_CALL)
+               g_string_append(gstr, "\"Ability to reject a call\" ");
+       if (features & AG_FEATURE_ENHANCED_CALL_STATUS)
+               g_string_append(gstr, "\"Enhanced call status\" ");
+       if (features & AG_FEATURE_ENHANCED_CALL_CONTROL)
+               g_string_append(gstr, "\"Enhanced call control\" ");
+       if (features & AG_FEATURE_EXTENDED_ERROR_RESULT_CODES)
+               g_string_append(gstr, "\"Extended Error Result Codes\" ");
+
+       str = g_string_free(gstr, FALSE);
+
+       DBG("%s", str);
+
+       g_free(str);
+}
+
+static void print_hf_features(uint32_t features)
+{
+       GString *gstr;
+       char *str;
+
+       if (features == 0) {
+               DBG("HFP HF features: (none)");
+               return;
+       }
+
+       gstr = g_string_new("HFP HF features: ");
+
+       if (features & HF_FEATURE_EC_ANDOR_NR)
+               g_string_append(gstr, "\"EC and/or NR function\" ");
+       if (features & HF_FEATURE_CALL_WAITING_AND_3WAY)
+               g_string_append(gstr, "\"Call waiting and 3-way calling\" ");
+       if (features & HF_FEATURE_CLI_PRESENTATION)
+               g_string_append(gstr, "\"CLI presentation capability\" ");
+       if (features & HF_FEATURE_VOICE_RECOGNITION)
+               g_string_append(gstr, "\"Voice recognition activation\" ");
+       if (features & HF_FEATURE_REMOTE_VOLUME_CONTROL)
+               g_string_append(gstr, "\"Remote volume control\" ");
+       if (features & HF_FEATURE_ENHANCED_CALL_STATUS)
+               g_string_append(gstr, "\"Enhanced call status\" ");
+       if (features & HF_FEATURE_ENHANCED_CALL_CONTROL)
+               g_string_append(gstr, "\"Enhanced call control\" ");
+
+       str = g_string_free(gstr, FALSE);
+
+       DBG("%s", str);
+
+       g_free(str);
+}
+
+static const char *state2str(headset_state_t state)
+{
+       switch (state) {
+       case HEADSET_STATE_DISCONNECTED:
+               return "disconnected";
+       case HEADSET_STATE_CONNECTING:
+               return "connecting";
+       case HEADSET_STATE_CONNECTED:
+       case HEADSET_STATE_PLAY_IN_PROGRESS:
+               return "connected";
+       case HEADSET_STATE_PLAYING:
+               return "playing";
+       }
+
+       return NULL;
+}
+
+static int headset_send_valist(struct headset *hs, char *format, va_list ap)
+{
+       char rsp[BUF_SIZE];
+       ssize_t total_written, count;
+       int fd;
+
+       count = vsnprintf(rsp, sizeof(rsp), format, ap);
+
+       if (count < 0)
+               return -EINVAL;
+
+       if (!hs->rfcomm) {
+               error("headset_send: the headset is not connected");
+               return -EIO;
+       }
+
+       total_written = 0;
+       fd = g_io_channel_unix_get_fd(hs->rfcomm);
+
+       while (total_written < count) {
+               ssize_t written;
+
+               written = write(fd, rsp + total_written,
+                               count - total_written);
+               if (written < 0)
+                       return -errno;
+
+               total_written += written;
+       }
+
+       return 0;
+}
+
+static int __attribute__((format(printf, 2, 3)))
+                       headset_send(struct headset *hs, char *format, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, format);
+       ret = headset_send_valist(hs, format, ap);
+       va_end(ap);
+
+       return ret;
+}
+
+static int supported_features(struct audio_device *device, const char *buf)
+{
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+       int err;
+
+       if (strlen(buf) < 9)
+               return -EINVAL;
+
+       slc->hf_features = strtoul(&buf[8], NULL, 10);
+
+       print_hf_features(slc->hf_features);
+
+       err = headset_send(hs, "\r\n+BRSF: %u\r\n", ag.features);
+       if (err < 0)
+               return err;
+
+       return headset_send(hs, "\r\nOK\r\n");
+}
+
+static char *indicator_ranges(const struct indicator *indicators)
+{
+       int i;
+       GString *gstr;
+
+       gstr = g_string_new("\r\n+CIND: ");
+
+       for (i = 0; indicators[i].desc != NULL; i++) {
+               if (i == 0)
+                       g_string_append_printf(gstr, "(\"%s\",(%s))",
+                                               indicators[i].desc,
+                                               indicators[i].range);
+               else
+                       g_string_append_printf(gstr, ",(\"%s\",(%s))",
+                                               indicators[i].desc,
+                                               indicators[i].range);
+       }
+
+       g_string_append(gstr, "\r\n");
+
+       return g_string_free(gstr, FALSE);
+}
+
+static char *indicator_values(const struct indicator *indicators)
+{
+       int i;
+       GString *gstr;
+
+       gstr = g_string_new("\r\n+CIND: ");
+
+       for (i = 0; indicators[i].desc != NULL; i++) {
+               if (i == 0)
+                       g_string_append_printf(gstr, "%d", indicators[i].val);
+               else
+                       g_string_append_printf(gstr, ",%d", indicators[i].val);
+       }
+
+       g_string_append(gstr, "\r\n");
+
+       return g_string_free(gstr, FALSE);
+}
+
+static int report_indicators(struct audio_device *device, const char *buf)
+{
+       struct headset *hs = device->headset;
+       int err;
+       char *str;
+
+       if (strlen(buf) < 8)
+               return -EINVAL;
+
+       if (ag.indicators == NULL) {
+               error("HFP AG indicators not initialized");
+               return headset_send(hs, "\r\nERROR\r\n");
+       }
+
+       if (buf[7] == '=')
+               str = indicator_ranges(ag.indicators);
+       else
+               str = indicator_values(ag.indicators);
+
+       err = headset_send(hs, "%s", str);
+
+       g_free(str);
+
+       if (err < 0)
+               return err;
+
+       return headset_send(hs, "\r\nOK\r\n");
+}
+
+static void pending_connect_complete(struct connect_cb *cb, struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       if (hs->pending->err < 0)
+               cb->cb(NULL, cb->cb_data);
+       else
+               cb->cb(dev, cb->cb_data);
+}
+
+static void pending_connect_finalize(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+       struct pending_connect *p = hs->pending;
+
+       if (p == NULL)
+               return;
+
+       if (p->svclass)
+               bt_cancel_discovery(&dev->src, &dev->dst);
+
+       g_slist_foreach(p->callbacks, (GFunc) pending_connect_complete, dev);
+
+       g_slist_free_full(p->callbacks, g_free);
+
+       if (p->io) {
+               g_io_channel_shutdown(p->io, TRUE, NULL);
+               g_io_channel_unref(p->io);
+       }
+
+       if (p->msg)
+               dbus_message_unref(p->msg);
+
+       if (p->call) {
+               dbus_pending_call_cancel(p->call);
+               dbus_pending_call_unref(p->call);
+       }
+
+       g_free(p);
+
+       hs->pending = NULL;
+}
+
+static void pending_connect_init(struct headset *hs, headset_state_t target_state)
+{
+       if (hs->pending) {
+               if (hs->pending->target_state < target_state)
+                       hs->pending->target_state = target_state;
+               return;
+       }
+
+       hs->pending = g_new0(struct pending_connect, 1);
+       hs->pending->target_state = target_state;
+}
+
+static unsigned int connect_cb_new(struct headset *hs,
+                                       headset_state_t target_state,
+                                       headset_stream_cb_t func,
+                                       void *user_data)
+{
+       struct connect_cb *cb;
+       static unsigned int free_cb_id = 1;
+
+       pending_connect_init(hs, target_state);
+
+       if (!func)
+               return 0;
+
+       cb = g_new(struct connect_cb, 1);
+
+       cb->cb = func;
+       cb->cb_data = user_data;
+       cb->id = free_cb_id++;
+
+       hs->pending->callbacks = g_slist_append(hs->pending->callbacks,
+                                               cb);
+
+       return cb->id;
+}
+
+static void __attribute__((format(printf, 3, 4)))
+               send_foreach_headset(GSList *devices,
+                                       int (*cmp) (struct headset *hs),
+                                       char *format, ...)
+{
+       GSList *l;
+       va_list ap;
+
+       for (l = devices; l != NULL; l = l->next) {
+               struct audio_device *device = l->data;
+               struct headset *hs = device->headset;
+               int ret;
+
+               assert(hs != NULL);
+
+               if (cmp && cmp(hs) != 0)
+                       continue;
+
+               va_start(ap, format);
+               ret = headset_send_valist(hs, format, ap);
+               if (ret < 0)
+                       error("Failed to send to headset: %s (%d)",
+                                       strerror(-ret), -ret);
+               va_end(ap);
+       }
+}
+
+static int cli_cmp(struct headset *hs)
+{
+       struct headset_slc *slc = hs->slc;
+
+       if (!hs->hfp_active)
+               return -1;
+
+       if (slc->cli_active)
+               return 0;
+       else
+               return -1;
+}
+
+static gboolean ring_timer_cb(gpointer data)
+{
+       send_foreach_headset(active_devices, NULL, "\r\nRING\r\n");
+
+       if (ag.number)
+               send_foreach_headset(active_devices, cli_cmp,
+                                       "\r\n+CLIP: \"%s\",%d\r\n",
+                                       ag.number, ag.number_type);
+
+       return TRUE;
+}
+
+static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+       int sk;
+       struct audio_device *dev = user_data;
+       struct headset *hs = dev->headset;
+       struct headset_slc *slc = hs->slc;
+       struct pending_connect *p = hs->pending;
+
+       if (err) {
+               error("%s", err->message);
+
+               if (p != NULL) {
+                       p->err = -errno;
+                       if (p->msg)
+                               error_connect_failed(dev->conn, p->msg, p->err);
+                       pending_connect_finalize(dev);
+               }
+
+               if (hs->rfcomm)
+                       headset_set_state(dev, HEADSET_STATE_CONNECTED);
+               else
+                       headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+
+               return;
+       }
+
+       DBG("SCO socket opened for headset %s", dev->path);
+
+       sk = g_io_channel_unix_get_fd(chan);
+
+       DBG("SCO fd=%d", sk);
+
+       if (p) {
+               p->io = NULL;
+               if (p->msg) {
+                       DBusMessage *reply;
+                       reply = dbus_message_new_method_return(p->msg);
+                       g_dbus_send_message(dev->conn, reply);
+               }
+
+               pending_connect_finalize(dev);
+       }
+
+       fcntl(sk, F_SETFL, 0);
+
+       headset_set_state(dev, HEADSET_STATE_PLAYING);
+
+       if (slc->pending_ring) {
+               ring_timer_cb(NULL);
+               ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL,
+                                               ring_timer_cb,
+                                               NULL);
+               slc->pending_ring = FALSE;
+       }
+}
+
+static int sco_connect(struct audio_device *dev, headset_stream_cb_t cb,
+                       void *user_data, unsigned int *cb_id)
+{
+       struct headset *hs = dev->headset;
+       GError *err = NULL;
+       GIOChannel *io;
+
+       if (hs->state != HEADSET_STATE_CONNECTED)
+               return -EINVAL;
+
+       io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &dev->src,
+                               BT_IO_OPT_DEST_BDADDR, &dev->dst,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("%s", err->message);
+               g_error_free(err);
+               return -EIO;
+       }
+
+       hs->sco = io;
+
+       headset_set_state(dev, HEADSET_STATE_PLAY_IN_PROGRESS);
+
+       pending_connect_init(hs, HEADSET_STATE_PLAYING);
+
+       if (cb) {
+               unsigned int id = connect_cb_new(hs, HEADSET_STATE_PLAYING,
+                                                       cb, user_data);
+               if (cb_id)
+                       *cb_id = id;
+       }
+
+       return 0;
+}
+
+static int hfp_cmp(struct headset *hs)
+{
+       if (hs->hfp_active)
+               return 0;
+       else
+               return -1;
+}
+
+static void hfp_slc_complete(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+       struct pending_connect *p = hs->pending;
+
+       DBG("HFP Service Level Connection established");
+
+       headset_set_state(dev, HEADSET_STATE_CONNECTED);
+
+       if (p == NULL)
+               return;
+
+       if (p->target_state == HEADSET_STATE_CONNECTED) {
+               if (p->msg) {
+                       DBusMessage *reply = dbus_message_new_method_return(p->msg);
+                       g_dbus_send_message(dev->conn, reply);
+               }
+               pending_connect_finalize(dev);
+               return;
+       }
+
+       p->err = sco_connect(dev, NULL, NULL, NULL);
+       if (p->err < 0) {
+               if (p->msg)
+                       error_connect_failed(dev->conn, p->msg, p->err);
+               pending_connect_finalize(dev);
+       }
+}
+
+static int telephony_generic_rsp(struct audio_device *device, cme_error_t err)
+{
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+
+       if ((err != CME_ERROR_NONE) && slc->cme_enabled)
+               return headset_send(hs, "\r\n+CME ERROR: %d\r\n", err);
+
+       switch (err) {
+       case CME_ERROR_NONE:
+               return headset_send(hs, "\r\nOK\r\n");
+       case CME_ERROR_NO_NETWORK_SERVICE:
+               return headset_send(hs, "\r\nNO CARRIER\r\n");
+       default:
+               return headset_send(hs, "\r\nERROR\r\n");
+       }
+}
+
+int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err)
+{
+       struct audio_device *device = telephony_device;
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+       int ret;
+
+       if (err != CME_ERROR_NONE)
+               return telephony_generic_rsp(telephony_device, err);
+
+       ret = headset_send(hs, "\r\nOK\r\n");
+       if (ret < 0)
+               return ret;
+
+       if (hs->state != HEADSET_STATE_CONNECTING)
+               return 0;
+
+       if (slc->hf_features & HF_FEATURE_CALL_WAITING_AND_3WAY &&
+                       ag.features & AG_FEATURE_THREE_WAY_CALLING)
+               return 0;
+
+       hfp_slc_complete(device);
+
+       return 0;
+}
+
+static int event_reporting(struct audio_device *dev, const char *buf)
+{
+       char **tokens; /* <mode>, <keyp>, <disp>, <ind>, <bfr> */
+
+       if (strlen(buf) < 13)
+               return -EINVAL;
+
+       tokens = g_strsplit(&buf[8], ",", 5);
+       if (g_strv_length(tokens) < 4) {
+               g_strfreev(tokens);
+               return -EINVAL;
+       }
+
+       ag.er_mode = atoi(tokens[0]);
+       ag.er_ind = atoi(tokens[3]);
+
+       g_strfreev(tokens);
+       tokens = NULL;
+
+       DBG("Event reporting (CMER): mode=%d, ind=%d",
+                       ag.er_mode, ag.er_ind);
+
+       switch (ag.er_ind) {
+       case 0:
+       case 1:
+               telephony_event_reporting_req(dev, ag.er_ind);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int call_hold(struct audio_device *dev, const char *buf)
+{
+       struct headset *hs = dev->headset;
+       int err;
+
+       if (strlen(buf) < 9)
+               return -EINVAL;
+
+       if (buf[8] != '?') {
+               telephony_call_hold_req(dev, &buf[8]);
+               return 0;
+       }
+
+       err = headset_send(hs, "\r\n+CHLD: (%s)\r\n", ag.chld);
+       if (err < 0)
+               return err;
+
+       err = headset_send(hs, "\r\nOK\r\n");
+       if (err < 0)
+               return err;
+
+       if (hs->state != HEADSET_STATE_CONNECTING)
+               return 0;
+
+       hfp_slc_complete(dev);
+
+       return 0;
+}
+
+int telephony_key_press_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+static int key_press(struct audio_device *device, const char *buf)
+{
+       if (strlen(buf) < 9)
+               return -EINVAL;
+
+       g_dbus_emit_signal(device->conn, device->path,
+                       AUDIO_HEADSET_INTERFACE, "AnswerRequested",
+                       DBUS_TYPE_INVALID);
+
+       if (ag.ring_timer) {
+               g_source_remove(ag.ring_timer);
+               ag.ring_timer = 0;
+       }
+
+       telephony_key_press_req(device, &buf[8]);
+
+       return 0;
+}
+
+int telephony_answer_call_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+static int answer_call(struct audio_device *device, const char *buf)
+{
+       if (ag.ring_timer) {
+               g_source_remove(ag.ring_timer);
+               ag.ring_timer = 0;
+       }
+
+       if (ag.number) {
+               g_free(ag.number);
+               ag.number = NULL;
+       }
+
+       telephony_answer_call_req(device);
+
+       return 0;
+}
+
+int telephony_terminate_call_rsp(void *telephony_device,
+                                       cme_error_t err)
+{
+       struct audio_device *device = telephony_device;
+       struct headset *hs = device->headset;
+
+       if (err != CME_ERROR_NONE)
+               return telephony_generic_rsp(telephony_device, err);
+
+       g_dbus_emit_signal(device->conn, device->path,
+                       AUDIO_HEADSET_INTERFACE, "CallTerminated",
+                       DBUS_TYPE_INVALID);
+
+       return headset_send(hs, "\r\nOK\r\n");
+}
+
+static int terminate_call(struct audio_device *device, const char *buf)
+{
+       if (ag.number) {
+               g_free(ag.number);
+               ag.number = NULL;
+       }
+
+       if (ag.ring_timer) {
+               g_source_remove(ag.ring_timer);
+               ag.ring_timer = 0;
+       }
+
+       telephony_terminate_call_req(device);
+
+       return 0;
+}
+
+static int cli_notification(struct audio_device *device, const char *buf)
+{
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+
+       if (strlen(buf) < 9)
+               return -EINVAL;
+
+       slc->cli_active = buf[8] == '1' ? TRUE : FALSE;
+
+       return headset_send(hs, "\r\nOK\r\n");
+}
+
+int telephony_response_and_hold_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+static int response_and_hold(struct audio_device *device, const char *buf)
+{
+       struct headset *hs = device->headset;
+
+       if (strlen(buf) < 8)
+               return -EINVAL;
+
+       if (ag.rh == BTRH_NOT_SUPPORTED)
+               return telephony_generic_rsp(device, CME_ERROR_NOT_SUPPORTED);
+
+       if (buf[7] == '=') {
+               telephony_response_and_hold_req(device, atoi(&buf[8]) < 0);
+               return 0;
+       }
+
+       if (ag.rh >= 0)
+               headset_send(hs, "\r\n+BTRH: %d\r\n", ag.rh);
+
+       return headset_send(hs, "\r\nOK\r\n");
+}
+
+int telephony_last_dialed_number_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+static int last_dialed_number(struct audio_device *device, const char *buf)
+{
+       telephony_last_dialed_number_req(device);
+
+       return 0;
+}
+
+int telephony_dial_number_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+static int dial_number(struct audio_device *device, const char *buf)
+{
+       char number[BUF_SIZE];
+       size_t buf_len;
+
+       buf_len = strlen(buf);
+
+       if (buf[buf_len - 1] != ';') {
+               DBG("Rejecting non-voice call dial request");
+               return -EINVAL;
+       }
+
+       memset(number, 0, sizeof(number));
+       strncpy(number, &buf[3], buf_len - 4);
+
+       telephony_dial_number_req(device, number);
+
+       return 0;
+}
+
+static int headset_set_gain(struct audio_device *device, uint16_t gain, char type)
+{
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+       const char *name, *property;
+
+       if (gain > 15) {
+               error("Invalid gain value: %u", gain);
+               return -EINVAL;
+       }
+
+       switch (type) {
+       case HEADSET_GAIN_SPEAKER:
+               if (slc->sp_gain == gain) {
+                       DBG("Ignoring no-change in speaker gain");
+                       return -EALREADY;
+               }
+               name = "SpeakerGainChanged";
+               property = "SpeakerGain";
+               slc->sp_gain = gain;
+               break;
+       case HEADSET_GAIN_MICROPHONE:
+               if (slc->mic_gain == gain) {
+                       DBG("Ignoring no-change in microphone gain");
+                       return -EALREADY;
+               }
+               name = "MicrophoneGainChanged";
+               property = "MicrophoneGain";
+               slc->mic_gain = gain;
+               break;
+       default:
+               error("Unknown gain setting");
+               return -EINVAL;
+       }
+
+       g_dbus_emit_signal(device->conn, device->path,
+                               AUDIO_HEADSET_INTERFACE, name,
+                               DBUS_TYPE_UINT16, &gain,
+                               DBUS_TYPE_INVALID);
+
+       emit_property_changed(device->conn, device->path,
+                               AUDIO_HEADSET_INTERFACE, property,
+                               DBUS_TYPE_UINT16, &gain);
+
+       return 0;
+}
+
+static int signal_gain_setting(struct audio_device *device, const char *buf)
+{
+       struct headset *hs = device->headset;
+       dbus_uint16_t gain;
+       int err;
+
+       if (strlen(buf) < 8) {
+               error("Too short string for Gain setting");
+               return -EINVAL;
+       }
+
+       gain = (dbus_uint16_t) strtol(&buf[7], NULL, 10);
+
+       err = headset_set_gain(device, gain, buf[5]);
+       if (err < 0 && err != -EALREADY)
+               return err;
+
+       return headset_send(hs, "\r\nOK\r\n");
+}
+
+int telephony_transmit_dtmf_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+static int dtmf_tone(struct audio_device *device, const char *buf)
+{
+       char tone;
+
+       if (strlen(buf) < 8) {
+               error("Too short string for DTMF tone");
+               return -EINVAL;
+       }
+
+       tone = buf[7];
+       if (tone >= '#' && tone <= 'D')
+               telephony_transmit_dtmf_req(device, tone);
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+int telephony_subscriber_number_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+static int subscriber_number(struct audio_device *device, const char *buf)
+{
+       telephony_subscriber_number_req(device);
+
+       return 0;
+}
+
+int telephony_list_current_calls_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+static int list_current_calls(struct audio_device *device, const char *buf)
+{
+       telephony_list_current_calls_req(device);
+
+       return 0;
+}
+
+static int extended_errors(struct audio_device *device, const char *buf)
+{
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+
+       if (strlen(buf) < 9)
+               return -EINVAL;
+
+       if (buf[8] == '1') {
+               slc->cme_enabled = TRUE;
+               DBG("CME errors enabled for headset %p", hs);
+       } else {
+               slc->cme_enabled = FALSE;
+               DBG("CME errors disabled for headset %p", hs);
+       }
+
+       return headset_send(hs, "\r\nOK\r\n");
+}
+
+static int call_waiting_notify(struct audio_device *device, const char *buf)
+{
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+
+       if (strlen(buf) < 9)
+               return -EINVAL;
+
+       if (buf[8] == '1') {
+               slc->cwa_enabled = TRUE;
+               DBG("Call waiting notification enabled for headset %p", hs);
+       } else {
+               slc->cwa_enabled = FALSE;
+               DBG("Call waiting notification disabled for headset %p", hs);
+       }
+
+       return headset_send(hs, "\r\nOK\r\n");
+}
+
+int telephony_operator_selection_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+int telephony_call_hold_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+int telephony_nr_and_ec_rsp(void *telephony_device, cme_error_t err)
+{
+       struct audio_device *device = telephony_device;
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+
+       if (err == CME_ERROR_NONE) {
+               GSList *l;
+
+               for (l = hs->nrec_cbs; l; l = l->next) {
+                       struct headset_nrec_callback *nrec_cb = l->data;
+
+                       nrec_cb->cb(device, slc->nrec_req, nrec_cb->user_data);
+               }
+
+               slc->nrec = hs->slc->nrec_req;
+       }
+
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+int telephony_voice_dial_rsp(void *telephony_device, cme_error_t err)
+{
+       return telephony_generic_rsp(telephony_device, err);
+}
+
+int telephony_operator_selection_ind(int mode, const char *oper)
+{
+       if (!active_devices)
+               return -ENODEV;
+
+       send_foreach_headset(active_devices, hfp_cmp,
+                               "\r\n+COPS: %d,0,\"%s\"\r\n",
+                               mode, oper);
+       return 0;
+}
+
+static int operator_selection(struct audio_device *device, const char *buf)
+{
+       struct headset *hs = device->headset;
+
+       if (strlen(buf) < 8)
+               return -EINVAL;
+
+       switch (buf[7]) {
+       case '?':
+               telephony_operator_selection_req(device);
+               break;
+       case '=':
+               return headset_send(hs, "\r\nOK\r\n");
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nr_and_ec(struct audio_device *device, const char *buf)
+{
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+
+       if (strlen(buf) < 9)
+               return -EINVAL;
+
+       if (buf[8] == '0')
+               slc->nrec_req = FALSE;
+       else
+               slc->nrec_req = TRUE;
+
+       telephony_nr_and_ec_req(device, slc->nrec_req);
+
+       return 0;
+}
+
+static int voice_dial(struct audio_device *device, const char *buf)
+{
+       gboolean enable;
+
+       if (strlen(buf) < 9)
+               return -EINVAL;
+
+       if (buf[8] == '0')
+               enable = FALSE;
+       else
+               enable = TRUE;
+
+       telephony_voice_dial_req(device, enable);
+
+       return 0;
+}
+
+static int apple_command(struct audio_device *device, const char *buf)
+{
+       DBG("Got Apple command: %s", buf);
+
+       return telephony_generic_rsp(device, CME_ERROR_NONE);
+}
+
+static struct event event_callbacks[] = {
+       { "ATA", answer_call },
+       { "ATD", dial_number },
+       { "AT+VG", signal_gain_setting },
+       { "AT+BRSF", supported_features },
+       { "AT+CIND", report_indicators },
+       { "AT+CMER", event_reporting },
+       { "AT+CHLD", call_hold },
+       { "AT+CHUP", terminate_call },
+       { "AT+CKPD", key_press },
+       { "AT+CLIP", cli_notification },
+       { "AT+BTRH", response_and_hold },
+       { "AT+BLDN", last_dialed_number },
+       { "AT+VTS", dtmf_tone },
+       { "AT+CNUM", subscriber_number },
+       { "AT+CLCC", list_current_calls },
+       { "AT+CMEE", extended_errors },
+       { "AT+CCWA", call_waiting_notify },
+       { "AT+COPS", operator_selection },
+       { "AT+NREC", nr_and_ec },
+       { "AT+BVRA", voice_dial },
+       { "AT+XAPL", apple_command },
+       { "AT+IPHONEACCEV", apple_command },
+       { 0 }
+};
+
+static int handle_event(struct audio_device *device, const char *buf)
+{
+       struct event *ev;
+
+       DBG("Received %s", buf);
+
+       for (ev = event_callbacks; ev->cmd; ev++) {
+               if (!strncmp(buf, ev->cmd, strlen(ev->cmd)))
+                       return ev->callback(device, buf);
+       }
+
+       return -EINVAL;
+}
+
+static void close_sco(struct audio_device *device)
+{
+       struct headset *hs = device->headset;
+
+       if (hs->sco) {
+               int sock = g_io_channel_unix_get_fd(hs->sco);
+               shutdown(sock, SHUT_RDWR);
+               g_io_channel_shutdown(hs->sco, TRUE, NULL);
+               g_io_channel_unref(hs->sco);
+               hs->sco = NULL;
+       }
+
+       if (hs->sco_id) {
+               g_source_remove(hs->sco_id);
+               hs->sco_id = 0;
+       }
+}
+
+static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond,
+                               struct audio_device *device)
+{
+       struct headset *hs;
+       struct headset_slc *slc;
+       unsigned char buf[BUF_SIZE];
+       ssize_t bytes_read;
+       size_t free_space;
+       int fd;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       hs = device->headset;
+       slc = hs->slc;
+
+       if (cond & (G_IO_ERR | G_IO_HUP)) {
+               DBG("ERR or HUP on RFCOMM socket");
+               goto failed;
+       }
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       bytes_read = read(fd, buf, sizeof(buf) - 1);
+       if (bytes_read < 0)
+               return TRUE;
+
+       free_space = sizeof(slc->buf) - slc->data_start -
+                       slc->data_length - 1;
+
+       if (free_space < (size_t) bytes_read) {
+               /* Very likely that the HS is sending us garbage so
+                * just ignore the data and disconnect */
+               error("Too much data to fit incoming buffer");
+               goto failed;
+       }
+
+       memcpy(&slc->buf[slc->data_start], buf, bytes_read);
+       slc->data_length += bytes_read;
+
+       /* Make sure the data is null terminated so we can use string
+        * functions */
+       slc->buf[slc->data_start + slc->data_length] = '\0';
+
+       while (slc->data_length > 0) {
+               char *cr;
+               int err;
+               off_t cmd_len;
+
+               cr = strchr(&slc->buf[slc->data_start], '\r');
+               if (!cr)
+                       break;
+
+               cmd_len = 1 + (off_t) cr - (off_t) &slc->buf[slc->data_start];
+               *cr = '\0';
+
+               if (cmd_len > 1)
+                       err = handle_event(device, &slc->buf[slc->data_start]);
+               else
+                       /* Silently skip empty commands */
+                       err = 0;
+
+               if (err == -EINVAL) {
+                       error("Badly formated or unrecognized command: %s",
+                                       &slc->buf[slc->data_start]);
+                       err = telephony_generic_rsp(device,
+                                               CME_ERROR_NOT_SUPPORTED);
+                       if (err < 0)
+                               goto failed;
+               } else if (err < 0)
+                       error("Error handling command %s: %s (%d)",
+                                               &slc->buf[slc->data_start],
+                                               strerror(-err), -err);
+
+               slc->data_start += cmd_len;
+               slc->data_length -= cmd_len;
+
+               if (!slc->data_length)
+                       slc->data_start = 0;
+       }
+
+       return TRUE;
+
+failed:
+       headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+
+       return FALSE;
+}
+
+static gboolean sco_cb(GIOChannel *chan, GIOCondition cond,
+                       struct audio_device *device)
+{
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       error("Audio connection got disconnected");
+
+       pending_connect_finalize(device);
+       headset_set_state(device, HEADSET_STATE_CONNECTED);
+
+       return FALSE;
+}
+
+void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+       struct headset *hs = dev->headset;
+       struct pending_connect *p = hs->pending;
+       char hs_address[18];
+
+       if (err) {
+               error("%s", err->message);
+               goto failed;
+       }
+
+       /* For HFP telephony isn't ready just disconnect */
+       if (hs->hfp_active && !ag.telephony_ready) {
+               error("Unable to accept HFP connection since the telephony "
+                               "subsystem isn't initialized");
+               goto failed;
+       }
+
+       hs->rfcomm = hs->tmp_rfcomm;
+       hs->tmp_rfcomm = NULL;
+
+       ba2str(&dev->dst, hs_address);
+
+       if (p)
+               p->io = NULL;
+       else
+               hs->auto_dc = FALSE;
+
+       g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,
+                       (GIOFunc) rfcomm_io_cb, dev);
+
+       DBG("%s: Connected to %s", dev->path, hs_address);
+
+       hs->slc = g_new0(struct headset_slc, 1);
+       hs->slc->sp_gain = 15;
+       hs->slc->mic_gain = 15;
+       hs->slc->nrec = TRUE;
+
+       /* In HFP mode wait for Service Level Connection */
+       if (hs->hfp_active)
+               return;
+
+       headset_set_state(dev, HEADSET_STATE_CONNECTED);
+
+       if (p && p->target_state == HEADSET_STATE_PLAYING) {
+               p->err = sco_connect(dev, NULL, NULL, NULL);
+               if (p->err < 0)
+                       goto failed;
+               return;
+       }
+
+       if (p && p->msg) {
+               DBusMessage *reply = dbus_message_new_method_return(p->msg);
+               g_dbus_send_message(dev->conn, reply);
+       }
+
+       pending_connect_finalize(dev);
+
+       return;
+
+failed:
+       if (p && p->msg)
+               error_connect_failed(dev->conn, p->msg, p->err);
+       pending_connect_finalize(dev);
+       if (hs->rfcomm)
+               headset_set_state(dev, HEADSET_STATE_CONNECTED);
+       else
+               headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+}
+
+static int headset_set_channel(struct headset *headset,
+                               const sdp_record_t *record, uint16_t svc)
+{
+       int ch;
+       sdp_list_t *protos;
+
+       if (sdp_get_access_protos(record, &protos) < 0) {
+               error("Unable to get access protos from headset record");
+               return -1;
+       }
+
+       ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+       sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+       sdp_list_free(protos, NULL);
+
+       if (ch <= 0) {
+               error("Unable to get RFCOMM channel from Headset record");
+               return -1;
+       }
+
+       headset->rfcomm_ch = ch;
+
+       if (svc == HANDSFREE_SVCLASS_ID) {
+               headset->hfp_handle = record->handle;
+               headset->hsp_handle = 0;
+               DBG("Discovered Handsfree service on channel %d", ch);
+       } else {
+               headset->hsp_handle = record->handle;
+               headset->hfp_handle = 0;
+               DBG("Discovered Headset service on channel %d", ch);
+       }
+
+       return 0;
+}
+
+static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+       struct audio_device *dev = user_data;
+       struct headset *hs = dev->headset;
+       struct pending_connect *p = hs->pending;
+       sdp_record_t *record = NULL;
+       sdp_list_t *r;
+       uuid_t uuid;
+
+       assert(hs->pending != NULL);
+
+       if (err < 0) {
+               error("Unable to get service record: %s (%d)",
+                                                       strerror(-err), -err);
+               p->err = -err;
+               if (p->msg)
+                       error_connect_failed(dev->conn, p->msg, p->err);
+               goto failed;
+       }
+
+       if (!recs || !recs->data) {
+               error("No records found");
+               goto failed_not_supported;
+       }
+
+       sdp_uuid16_create(&uuid, p->svclass);
+
+       for (r = recs; r != NULL; r = r->next) {
+               sdp_list_t *classes;
+               uuid_t class;
+
+               record = r->data;
+
+               if (sdp_get_service_classes(record, &classes) < 0) {
+                       error("Unable to get service classes from record");
+                       continue;
+               }
+
+               memcpy(&class, classes->data, sizeof(uuid));
+
+               sdp_list_free(classes, free);
+
+               if (sdp_uuid_cmp(&class, &uuid) == 0)
+                       break;
+       }
+
+       if (r == NULL) {
+               error("No record found with UUID 0x%04x", p->svclass);
+               goto failed_not_supported;
+       }
+
+       if (headset_set_channel(hs, record, p->svclass) < 0) {
+               error("Unable to extract RFCOMM channel from service record");
+               goto failed_not_supported;
+       }
+
+       /* Set svclass to 0 so we can easily check that SDP is no-longer
+        * going on (to know if bt_cancel_discovery needs to be called) */
+       p->svclass = 0;
+
+       err = rfcomm_connect(dev, NULL, NULL, NULL);
+       if (err < 0) {
+               error("Unable to connect: %s (%d)", strerror(-err), -err);
+               p->err = -err;
+               if (p->msg != NULL)
+                       error_connect_failed(dev->conn, p->msg, p->err);
+               goto failed;
+       }
+
+       return;
+
+failed_not_supported:
+       if (p->svclass == HANDSFREE_SVCLASS_ID &&
+                       get_records(dev, NULL, NULL, NULL) == 0)
+               return;
+       if (p->msg) {
+               DBusMessage *reply = btd_error_not_supported(p->msg);
+               g_dbus_send_message(dev->conn, reply);
+       }
+failed:
+       p->svclass = 0;
+       pending_connect_finalize(dev);
+       headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+}
+
+static int get_records(struct audio_device *device, headset_stream_cb_t cb,
+                       void *user_data, unsigned int *cb_id)
+{
+       struct headset *hs = device->headset;
+       uint16_t svclass;
+       uuid_t uuid;
+       int err;
+
+       if (hs->pending && hs->pending->svclass == HANDSFREE_SVCLASS_ID)
+               svclass = HEADSET_SVCLASS_ID;
+       else
+               svclass = hs->search_hfp ? HANDSFREE_SVCLASS_ID :
+                                                       HEADSET_SVCLASS_ID;
+
+       sdp_uuid16_create(&uuid, svclass);
+
+       err = bt_search_service(&device->src, &device->dst, &uuid,
+                                               get_record_cb, device, NULL);
+       if (err < 0)
+               return err;
+
+       if (hs->pending) {
+               hs->pending->svclass = svclass;
+               return 0;
+       }
+
+       headset_set_state(device, HEADSET_STATE_CONNECTING);
+
+       pending_connect_init(hs, HEADSET_STATE_CONNECTED);
+
+       hs->pending->svclass = svclass;
+
+       if (cb) {
+               unsigned int id;
+               id = connect_cb_new(hs, HEADSET_STATE_CONNECTED,
+                                       cb, user_data);
+               if (cb_id)
+                       *cb_id = id;
+       }
+
+       return 0;
+}
+
+static int rfcomm_connect(struct audio_device *dev, headset_stream_cb_t cb,
+                               void *user_data, unsigned int *cb_id)
+{
+       struct headset *hs = dev->headset;
+       char address[18];
+       GError *err = NULL;
+
+       if (!manager_allow_headset_connection(dev))
+               return -ECONNREFUSED;
+
+       if (hs->rfcomm_ch < 0)
+               return get_records(dev, cb, user_data, cb_id);
+
+       ba2str(&dev->dst, address);
+
+       DBG("%s: Connecting to %s channel %d", dev->path, address,
+               hs->rfcomm_ch);
+
+       hs->tmp_rfcomm = bt_io_connect(BT_IO_RFCOMM, headset_connect_cb, dev,
+                                       NULL, &err,
+                                       BT_IO_OPT_SOURCE_BDADDR, &dev->src,
+                                       BT_IO_OPT_DEST_BDADDR, &dev->dst,
+                                       BT_IO_OPT_CHANNEL, hs->rfcomm_ch,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                                       BT_IO_OPT_INVALID);
+
+       hs->rfcomm_ch = -1;
+
+       if (!hs->tmp_rfcomm) {
+               error("%s", err->message);
+               g_error_free(err);
+               return -EIO;
+       }
+
+       hs->hfp_active = hs->hfp_handle != 0 ? TRUE : FALSE;
+       hs->rfcomm_initiator = FALSE;
+
+       headset_set_state(dev, HEADSET_STATE_CONNECTING);
+
+       pending_connect_init(hs, HEADSET_STATE_CONNECTED);
+
+       if (cb) {
+               unsigned int id = connect_cb_new(hs, HEADSET_STATE_CONNECTED,
+                                                       cb, user_data);
+               if (cb_id)
+                       *cb_id = id;
+       }
+
+       return 0;
+}
+
+static DBusMessage *hs_stop(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct headset *hs = device->headset;
+       DBusMessage *reply = NULL;
+
+       if (hs->state < HEADSET_STATE_PLAY_IN_PROGRESS)
+               return btd_error_not_connected(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       headset_set_state(device, HEADSET_STATE_CONNECTED);
+
+       return reply;
+}
+
+static DBusMessage *hs_is_playing(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct headset *hs = device->headset;
+       DBusMessage *reply;
+       dbus_bool_t playing;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       playing = (hs->state == HEADSET_STATE_PLAYING);
+
+       dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &playing,
+                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *hs_disconnect(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct headset *hs = device->headset;
+       char hs_address[18];
+
+       if (hs->state == HEADSET_STATE_DISCONNECTED)
+               return btd_error_not_connected(msg);
+
+       headset_shutdown(device);
+       ba2str(&device->dst, hs_address);
+       info("Disconnected from %s, %s", hs_address, device->path);
+
+       return dbus_message_new_method_return(msg);
+
+}
+
+static DBusMessage *hs_is_connected(DBusConnection *conn,
+                                               DBusMessage *msg,
+                                               void *data)
+{
+       struct audio_device *device = data;
+       DBusMessage *reply;
+       dbus_bool_t connected;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       connected = (device->headset->state >= HEADSET_STATE_CONNECTED);
+
+       dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
+                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *hs_connect(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct headset *hs = device->headset;
+       int err;
+
+       if (hs->state == HEADSET_STATE_CONNECTING)
+               return btd_error_in_progress(msg);
+       else if (hs->state > HEADSET_STATE_CONNECTING)
+               return btd_error_already_connected(msg);
+
+       if (hs->hfp_handle && !ag.telephony_ready)
+               return btd_error_not_ready(msg);
+
+       device->auto_connect = FALSE;
+
+       err = rfcomm_connect(device, NULL, NULL, NULL);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       hs->auto_dc = FALSE;
+
+       hs->pending->msg = dbus_message_ref(msg);
+
+       return NULL;
+}
+
+static DBusMessage *hs_ring(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct headset *hs = device->headset;
+       DBusMessage *reply = NULL;
+       int err;
+
+       if (hs->state < HEADSET_STATE_CONNECTED)
+               return btd_error_not_connected(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       if (ag.ring_timer) {
+               DBG("IndicateCall received when already indicating");
+               return reply;
+       }
+
+       err = headset_send(hs, "\r\nRING\r\n");
+       if (err < 0) {
+               dbus_message_unref(reply);
+               return btd_error_failed(msg, strerror(-err));
+       }
+
+       ring_timer_cb(NULL);
+       ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, ring_timer_cb,
+                                               NULL);
+
+       return reply;
+}
+
+static DBusMessage *hs_cancel_call(DBusConnection *conn,
+                                       DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct headset *hs = device->headset;
+       DBusMessage *reply = NULL;
+
+       if (hs->state < HEADSET_STATE_CONNECTED)
+               return btd_error_not_connected(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       if (ag.ring_timer) {
+               g_source_remove(ag.ring_timer);
+               ag.ring_timer = 0;
+       } else
+               DBG("Got CancelCall method call but no call is active");
+
+       return reply;
+}
+
+static DBusMessage *hs_play(DBusConnection *conn, DBusMessage *msg,
+                               void *data)
+{
+       struct audio_device *device = data;
+       struct headset *hs = device->headset;
+       int err;
+
+       if (sco_hci) {
+               error("Refusing Headset.Play() because SCO HCI routing "
+                               "is enabled");
+               return btd_error_not_available(msg);
+       }
+
+       switch (hs->state) {
+       case HEADSET_STATE_DISCONNECTED:
+       case HEADSET_STATE_CONNECTING:
+               return btd_error_not_connected(msg);
+       case HEADSET_STATE_PLAY_IN_PROGRESS:
+               if (hs->pending && hs->pending->msg == NULL) {
+                       hs->pending->msg = dbus_message_ref(msg);
+                       return NULL;
+               }
+               return btd_error_busy(msg);
+       case HEADSET_STATE_PLAYING:
+               return btd_error_already_connected(msg);
+       case HEADSET_STATE_CONNECTED:
+       default:
+               break;
+       }
+
+       err = sco_connect(device, NULL, NULL, NULL);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       hs->pending->msg = dbus_message_ref(msg);
+
+       return NULL;
+}
+
+static DBusMessage *hs_get_speaker_gain(DBusConnection *conn,
+                                       DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+       DBusMessage *reply;
+       dbus_uint16_t gain;
+
+       if (hs->state < HEADSET_STATE_CONNECTED)
+               return btd_error_not_available(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       gain = (dbus_uint16_t) slc->sp_gain;
+
+       dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain,
+                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *hs_get_mic_gain(DBusConnection *conn,
+                                       DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct headset *hs = device->headset;
+       struct headset_slc *slc = hs->slc;
+       DBusMessage *reply;
+       dbus_uint16_t gain;
+
+       if (hs->state < HEADSET_STATE_CONNECTED || slc == NULL)
+               return btd_error_not_available(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       gain = (dbus_uint16_t) slc->mic_gain;
+
+       dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain,
+                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *hs_set_gain(DBusConnection *conn,
+                               DBusMessage *msg,
+                               void *data, uint16_t gain,
+                               char type)
+{
+       struct audio_device *device = data;
+       struct headset *hs = device->headset;
+       DBusMessage *reply;
+       int err;
+
+       if (hs->state < HEADSET_STATE_CONNECTED)
+               return btd_error_not_connected(msg);
+
+       err = headset_set_gain(device, gain, type);
+       if (err < 0)
+               return btd_error_invalid_args(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       if (hs->state == HEADSET_STATE_PLAYING) {
+               err = headset_send(hs, "\r\n+VG%c=%u\r\n", type, gain);
+               if (err < 0) {
+                       dbus_message_unref(reply);
+                       return btd_error_failed(msg, strerror(-err));
+               }
+       }
+
+       return reply;
+}
+
+static DBusMessage *hs_set_speaker_gain(DBusConnection *conn,
+                                       DBusMessage *msg,
+                                       void *data)
+{
+       uint16_t gain;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &gain,
+                               DBUS_TYPE_INVALID))
+               return NULL;
+
+       return hs_set_gain(conn, msg, data, gain, HEADSET_GAIN_SPEAKER);
+}
+
+static DBusMessage *hs_set_mic_gain(DBusConnection *conn,
+                                       DBusMessage *msg,
+                                       void *data)
+{
+       uint16_t gain;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &gain,
+                               DBUS_TYPE_INVALID))
+               return NULL;
+
+       return hs_set_gain(conn, msg, data, gain, HEADSET_GAIN_MICROPHONE);
+}
+
+static DBusMessage *hs_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct audio_device *device = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       gboolean value;
+       const char *state;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+
+       /* Playing */
+       value = (device->headset->state == HEADSET_STATE_PLAYING);
+       dict_append_entry(&dict, "Playing", DBUS_TYPE_BOOLEAN, &value);
+
+       /* State */
+       state = state2str(device->headset->state);
+       if (state)
+               dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state);
+
+       /* Connected */
+       value = (device->headset->state >= HEADSET_STATE_CONNECTED);
+       dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
+
+       if (!value)
+               goto done;
+
+       /* SpeakerGain */
+       dict_append_entry(&dict, "SpeakerGain",
+                               DBUS_TYPE_UINT16,
+                               &device->headset->slc->sp_gain);
+
+       /* MicrophoneGain */
+       dict_append_entry(&dict, "MicrophoneGain",
+                               DBUS_TYPE_UINT16,
+                               &device->headset->slc->mic_gain);
+
+done:
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *hs_set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       const char *property;
+       DBusMessageIter iter;
+       DBusMessageIter sub;
+       uint16_t gain;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return btd_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return btd_error_invalid_args(msg);
+       dbus_message_iter_recurse(&iter, &sub);
+
+       if (g_str_equal("SpeakerGain", property)) {
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16)
+                       return btd_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&sub, &gain);
+               return hs_set_gain(conn, msg, data, gain,
+                                       HEADSET_GAIN_SPEAKER);
+       } else if (g_str_equal("MicrophoneGain", property)) {
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16)
+                       return btd_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&sub, &gain);
+               return hs_set_gain(conn, msg, data, gain,
+                                       HEADSET_GAIN_MICROPHONE);
+       }
+
+       return btd_error_invalid_args(msg);
+}
+
+static const GDBusMethodTable headset_methods[] = {
+       { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, hs_connect) },
+       { GDBUS_METHOD("Disconnect", NULL, NULL, hs_disconnect) },
+       { GDBUS_METHOD("IsConnected",
+                       NULL, GDBUS_ARGS({ "connected", "b" }),
+                       hs_is_connected) },
+       { GDBUS_METHOD("IndicateCall", NULL, NULL, hs_ring) },
+       { GDBUS_METHOD("CancelCall", NULL, NULL, hs_cancel_call) },
+       { GDBUS_DEPRECATED_ASYNC_METHOD("Play", NULL, NULL, hs_play) },
+       { GDBUS_METHOD("Stop", NULL, NULL, hs_stop) },
+       { GDBUS_DEPRECATED_METHOD("IsPlaying",
+                                       NULL, GDBUS_ARGS({ "playing", "b" }),
+                                       hs_is_playing) },
+       { GDBUS_DEPRECATED_METHOD("GetSpeakerGain",
+                                       NULL, GDBUS_ARGS({ "gain", "q" }),
+                                       hs_get_speaker_gain) },
+       { GDBUS_DEPRECATED_METHOD("GetMicrophoneGain",
+                                       NULL, GDBUS_ARGS({ "gain", "q" }),
+                                       hs_get_mic_gain) },
+       { GDBUS_DEPRECATED_METHOD("SetSpeakerGain",
+                                       GDBUS_ARGS({ "gain", "q" }), NULL,
+                                       hs_set_speaker_gain) },
+       { GDBUS_DEPRECATED_METHOD("SetMicrophoneGain",
+                                       GDBUS_ARGS({ "gain", "q" }), NULL,
+                                       hs_set_mic_gain) },
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       hs_get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+                       hs_set_property) },
+       { }
+};
+
+static const GDBusSignalTable headset_signals[] = {
+       { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) },
+       { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) },
+       { GDBUS_DEPRECATED_SIGNAL("AnswerRequested", NULL) },
+       { GDBUS_DEPRECATED_SIGNAL("Stopped", NULL) },
+       { GDBUS_DEPRECATED_SIGNAL("Playing", NULL) },
+       { GDBUS_DEPRECATED_SIGNAL("SpeakerGainChanged",
+                                               GDBUS_ARGS({ "gain", "q" })) },
+       { GDBUS_DEPRECATED_SIGNAL("MicrophoneGainChanged",
+                                               GDBUS_ARGS({ "gain", "q" })) },
+       { GDBUS_SIGNAL("CallTerminated", NULL) },
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+void headset_update(struct audio_device *dev, uint16_t svc,
+                       const char *uuidstr)
+{
+       struct headset *headset = dev->headset;
+       const sdp_record_t *record;
+
+       record = btd_device_get_record(dev->btd_dev, uuidstr);
+       if (!record)
+               return;
+
+       switch (svc) {
+       case HANDSFREE_SVCLASS_ID:
+               if (headset->hfp_handle &&
+                               (headset->hfp_handle != record->handle)) {
+                       error("More than one HFP record found on device");
+                       return;
+               }
+
+               headset->hfp_handle = record->handle;
+               break;
+
+       case HEADSET_SVCLASS_ID:
+               if (headset->hsp_handle &&
+                               (headset->hsp_handle != record->handle)) {
+                       error("More than one HSP record found on device");
+                       return;
+               }
+
+               headset->hsp_handle = record->handle;
+
+               /* Ignore this record if we already have access to HFP */
+               if (headset->hfp_handle)
+                       return;
+
+               break;
+
+       default:
+               DBG("Invalid record passed to headset_update");
+               return;
+       }
+}
+
+static int headset_close_rfcomm(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+       GIOChannel *rfcomm = hs->tmp_rfcomm ? hs->tmp_rfcomm : hs->rfcomm;
+
+       if (rfcomm) {
+               g_io_channel_shutdown(rfcomm, TRUE, NULL);
+               g_io_channel_unref(rfcomm);
+               hs->tmp_rfcomm = NULL;
+               hs->rfcomm = NULL;
+       }
+
+       g_free(hs->slc);
+       hs->slc = NULL;
+
+       return 0;
+}
+
+static void headset_free(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       if (hs->dc_timer) {
+               g_source_remove(hs->dc_timer);
+               hs->dc_timer = 0;
+       }
+
+       close_sco(dev);
+
+       headset_close_rfcomm(dev);
+
+       g_slist_free_full(hs->nrec_cbs, g_free);
+
+       g_free(hs);
+       dev->headset = NULL;
+}
+
+static void path_unregister(void *data)
+{
+       struct audio_device *dev = data;
+       struct headset *hs = dev->headset;
+
+       if (hs->state > HEADSET_STATE_DISCONNECTED) {
+               DBG("Headset unregistered while device was connected!");
+               headset_shutdown(dev);
+       }
+
+       DBG("Unregistered interface %s on path %s",
+               AUDIO_HEADSET_INTERFACE, dev->path);
+
+       headset_free(dev);
+}
+
+void headset_unregister(struct audio_device *dev)
+{
+       g_dbus_unregister_interface(dev->conn, dev->path,
+               AUDIO_HEADSET_INTERFACE);
+}
+
+struct headset *headset_init(struct audio_device *dev, uint16_t svc,
+                               const char *uuidstr)
+{
+       struct headset *hs;
+       const sdp_record_t *record;
+
+       hs = g_new0(struct headset, 1);
+       hs->rfcomm_ch = -1;
+       hs->search_hfp = server_is_enabled(&dev->src, HANDSFREE_SVCLASS_ID);
+
+       record = btd_device_get_record(dev->btd_dev, uuidstr);
+       if (!record)
+               goto register_iface;
+
+       switch (svc) {
+       case HANDSFREE_SVCLASS_ID:
+               hs->hfp_handle = record->handle;
+               break;
+
+       case HEADSET_SVCLASS_ID:
+               hs->hsp_handle = record->handle;
+               break;
+
+       default:
+               DBG("Invalid record passed to headset_init");
+               g_free(hs);
+               return NULL;
+       }
+
+register_iface:
+       if (!g_dbus_register_interface(dev->conn, dev->path,
+                                       AUDIO_HEADSET_INTERFACE,
+                                       headset_methods, headset_signals, NULL,
+                                       dev, path_unregister)) {
+               g_free(hs);
+               return NULL;
+       }
+
+       DBG("Registered interface %s on path %s",
+               AUDIO_HEADSET_INTERFACE, dev->path);
+
+       return hs;
+}
+
+uint32_t headset_config_init(GKeyFile *config)
+{
+       GError *err = NULL;
+       char *str;
+
+       /* Use the default values if there is no config file */
+       if (config == NULL)
+               return ag.features;
+
+       str = g_key_file_get_string(config, "General", "SCORouting",
+                                       &err);
+       if (err) {
+               DBG("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else {
+               if (strcmp(str, "PCM") == 0)
+                       sco_hci = FALSE;
+               else if (strcmp(str, "HCI") == 0)
+                       sco_hci = TRUE;
+               else
+                       error("Invalid Headset Routing value: %s", str);
+               g_free(str);
+       }
+
+       /* Init fast connectable option */
+       str = g_key_file_get_string(config, "Headset", "FastConnectable",
+                                       &err);
+       if (err) {
+               DBG("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else {
+               fast_connectable = strcmp(str, "true") == 0;
+               if (fast_connectable)
+                       manager_set_fast_connectable(FALSE);
+               g_free(str);
+       }
+
+       return ag.features;
+}
+
+static gboolean hs_dc_timeout(struct audio_device *dev)
+{
+       headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+       return FALSE;
+}
+
+gboolean headset_cancel_stream(struct audio_device *dev, unsigned int id)
+{
+       struct headset *hs = dev->headset;
+       struct pending_connect *p = hs->pending;
+       GSList *l;
+       struct connect_cb *cb = NULL;
+
+       if (!p)
+               return FALSE;
+
+       for (l = p->callbacks; l != NULL; l = l->next) {
+               struct connect_cb *tmp = l->data;
+
+               if (tmp->id == id) {
+                       cb = tmp;
+                       break;
+               }
+       }
+
+       if (!cb)
+               return FALSE;
+
+       p->callbacks = g_slist_remove(p->callbacks, cb);
+       g_free(cb);
+
+       if (p->callbacks || p->msg)
+               return TRUE;
+
+       if (hs->auto_dc) {
+               if (hs->rfcomm)
+                       hs->dc_timer = g_timeout_add_seconds(DC_TIMEOUT,
+                                               (GSourceFunc) hs_dc_timeout,
+                                               dev);
+               else
+                       headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+       }
+
+       return TRUE;
+}
+
+static gboolean dummy_connect_complete(struct audio_device *dev)
+{
+       pending_connect_finalize(dev);
+       return FALSE;
+}
+
+unsigned int headset_request_stream(struct audio_device *dev,
+                                       headset_stream_cb_t cb,
+                                       void *user_data)
+{
+       struct headset *hs = dev->headset;
+       unsigned int id;
+
+       if (hs->state == HEADSET_STATE_PLAYING) {
+               id = connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data);
+               g_idle_add((GSourceFunc) dummy_connect_complete, dev);
+               return id;
+       }
+
+       if (hs->dc_timer) {
+               g_source_remove(hs->dc_timer);
+               hs->dc_timer = 0;
+       }
+
+       if (hs->state == HEADSET_STATE_CONNECTING ||
+                       hs->state == HEADSET_STATE_PLAY_IN_PROGRESS)
+               return connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data);
+
+       if (hs->rfcomm == NULL) {
+               if (rfcomm_connect(dev, cb, user_data, &id) < 0)
+                       return 0;
+               hs->auto_dc = TRUE;
+       } else if (sco_connect(dev, cb, user_data, &id) < 0)
+               return 0;
+
+       hs->pending->target_state = HEADSET_STATE_PLAYING;
+
+       return id;
+}
+
+unsigned int headset_config_stream(struct audio_device *dev,
+                                       gboolean auto_dc,
+                                       headset_stream_cb_t cb,
+                                       void *user_data)
+{
+       struct headset *hs = dev->headset;
+       unsigned int id = 0;
+
+       if (hs->dc_timer) {
+               g_source_remove(hs->dc_timer);
+               hs->dc_timer = 0;
+       }
+
+       if (hs->state == HEADSET_STATE_CONNECTING)
+               return connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb,
+                                       user_data);
+
+       if (hs->rfcomm)
+               goto done;
+
+       if (rfcomm_connect(dev, cb, user_data, &id) < 0)
+               return 0;
+
+       hs->auto_dc = auto_dc;
+       hs->pending->target_state = HEADSET_STATE_CONNECTED;
+
+       return id;
+
+done:
+       id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, user_data);
+       g_idle_add((GSourceFunc) dummy_connect_complete, dev);
+       return id;
+}
+
+unsigned int headset_suspend_stream(struct audio_device *dev,
+                                       headset_stream_cb_t cb,
+                                       void *user_data)
+{
+       struct headset *hs = dev->headset;
+       unsigned int id;
+       int sock;
+
+       if (hs->state == HEADSET_STATE_DISCONNECTED ||
+                               hs->state == HEADSET_STATE_CONNECTING)
+               return 0;
+
+       if (hs->dc_timer) {
+               g_source_remove(hs->dc_timer);
+               hs->dc_timer = 0;
+       }
+
+       if (hs->sco) {
+               sock = g_io_channel_unix_get_fd(hs->sco);
+
+               /* shutdown but leave the socket open and wait for hup */
+               shutdown(sock, SHUT_RDWR);
+       } else {
+               headset_set_state(dev, HEADSET_STATE_CONNECTED);
+
+               g_idle_add((GSourceFunc) dummy_connect_complete, dev);
+       }
+
+       id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, user_data);
+
+       return id;
+}
+
+gboolean headset_get_hfp_active(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       return hs->hfp_active;
+}
+
+void headset_set_hfp_active(struct audio_device *dev, gboolean active)
+{
+       struct headset *hs = dev->headset;
+
+       hs->hfp_active = active;
+}
+
+gboolean headset_get_rfcomm_initiator(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       return hs->rfcomm_initiator;
+}
+
+void headset_set_rfcomm_initiator(struct audio_device *dev,
+                                       gboolean initiator)
+{
+       struct headset *hs = dev->headset;
+
+       hs->rfcomm_initiator = initiator;
+}
+
+GIOChannel *headset_get_rfcomm(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       return hs->tmp_rfcomm;
+}
+
+int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *io)
+{
+       struct headset *hs = dev->headset;
+
+       if (hs->tmp_rfcomm)
+               return -EALREADY;
+
+       hs->tmp_rfcomm = g_io_channel_ref(io);
+
+       return 0;
+}
+
+int headset_connect_sco(struct audio_device *dev, GIOChannel *io)
+{
+       struct headset *hs = dev->headset;
+       struct headset_slc *slc = hs->slc;
+
+       if (hs->sco)
+               return -EISCONN;
+
+       hs->sco = g_io_channel_ref(io);
+
+       if (slc->pending_ring) {
+               ring_timer_cb(NULL);
+               ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL,
+                                               ring_timer_cb,
+                                               NULL);
+               slc->pending_ring = FALSE;
+       }
+
+       return 0;
+}
+
+void headset_set_state(struct audio_device *dev, headset_state_t state)
+{
+       struct headset *hs = dev->headset;
+       struct headset_slc *slc = hs->slc;
+       gboolean value;
+       const char *state_str;
+       headset_state_t old_state = hs->state;
+       GSList *l;
+
+       if (old_state == state)
+               return;
+
+       state_str = state2str(state);
+
+       switch (state) {
+       case HEADSET_STATE_DISCONNECTED:
+               value = FALSE;
+               close_sco(dev);
+               headset_close_rfcomm(dev);
+               emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_HEADSET_INTERFACE, "State",
+                                       DBUS_TYPE_STRING, &state_str);
+               g_dbus_emit_signal(dev->conn, dev->path,
+                                       AUDIO_HEADSET_INTERFACE,
+                                       "Disconnected",
+                                       DBUS_TYPE_INVALID);
+               if (hs->state > HEADSET_STATE_CONNECTING) {
+                       emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_HEADSET_INTERFACE, "Connected",
+                                       DBUS_TYPE_BOOLEAN, &value);
+                       telephony_device_disconnected(dev);
+               }
+               active_devices = g_slist_remove(active_devices, dev);
+               break;
+       case HEADSET_STATE_CONNECTING:
+               emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_HEADSET_INTERFACE, "State",
+                                       DBUS_TYPE_STRING, &state_str);
+               break;
+       case HEADSET_STATE_CONNECTED:
+               close_sco(dev);
+               if (hs->state != HEADSET_STATE_PLAY_IN_PROGRESS)
+                       emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_HEADSET_INTERFACE, "State",
+                                       DBUS_TYPE_STRING, &state_str);
+               if (hs->state < state) {
+                       if (ag.features & AG_FEATURE_INBAND_RINGTONE)
+                               slc->inband_ring = TRUE;
+                       else
+                               slc->inband_ring = FALSE;
+                       g_dbus_emit_signal(dev->conn, dev->path,
+                                               AUDIO_HEADSET_INTERFACE,
+                                               "Connected",
+                                               DBUS_TYPE_INVALID);
+                       value = TRUE;
+                       emit_property_changed(dev->conn, dev->path,
+                                               AUDIO_HEADSET_INTERFACE,
+                                               "Connected",
+                                               DBUS_TYPE_BOOLEAN, &value);
+                       active_devices = g_slist_append(active_devices, dev);
+                       telephony_device_connected(dev);
+               } else if (hs->state == HEADSET_STATE_PLAYING) {
+                       value = FALSE;
+                       g_dbus_emit_signal(dev->conn, dev->path,
+                                               AUDIO_HEADSET_INTERFACE,
+                                               "Stopped",
+                                               DBUS_TYPE_INVALID);
+                       emit_property_changed(dev->conn, dev->path,
+                                               AUDIO_HEADSET_INTERFACE,
+                                               "Playing",
+                                               DBUS_TYPE_BOOLEAN, &value);
+               }
+               break;
+       case HEADSET_STATE_PLAY_IN_PROGRESS:
+               break;
+       case HEADSET_STATE_PLAYING:
+               value = TRUE;
+               emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_HEADSET_INTERFACE, "State",
+                                       DBUS_TYPE_STRING, &state_str);
+
+               /* Do not watch HUP since we need to know when the link is
+                  really disconnected */
+               hs->sco_id = g_io_add_watch(hs->sco,
+                                       G_IO_ERR | G_IO_NVAL,
+                                       (GIOFunc) sco_cb, dev);
+
+               g_dbus_emit_signal(dev->conn, dev->path,
+                                       AUDIO_HEADSET_INTERFACE, "Playing",
+                                       DBUS_TYPE_INVALID);
+               emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_HEADSET_INTERFACE, "Playing",
+                                       DBUS_TYPE_BOOLEAN, &value);
+
+               if (slc->sp_gain >= 0)
+                       headset_send(hs, "\r\n+VGS=%u\r\n", slc->sp_gain);
+               if (slc->mic_gain >= 0)
+                       headset_send(hs, "\r\n+VGM=%u\r\n", slc->mic_gain);
+               break;
+       }
+
+       hs->state = state;
+
+       DBG("State changed %s: %s -> %s", dev->path, str_state[old_state],
+               str_state[state]);
+
+       for (l = headset_callbacks; l != NULL; l = l->next) {
+               struct headset_state_callback *cb = l->data;
+               cb->cb(dev, old_state, state, cb->user_data);
+       }
+}
+
+headset_state_t headset_get_state(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       return hs->state;
+}
+
+int headset_get_channel(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       return hs->rfcomm_ch;
+}
+
+gboolean headset_is_active(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       if (hs->state != HEADSET_STATE_DISCONNECTED)
+               return TRUE;
+
+       return FALSE;
+}
+
+headset_lock_t headset_get_lock(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       return hs->lock;
+}
+
+gboolean headset_lock(struct audio_device *dev, headset_lock_t lock)
+{
+       struct headset *hs = dev->headset;
+
+       if (hs->lock & lock)
+               return FALSE;
+
+       hs->lock |= lock;
+
+       return TRUE;
+}
+
+gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock)
+{
+       struct headset *hs = dev->headset;
+
+       if (!(hs->lock & lock))
+               return FALSE;
+
+       hs->lock &= ~lock;
+
+       if (hs->lock)
+               return TRUE;
+
+       if (hs->state == HEADSET_STATE_PLAYING)
+               headset_set_state(dev, HEADSET_STATE_CONNECTED);
+
+       if (hs->auto_dc) {
+               if (hs->state == HEADSET_STATE_CONNECTED)
+                       hs->dc_timer = g_timeout_add_seconds(DC_TIMEOUT,
+                                               (GSourceFunc) hs_dc_timeout,
+                                               dev);
+               else
+                       headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+       }
+
+       return TRUE;
+}
+
+gboolean headset_suspend(struct audio_device *dev, void *data)
+{
+       return TRUE;
+}
+
+gboolean headset_play(struct audio_device *dev, void *data)
+{
+       return TRUE;
+}
+
+int headset_get_sco_fd(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       if (!hs->sco)
+               return -1;
+
+       return g_io_channel_unix_get_fd(hs->sco);
+}
+
+gboolean headset_get_nrec(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       if (!hs->slc)
+               return TRUE;
+
+       return hs->slc->nrec;
+}
+
+unsigned int headset_add_nrec_cb(struct audio_device *dev,
+                                       headset_nrec_cb cb, void *user_data)
+{
+       struct headset *hs = dev->headset;
+       struct headset_nrec_callback *nrec_cb;
+       static unsigned int id = 0;
+
+       nrec_cb = g_new(struct headset_nrec_callback, 1);
+       nrec_cb->cb = cb;
+       nrec_cb->user_data = user_data;
+       nrec_cb->id = ++id;
+
+       hs->nrec_cbs = g_slist_prepend(hs->nrec_cbs, nrec_cb);
+
+       return nrec_cb->id;
+}
+
+gboolean headset_remove_nrec_cb(struct audio_device *dev, unsigned int id)
+{
+       struct headset *hs = dev->headset;
+       GSList *l;
+
+       for (l = hs->nrec_cbs; l != NULL; l = l->next) {
+               struct headset_nrec_callback *cb = l->data;
+               if (cb && cb->id == id) {
+                       hs->nrec_cbs = g_slist_remove(hs->nrec_cbs, cb);
+                       g_free(cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+gboolean headset_get_inband(struct audio_device *dev)
+{
+       struct headset *hs = dev->headset;
+
+       if (!hs->slc)
+               return TRUE;
+
+       return hs->slc->inband_ring;
+}
+
+gboolean headset_get_sco_hci(struct audio_device *dev)
+{
+       return sco_hci;
+}
+
+void headset_shutdown(struct audio_device *dev)
+{
+       struct pending_connect *p = dev->headset->pending;
+
+       if (p && p->msg)
+               error_connect_failed(dev->conn, p->msg, ECANCELED);
+
+       pending_connect_finalize(dev);
+       headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+}
+
+int telephony_event_ind(int index)
+{
+       if (!active_devices)
+               return -ENODEV;
+
+       if (!ag.er_ind) {
+               DBG("telephony_report_event called but events are disabled");
+               return -EINVAL;
+       }
+
+       send_foreach_headset(active_devices, hfp_cmp,
+                               "\r\n+CIEV: %d,%d\r\n", index + 1,
+                               ag.indicators[index].val);
+
+       return 0;
+}
+
+int telephony_response_and_hold_ind(int rh)
+{
+       if (!active_devices)
+               return -ENODEV;
+
+       ag.rh = rh;
+
+       /* If we aren't in any response and hold state don't send anything */
+       if (ag.rh < 0)
+               return 0;
+
+       send_foreach_headset(active_devices, hfp_cmp, "\r\n+BTRH: %d\r\n",
+                               ag.rh);
+
+       return 0;
+}
+
+int telephony_incoming_call_ind(const char *number, int type)
+{
+       struct audio_device *dev;
+       struct headset *hs;
+       struct headset_slc *slc;
+
+       if (fast_connectable)
+               manager_set_fast_connectable(TRUE);
+
+       if (!active_devices)
+               return -ENODEV;
+
+       /* Get the latest connected device */
+       dev = active_devices->data;
+       hs = dev->headset;
+       slc = hs->slc;
+
+       if (ag.ring_timer) {
+               DBG("telephony_incoming_call_ind: already calling");
+               return -EBUSY;
+       }
+
+       /* With HSP 1.2 the RING messages should *not* be sent if inband
+        * ringtone is being used */
+       if (!hs->hfp_active && slc->inband_ring)
+               return 0;
+
+       g_free(ag.number);
+       ag.number = g_strdup(number);
+       ag.number_type = type;
+
+       if (slc->inband_ring && hs->hfp_active &&
+                                       hs->state != HEADSET_STATE_PLAYING) {
+               slc->pending_ring = TRUE;
+               return 0;
+       }
+
+       ring_timer_cb(NULL);
+       ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, ring_timer_cb,
+                                               NULL);
+
+       return 0;
+}
+
+int telephony_calling_stopped_ind(void)
+{
+       struct audio_device *dev;
+
+       if (fast_connectable)
+               manager_set_fast_connectable(FALSE);
+
+       if (ag.ring_timer) {
+               g_source_remove(ag.ring_timer);
+               ag.ring_timer = 0;
+       }
+
+       if (!active_devices)
+               return 0;
+
+       /* In case SCO isn't fully up yet */
+       dev = active_devices->data;
+
+       if (!dev->headset->slc->pending_ring && !ag.ring_timer)
+               return -EINVAL;
+
+       dev->headset->slc->pending_ring = FALSE;
+
+       return 0;
+}
+
+int telephony_ready_ind(uint32_t features,
+                       const struct indicator *indicators, int rh,
+                       const char *chld)
+{
+       ag.telephony_ready = TRUE;
+       ag.features = features;
+       ag.indicators = indicators;
+       ag.rh = rh;
+       ag.chld = chld;
+
+       DBG("Telephony plugin initialized");
+
+       print_ag_features(ag.features);
+
+       return 0;
+}
+
+int telephony_deinit(void)
+{
+       g_free(ag.number);
+
+       memset(&ag, 0, sizeof(ag));
+
+       ag.er_mode = 3;
+       ag.rh = BTRH_NOT_SUPPORTED;
+
+       DBG("Telephony deinitialized");
+
+       return 0;
+}
+
+int telephony_list_current_call_ind(int idx, int dir, int status, int mode,
+                                       int mprty, const char *number,
+                                       int type)
+{
+       if (!active_devices)
+               return -ENODEV;
+
+       if (number && strlen(number) > 0)
+               send_foreach_headset(active_devices, hfp_cmp,
+                               "\r\n+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n",
+                               idx, dir, status, mode, mprty, number, type);
+       else
+               send_foreach_headset(active_devices, hfp_cmp,
+                                       "\r\n+CLCC: %d,%d,%d,%d,%d\r\n",
+                                       idx, dir, status, mode, mprty);
+
+       return 0;
+}
+
+int telephony_subscriber_number_ind(const char *number, int type, int service)
+{
+       if (!active_devices)
+               return -ENODEV;
+
+       send_foreach_headset(active_devices, hfp_cmp,
+                               "\r\n+CNUM: ,%s,%d,,%d\r\n",
+                               number, type, service);
+
+       return 0;
+}
+
+static int cwa_cmp(struct headset *hs)
+{
+       if (!hs->hfp_active)
+               return -1;
+
+       if (hs->slc->cwa_enabled)
+               return 0;
+       else
+               return -1;
+}
+
+int telephony_call_waiting_ind(const char *number, int type)
+{
+       if (!active_devices)
+               return -ENODEV;
+
+       send_foreach_headset(active_devices, cwa_cmp,
+                               "\r\n+CCWA: \"%s\",%d\r\n",
+                               number, type);
+
+       return 0;
+}
+
+unsigned int headset_add_state_cb(headset_state_cb cb, void *user_data)
+{
+       struct headset_state_callback *state_cb;
+       static unsigned int id = 0;
+
+       state_cb = g_new(struct headset_state_callback, 1);
+       state_cb->cb = cb;
+       state_cb->user_data = user_data;
+       state_cb->id = ++id;
+
+       headset_callbacks = g_slist_append(headset_callbacks, state_cb);
+
+       return state_cb->id;
+}
+
+gboolean headset_remove_state_cb(unsigned int id)
+{
+       GSList *l;
+
+       for (l = headset_callbacks; l != NULL; l = l->next) {
+               struct headset_state_callback *cb = l->data;
+               if (cb && cb->id == id) {
+                       headset_callbacks = g_slist_remove(headset_callbacks, cb);
+                       g_free(cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
diff --git a/audio/headset.h b/audio/headset.h
new file mode 100644 (file)
index 0000000..465c2d6
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define AUDIO_HEADSET_INTERFACE "org.bluez.Headset"
+
+#define DEFAULT_HS_AG_CHANNEL 12
+#define DEFAULT_HF_AG_CHANNEL 13
+
+typedef enum {
+       HEADSET_STATE_DISCONNECTED,
+       HEADSET_STATE_CONNECTING,
+       HEADSET_STATE_CONNECTED,
+       HEADSET_STATE_PLAY_IN_PROGRESS,
+       HEADSET_STATE_PLAYING
+} headset_state_t;
+
+typedef enum {
+       HEADSET_LOCK_READ = 1,
+       HEADSET_LOCK_WRITE = 1 << 1,
+} headset_lock_t;
+
+typedef void (*headset_state_cb) (struct audio_device *dev,
+                                       headset_state_t old_state,
+                                       headset_state_t new_state,
+                                       void *user_data);
+typedef void (*headset_nrec_cb) (struct audio_device *dev,
+                                       gboolean nrec,
+                                       void *user_data);
+
+unsigned int headset_add_state_cb(headset_state_cb cb, void *user_data);
+gboolean headset_remove_state_cb(unsigned int id);
+
+typedef void (*headset_stream_cb_t) (struct audio_device *dev, void *user_data);
+
+void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data);
+
+GIOChannel *headset_get_rfcomm(struct audio_device *dev);
+
+struct headset *headset_init(struct audio_device *dev, uint16_t svc,
+                               const char *uuidstr);
+
+void headset_unregister(struct audio_device *dev);
+
+uint32_t headset_config_init(GKeyFile *config);
+
+void headset_update(struct audio_device *dev, uint16_t svc,
+                       const char *uuidstr);
+
+unsigned int headset_config_stream(struct audio_device *dev,
+                                       gboolean auto_dc,
+                                       headset_stream_cb_t cb,
+                                       void *user_data);
+unsigned int headset_request_stream(struct audio_device *dev,
+                                       headset_stream_cb_t cb,
+                                       void *user_data);
+unsigned int headset_suspend_stream(struct audio_device *dev,
+                                       headset_stream_cb_t cb,
+                                       void *user_data);
+gboolean headset_cancel_stream(struct audio_device *dev, unsigned int id);
+
+gboolean headset_get_hfp_active(struct audio_device *dev);
+void headset_set_hfp_active(struct audio_device *dev, gboolean active);
+
+gboolean headset_get_rfcomm_initiator(struct audio_device *dev);
+void headset_set_rfcomm_initiator(struct audio_device *dev,
+                                                       gboolean initiator);
+
+int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
+int headset_connect_sco(struct audio_device *dev, GIOChannel *io);
+
+headset_state_t headset_get_state(struct audio_device *dev);
+void headset_set_state(struct audio_device *dev, headset_state_t state);
+
+int headset_get_channel(struct audio_device *dev);
+
+int headset_get_sco_fd(struct audio_device *dev);
+gboolean headset_get_nrec(struct audio_device *dev);
+unsigned int headset_add_nrec_cb(struct audio_device *dev,
+                                       headset_nrec_cb cb, void *user_data);
+gboolean headset_remove_nrec_cb(struct audio_device *dev, unsigned int id);
+gboolean headset_get_inband(struct audio_device *dev);
+gboolean headset_get_sco_hci(struct audio_device *dev);
+
+gboolean headset_is_active(struct audio_device *dev);
+
+headset_lock_t headset_get_lock(struct audio_device *dev);
+gboolean headset_lock(struct audio_device *dev, headset_lock_t lock);
+gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock);
+gboolean headset_suspend(struct audio_device *dev, void *data);
+gboolean headset_play(struct audio_device *dev, void *data);
+void headset_shutdown(struct audio_device *dev);
diff --git a/audio/ipc.c b/audio/ipc.c
new file mode 100644 (file)
index 0000000..02d956b
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "ipc.h"
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+/* This table contains the string representation for messages types */
+static const char *strtypes[] = {
+       "BT_REQUEST",
+       "BT_RESPONSE",
+       "BT_INDICATION",
+       "BT_ERROR",
+};
+
+/* This table contains the string representation for messages names */
+static const char *strnames[] = {
+       "BT_GET_CAPABILITIES",
+       "BT_OPEN",
+       "BT_SET_CONFIGURATION",
+       "BT_NEW_STREAM",
+       "BT_START_STREAM",
+       "BT_STOP_STREAM",
+       "BT_SUSPEND_STREAM",
+       "BT_RESUME_STREAM",
+       "BT_CONTROL",
+};
+
+int bt_audio_service_open(void)
+{
+       int sk;
+       int err;
+       struct sockaddr_un addr = {
+               AF_UNIX, BT_IPC_SOCKET_NAME
+       };
+
+       sk = socket(PF_LOCAL, SOCK_STREAM, 0);
+       if (sk < 0) {
+               err = -errno;
+               fprintf(stderr, "%s: Cannot open socket: %s (%d)\n",
+                       __FUNCTION__, strerror(-err), -err);
+               errno = -err;
+               return -1;
+       }
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               err = -errno;
+               fprintf(stderr, "%s: connect() failed: %s (%d)\n",
+                       __FUNCTION__, strerror(-err), -err);
+               close(sk);
+               errno = -err;
+               return -1;
+       }
+
+       return sk;
+}
+
+int bt_audio_service_close(int sk)
+{
+       return close(sk);
+}
+
+int bt_audio_service_get_data_fd(int sk)
+{
+       char cmsg_b[CMSG_SPACE(sizeof(int))], m;
+       int err, ret;
+       struct iovec iov = { &m, sizeof(m) };
+       struct msghdr msgh;
+       struct cmsghdr *cmsg;
+
+       memset(&msgh, 0, sizeof(msgh));
+       msgh.msg_iov = &iov;
+       msgh.msg_iovlen = 1;
+       msgh.msg_control = &cmsg_b;
+       msgh.msg_controllen = CMSG_LEN(sizeof(int));
+
+       ret = recvmsg(sk, &msgh, 0);
+       if (ret < 0) {
+               err = -errno;
+               fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n",
+                       __FUNCTION__, strerror(-err), -err);
+               errno = -err;
+               return -1;
+       }
+
+       /* Receive auxiliary data in msgh */
+       for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL;
+                       cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
+               if (cmsg->cmsg_level == SOL_SOCKET
+                               && cmsg->cmsg_type == SCM_RIGHTS) {
+                       memcpy(&ret, CMSG_DATA(cmsg), sizeof(int));
+                       return ret;
+               }
+       }
+
+       errno = EINVAL;
+       return -1;
+}
+
+const char *bt_audio_strtype(uint8_t type)
+{
+       if (type >= ARRAY_SIZE(strtypes))
+               return NULL;
+
+       return strtypes[type];
+}
+
+const char *bt_audio_strname(uint8_t name)
+{
+       if (name >= ARRAY_SIZE(strnames))
+               return NULL;
+
+       return strnames[name];
+}
diff --git a/audio/ipc.h b/audio/ipc.h
new file mode 100644 (file)
index 0000000..61ae019
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/*
+  Message sequence chart of streaming sequence for A2DP transport
+
+  Audio daemon                 User
+                               on snd_pcm_open
+                               <--BT_GET_CAPABILITIES_REQ
+
+  BT_GET_CAPABILITIES_RSP-->
+
+                               on snd_pcm_hw_params
+                               <--BT_SETCONFIGURATION_REQ
+
+  BT_SET_CONFIGURATION_RSP-->
+
+                               on snd_pcm_prepare
+                               <--BT_START_STREAM_REQ
+
+  <Moves to streaming state>
+  BT_START_STREAM_RSP-->
+
+  BT_NEW_STREAM_IND -->
+
+                               <  streams data >
+                               ..........
+
+                               on snd_pcm_drop/snd_pcm_drain
+
+                               <--BT_STOP_STREAM_REQ
+
+  <Moves to open state>
+  BT_STOP_STREAM_RSP-->
+
+                               on IPC close or appl crash
+  <Moves to idle>
+
+ */
+
+#ifndef BT_AUDIOCLIENT_H
+#define BT_AUDIOCLIENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+
+#define BT_SUGGESTED_BUFFER_SIZE   512
+#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio"
+
+/* Generic message header definition, except for RESPONSE messages */
+typedef struct {
+       uint8_t type;
+       uint8_t name;
+       uint16_t length;
+} __attribute__ ((packed)) bt_audio_msg_header_t;
+
+typedef struct {
+       bt_audio_msg_header_t h;
+       uint8_t posix_errno;
+} __attribute__ ((packed)) bt_audio_error_t;
+
+/* Message types */
+#define BT_REQUEST                     0
+#define BT_RESPONSE                    1
+#define BT_INDICATION                  2
+#define BT_ERROR                       3
+
+/* Messages names */
+#define BT_GET_CAPABILITIES            0
+#define BT_OPEN                                1
+#define BT_SET_CONFIGURATION           2
+#define BT_NEW_STREAM                  3
+#define BT_START_STREAM                        4
+#define BT_STOP_STREAM                 5
+#define BT_CLOSE                       6
+#define BT_CONTROL                     7
+#define BT_DELAY_REPORT                        8
+
+#define BT_CAPABILITIES_TRANSPORT_A2DP 0
+#define BT_CAPABILITIES_TRANSPORT_SCO  1
+#define BT_CAPABILITIES_TRANSPORT_ANY  2
+
+#define BT_CAPABILITIES_ACCESS_MODE_READ       1
+#define BT_CAPABILITIES_ACCESS_MODE_WRITE      2
+#define BT_CAPABILITIES_ACCESS_MODE_READWRITE  3
+
+#define BT_FLAG_AUTOCONNECT    1
+
+struct bt_get_capabilities_req {
+       bt_audio_msg_header_t   h;
+       char                    source[18];     /* Address of the local Device */
+       char                    destination[18];/* Address of the remote Device */
+       char                    object[128];    /* DBus object path */
+       uint8_t                 transport;      /* Requested transport */
+       uint8_t                 flags;          /* Requested flags */
+       uint8_t                 seid;           /* Requested capability configuration */
+} __attribute__ ((packed));
+
+/**
+ * SBC Codec parameters as per A2DP profile 1.0 Â§ 4.3
+ */
+
+/* A2DP seid are 6 bytes long so HSP/HFP are assigned to 7-8 bits */
+#define BT_A2DP_SEID_RANGE                     (1 << 6) - 1
+
+#define BT_A2DP_SBC_SOURCE                     0x00
+#define BT_A2DP_SBC_SINK                       0x01
+#define BT_A2DP_MPEG12_SOURCE                  0x02
+#define BT_A2DP_MPEG12_SINK                    0x03
+#define BT_A2DP_MPEG24_SOURCE                  0x04
+#define BT_A2DP_MPEG24_SINK                    0x05
+#define BT_A2DP_ATRAC_SOURCE                   0x06
+#define BT_A2DP_ATRAC_SINK                     0x07
+#define BT_A2DP_UNKNOWN_SOURCE                 0x08
+#define BT_A2DP_UNKNOWN_SINK                   0x09
+
+#define BT_SBC_SAMPLING_FREQ_16000             (1 << 3)
+#define BT_SBC_SAMPLING_FREQ_32000             (1 << 2)
+#define BT_SBC_SAMPLING_FREQ_44100             (1 << 1)
+#define BT_SBC_SAMPLING_FREQ_48000             1
+
+#define BT_A2DP_CHANNEL_MODE_MONO              (1 << 3)
+#define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL      (1 << 2)
+#define BT_A2DP_CHANNEL_MODE_STEREO            (1 << 1)
+#define BT_A2DP_CHANNEL_MODE_JOINT_STEREO      1
+
+#define BT_A2DP_BLOCK_LENGTH_4                 (1 << 3)
+#define BT_A2DP_BLOCK_LENGTH_8                 (1 << 2)
+#define BT_A2DP_BLOCK_LENGTH_12                        (1 << 1)
+#define BT_A2DP_BLOCK_LENGTH_16                        1
+
+#define BT_A2DP_SUBBANDS_4                     (1 << 1)
+#define BT_A2DP_SUBBANDS_8                     1
+
+#define BT_A2DP_ALLOCATION_SNR                 (1 << 1)
+#define BT_A2DP_ALLOCATION_LOUDNESS            1
+
+#define BT_MPEG_SAMPLING_FREQ_16000            (1 << 5)
+#define BT_MPEG_SAMPLING_FREQ_22050            (1 << 4)
+#define BT_MPEG_SAMPLING_FREQ_24000            (1 << 3)
+#define BT_MPEG_SAMPLING_FREQ_32000            (1 << 2)
+#define BT_MPEG_SAMPLING_FREQ_44100            (1 << 1)
+#define BT_MPEG_SAMPLING_FREQ_48000            1
+
+#define BT_MPEG_LAYER_1                                (1 << 2)
+#define BT_MPEG_LAYER_2                                (1 << 1)
+#define BT_MPEG_LAYER_3                                1
+
+#define BT_HFP_CODEC_PCM                       0x00
+
+#define BT_PCM_FLAG_NREC                       0x01
+#define BT_PCM_FLAG_PCM_ROUTING                        0x02
+
+#define BT_WRITE_LOCK                          (1 << 1)
+#define BT_READ_LOCK                           1
+
+typedef struct {
+       uint8_t seid;
+       uint8_t transport;
+       uint8_t type;
+       uint8_t length;
+       uint8_t configured;
+       uint8_t lock;
+       uint8_t data[0];
+} __attribute__ ((packed)) codec_capabilities_t;
+
+typedef struct {
+       codec_capabilities_t capability;
+       uint8_t channel_mode;
+       uint8_t frequency;
+       uint8_t allocation_method;
+       uint8_t subbands;
+       uint8_t block_length;
+       uint8_t min_bitpool;
+       uint8_t max_bitpool;
+} __attribute__ ((packed)) sbc_capabilities_t;
+
+typedef struct {
+       codec_capabilities_t capability;
+       uint8_t channel_mode;
+       uint8_t crc;
+       uint8_t layer;
+       uint8_t frequency;
+       uint8_t mpf;
+       uint16_t bitrate;
+} __attribute__ ((packed)) mpeg_capabilities_t;
+
+typedef struct {
+       codec_capabilities_t capability;
+       uint8_t flags;
+       uint16_t sampling_rate;
+} __attribute__ ((packed)) pcm_capabilities_t;
+
+struct bt_get_capabilities_rsp {
+       bt_audio_msg_header_t   h;
+       char                    source[18];     /* Address of the local Device */
+       char                    destination[18];/* Address of the remote Device */
+       char                    object[128];    /* DBus object path */
+       uint8_t                 data[0];        /* First codec_capabilities_t */
+} __attribute__ ((packed));
+
+struct bt_open_req {
+       bt_audio_msg_header_t   h;
+       char                    source[18];     /* Address of the local Device */
+       char                    destination[18];/* Address of the remote Device */
+       char                    object[128];    /* DBus object path */
+       uint8_t                 seid;           /* Requested capability configuration to lock */
+       uint8_t                 lock;           /* Requested lock */
+} __attribute__ ((packed));
+
+struct bt_open_rsp {
+       bt_audio_msg_header_t   h;
+       char                    source[18];     /* Address of the local Device */
+       char                    destination[18];/* Address of the remote Device */
+       char                    object[128];    /* DBus object path */
+} __attribute__ ((packed));
+
+struct bt_set_configuration_req {
+       bt_audio_msg_header_t   h;
+       codec_capabilities_t    codec;          /* Requested codec */
+} __attribute__ ((packed));
+
+struct bt_set_configuration_rsp {
+       bt_audio_msg_header_t   h;
+       uint16_t                link_mtu;       /* Max length that transport supports */
+} __attribute__ ((packed));
+
+#define BT_STREAM_ACCESS_READ          0
+#define BT_STREAM_ACCESS_WRITE         1
+#define BT_STREAM_ACCESS_READWRITE     2
+struct bt_start_stream_req {
+       bt_audio_msg_header_t   h;
+} __attribute__ ((packed));
+
+struct bt_start_stream_rsp {
+       bt_audio_msg_header_t   h;
+} __attribute__ ((packed));
+
+/* This message is followed by one byte of data containing the stream data fd
+   as ancillary data */
+struct bt_new_stream_ind {
+       bt_audio_msg_header_t   h;
+} __attribute__ ((packed));
+
+struct bt_stop_stream_req {
+       bt_audio_msg_header_t   h;
+} __attribute__ ((packed));
+
+struct bt_stop_stream_rsp {
+       bt_audio_msg_header_t   h;
+} __attribute__ ((packed));
+
+struct bt_close_req {
+       bt_audio_msg_header_t   h;
+} __attribute__ ((packed));
+
+struct bt_close_rsp {
+       bt_audio_msg_header_t   h;
+} __attribute__ ((packed));
+
+struct bt_suspend_stream_ind {
+       bt_audio_msg_header_t   h;
+} __attribute__ ((packed));
+
+struct bt_resume_stream_ind {
+       bt_audio_msg_header_t   h;
+} __attribute__ ((packed));
+
+#define BT_CONTROL_KEY_POWER                   0x40
+#define BT_CONTROL_KEY_VOL_UP                  0x41
+#define BT_CONTROL_KEY_VOL_DOWN                        0x42
+#define BT_CONTROL_KEY_MUTE                    0x43
+#define BT_CONTROL_KEY_PLAY                    0x44
+#define BT_CONTROL_KEY_STOP                    0x45
+#define BT_CONTROL_KEY_PAUSE                   0x46
+#define BT_CONTROL_KEY_RECORD                  0x47
+#define BT_CONTROL_KEY_REWIND                  0x48
+#define BT_CONTROL_KEY_FAST_FORWARD            0x49
+#define BT_CONTROL_KEY_EJECT                   0x4A
+#define BT_CONTROL_KEY_FORWARD                 0x4B
+#define BT_CONTROL_KEY_BACKWARD                        0x4C
+
+struct bt_control_req {
+       bt_audio_msg_header_t   h;
+       uint8_t                 mode;           /* Control Mode */
+       uint8_t                 key;            /* Control Key */
+} __attribute__ ((packed));
+
+struct bt_control_rsp {
+       bt_audio_msg_header_t   h;
+       uint8_t                 mode;           /* Control Mode */
+       uint8_t                 key;            /* Control Key */
+} __attribute__ ((packed));
+
+struct bt_control_ind {
+       bt_audio_msg_header_t   h;
+       uint8_t                 mode;           /* Control Mode */
+       uint8_t                 key;            /* Control Key */
+} __attribute__ ((packed));
+
+struct bt_delay_report_req {
+       bt_audio_msg_header_t   h;
+       uint16_t                delay;
+} __attribute__ ((packed));
+
+struct bt_delay_report_ind {
+       bt_audio_msg_header_t   h;
+       uint16_t                delay;
+} __attribute__ ((packed));
+
+/* Function declaration */
+
+/* Opens a connection to the audio service: return a socket descriptor */
+int bt_audio_service_open(void);
+
+/* Closes a connection to the audio service */
+int bt_audio_service_close(int sk);
+
+/* Receives stream data file descriptor : must be called after a
+BT_STREAMFD_IND message is returned */
+int bt_audio_service_get_data_fd(int sk);
+
+/* Human readable message type string */
+const char *bt_audio_strtype(uint8_t type);
+
+/* Human readable message name string */
+const char *bt_audio_strname(uint8_t name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BT_AUDIOCLIENT_H */
diff --git a/audio/main.c b/audio/main.c
new file mode 100644 (file)
index 0000000..5c751af
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "glib-helper.h"
+#include "btio.h"
+#include "plugin.h"
+#include "log.h"
+#include "device.h"
+#include "headset.h"
+#include "manager.h"
+#include "gateway.h"
+
+static GIOChannel *sco_server = NULL;
+
+static GKeyFile *load_config_file(const char *file)
+{
+       GError *err = NULL;
+       GKeyFile *keyfile;
+
+       keyfile = g_key_file_new();
+
+       g_key_file_set_list_separator(keyfile, ',');
+
+       if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+               error("Parsing %s failed: %s", file, err->message);
+               g_error_free(err);
+               g_key_file_free(keyfile);
+               return NULL;
+       }
+
+       return keyfile;
+}
+
+static void sco_server_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+       int sk;
+       struct audio_device *device;
+       char addr[18];
+       bdaddr_t src, dst;
+
+       if (err) {
+               error("sco_server_cb: %s", err->message);
+               return;
+       }
+
+       bt_io_get(chan, BT_IO_SCO, &err,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_DEST, addr,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("bt_io_get: %s", err->message);
+               goto drop;
+       }
+
+       device = manager_find_device(NULL, &src, &dst, AUDIO_HEADSET_INTERFACE,
+                                       FALSE);
+       if (!device)
+               device = manager_find_device(NULL, &src, &dst,
+                                               AUDIO_GATEWAY_INTERFACE,
+                                               FALSE);
+
+       if (!device)
+               goto drop;
+
+       if (device->headset) {
+               if (headset_get_state(device) < HEADSET_STATE_CONNECTED) {
+                       DBG("Refusing SCO from non-connected headset");
+                       goto drop;
+               }
+
+               if (!headset_get_hfp_active(device)) {
+                       error("Refusing non-HFP SCO connect attempt from %s",
+                                                                       addr);
+                       goto drop;
+               }
+
+               if (headset_connect_sco(device, chan) < 0)
+                       goto drop;
+
+               headset_set_state(device, HEADSET_STATE_PLAYING);
+       } else if (device->gateway) {
+               if (!gateway_is_connected(device)) {
+                       DBG("Refusing SCO from non-connected AG");
+                       goto drop;
+               }
+
+               if (gateway_connect_sco(device, chan) < 0)
+                       goto drop;
+       } else
+               goto drop;
+
+       sk = g_io_channel_unix_get_fd(chan);
+       fcntl(sk, F_SETFL, 0);
+
+       DBG("Accepted SCO connection from %s", addr);
+
+       return;
+
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static DBusConnection *connection;
+
+static int audio_init(void)
+{
+       GKeyFile *config;
+       gboolean enable_sco;
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -EIO;
+
+       config = load_config_file(CONFIGDIR "/audio.conf");
+
+       if (audio_manager_init(connection, config, &enable_sco) < 0)
+               goto failed;
+
+       if (!enable_sco)
+               return 0;
+
+       sco_server = bt_io_listen(BT_IO_SCO, sco_server_cb, NULL, NULL,
+                                       NULL, NULL,
+                                       BT_IO_OPT_INVALID);
+       if (!sco_server) {
+               error("Unable to start SCO server socket");
+               goto failed;
+       }
+
+       return 0;
+
+failed:
+       audio_manager_exit();
+
+       if (connection) {
+               dbus_connection_unref(connection);
+               connection = NULL;
+       }
+
+       return -EIO;
+}
+
+static void audio_exit(void)
+{
+       if (sco_server) {
+               g_io_channel_shutdown(sco_server, TRUE, NULL);
+               g_io_channel_unref(sco_server);
+               sco_server = NULL;
+       }
+
+       audio_manager_exit();
+
+       dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(audio, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, audio_init, audio_exit)
diff --git a/audio/manager.c b/audio/manager.c
new file mode 100644 (file)
index 0000000..d442d1d
--- /dev/null
@@ -0,0 +1,1456 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "glib-helper.h"
+#include "btio.h"
+#include "../src/adapter.h"
+#include "../src/manager.h"
+#include "../src/device.h"
+
+#include "log.h"
+#include "ipc.h"
+#include "device.h"
+#include "error.h"
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+#include "headset.h"
+#include "gateway.h"
+#include "sink.h"
+#include "source.h"
+#include "avrcp.h"
+#include "control.h"
+#include "manager.h"
+#include "sdpd.h"
+#include "telephony.h"
+#include "unix.h"
+
+typedef enum {
+       HEADSET = 1 << 0,
+       GATEWAY = 1 << 1,
+       SINK    = 1 << 2,
+       SOURCE  = 1 << 3,
+       CONTROL = 1 << 4,
+       TARGET  = 1 << 5,
+       INVALID = 1 << 6
+} audio_service_type;
+
+typedef enum {
+               GENERIC_AUDIO = 0,
+               ADVANCED_AUDIO,
+               AV_REMOTE,
+               GET_RECORDS
+} audio_sdp_state_t;
+
+struct audio_adapter {
+       struct btd_adapter *btd_adapter;
+       gboolean powered;
+       uint32_t hsp_ag_record_id;
+       uint32_t hfp_ag_record_id;
+       uint32_t hfp_hs_record_id;
+       GIOChannel *hsp_ag_server;
+       GIOChannel *hfp_ag_server;
+       GIOChannel *hfp_hs_server;
+       gint ref;
+};
+
+static gboolean auto_connect = TRUE;
+static int max_connected_headsets = 1;
+static DBusConnection *connection = NULL;
+static GKeyFile *config = NULL;
+static GSList *adapters = NULL;
+static GSList *devices = NULL;
+
+static struct enabled_interfaces enabled = {
+       .hfp            = TRUE,
+       .headset        = TRUE,
+       .gateway        = FALSE,
+       .sink           = TRUE,
+       .source         = FALSE,
+       .control        = TRUE,
+       .socket         = FALSE,
+       .media          = TRUE,
+};
+
+static struct audio_adapter *find_adapter(GSList *list,
+                                       struct btd_adapter *btd_adapter)
+{
+       for (; list; list = list->next) {
+               struct audio_adapter *adapter = list->data;
+
+               if (adapter->btd_adapter == btd_adapter)
+                       return adapter;
+       }
+
+       return NULL;
+}
+
+gboolean server_is_enabled(bdaddr_t *src, uint16_t svc)
+{
+       switch (svc) {
+       case HEADSET_SVCLASS_ID:
+               return enabled.headset;
+       case HEADSET_AGW_SVCLASS_ID:
+               return FALSE;
+       case HANDSFREE_SVCLASS_ID:
+               return enabled.headset && enabled.hfp;
+       case HANDSFREE_AGW_SVCLASS_ID:
+               return enabled.gateway;
+       case AUDIO_SINK_SVCLASS_ID:
+               return enabled.sink;
+       case AUDIO_SOURCE_SVCLASS_ID:
+               return enabled.source;
+       case AV_REMOTE_TARGET_SVCLASS_ID:
+       case AV_REMOTE_SVCLASS_ID:
+               return enabled.control;
+       default:
+               return FALSE;
+       }
+}
+
+static void handle_uuid(const char *uuidstr, struct audio_device *device)
+{
+       uuid_t uuid;
+       uint16_t uuid16;
+
+       if (bt_string2uuid(&uuid, uuidstr) < 0) {
+               error("%s not detected as an UUID-128", uuidstr);
+               return;
+       }
+
+       if (!sdp_uuid128_to_uuid(&uuid) && uuid.type != SDP_UUID16) {
+               error("Could not convert %s to a UUID-16", uuidstr);
+               return;
+       }
+
+       uuid16 = uuid.value.uuid16;
+
+       if (!server_is_enabled(&device->src, uuid16)) {
+               DBG("server not enabled for %s (0x%04x)", uuidstr, uuid16);
+               return;
+       }
+
+       switch (uuid16) {
+       case HEADSET_SVCLASS_ID:
+               DBG("Found Headset record");
+               if (device->headset)
+                       headset_update(device, uuid16, uuidstr);
+               else
+                       device->headset = headset_init(device, uuid16,
+                                                       uuidstr);
+               break;
+       case HEADSET_AGW_SVCLASS_ID:
+               DBG("Found Headset AG record");
+               break;
+       case HANDSFREE_SVCLASS_ID:
+               DBG("Found Handsfree record");
+               if (device->headset)
+                       headset_update(device, uuid16, uuidstr);
+               else
+                       device->headset = headset_init(device, uuid16,
+                                                               uuidstr);
+               break;
+       case HANDSFREE_AGW_SVCLASS_ID:
+               DBG("Found Handsfree AG record");
+               if (enabled.gateway && (device->gateway == NULL))
+                       device->gateway = gateway_init(device);
+               break;
+       case AUDIO_SINK_SVCLASS_ID:
+               DBG("Found Audio Sink");
+               if (device->sink == NULL)
+                       device->sink = sink_init(device);
+               break;
+       case AUDIO_SOURCE_SVCLASS_ID:
+               DBG("Found Audio Source");
+               if (device->source == NULL)
+                       device->source = source_init(device);
+               break;
+       case AV_REMOTE_SVCLASS_ID:
+       case AV_REMOTE_TARGET_SVCLASS_ID:
+               DBG("Found AV %s", uuid16 == AV_REMOTE_SVCLASS_ID ?
+                                                       "Remote" : "Target");
+               if (device->control)
+                       control_update(device->control, uuid16);
+               else
+                       device->control = control_init(device, uuid16);
+
+               if (device->sink && sink_is_active(device))
+                       avrcp_connect(device);
+               break;
+       default:
+               DBG("Unrecognized UUID: 0x%04X", uuid16);
+               break;
+       }
+}
+
+static sdp_record_t *hsp_ag_record(uint8_t ch)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+       uuid_t l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_record_t *record;
+       sdp_list_t *aproto, *proto[2];
+       sdp_data_t *channel;
+
+       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);
+
+       sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &svclass_uuid);
+       sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+       sdp_set_service_classes(record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+       profile.version = 0x0102;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &ch);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_set_info_attr(record, "Headset Audio Gateway", 0, 0);
+
+       sdp_data_free(channel);
+       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 sdp_record_t *hfp_hs_record(uint8_t ch)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+       uuid_t l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_record_t *record;
+       sdp_list_t *aproto, *proto[2];
+       sdp_data_t *channel;
+
+       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);
+
+       sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &svclass_uuid);
+       sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+       sdp_set_service_classes(record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+       profile.version = 0x0105;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &ch);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_set_info_attr(record, "Hands-Free", 0, 0);
+
+       sdp_data_free(channel);
+       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 sdp_record_t *hfp_ag_record(uint8_t ch, uint32_t feat)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+       uuid_t l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t *record;
+       sdp_data_t *channel, *features;
+       uint8_t netid = 0x01;
+       uint16_t sdpfeat;
+       sdp_data_t *network;
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       network = sdp_data_alloc(SDP_UINT8, &netid);
+       if (!network) {
+               sdp_record_free(record);
+               return NULL;
+       }
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(record, root);
+
+       sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &svclass_uuid);
+       sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+       sdp_set_service_classes(record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+       profile.version = 0x0105;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &ch);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       sdpfeat = (uint16_t) feat & 0xF;
+       features = sdp_data_alloc(SDP_UINT16, &sdpfeat);
+       sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_set_info_attr(record, "Hands-Free Audio Gateway", 0, 0);
+
+       sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+       sdp_data_free(channel);
+       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 void headset_auth_cb(DBusError *derr, void *user_data)
+{
+       struct audio_device *device = user_data;
+       GError *err = NULL;
+       GIOChannel *io;
+
+       if (device->hs_preauth_id) {
+               g_source_remove(device->hs_preauth_id);
+               device->hs_preauth_id = 0;
+       }
+
+       if (derr && dbus_error_is_set(derr)) {
+               error("Access denied: %s", derr->message);
+               headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+               return;
+       }
+
+       io = headset_get_rfcomm(device);
+
+       if (!bt_io_accept(io, headset_connect_cb, device, NULL, &err)) {
+               error("bt_io_accept: %s", err->message);
+               g_error_free(err);
+               headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+               return;
+       }
+}
+
+static gboolean hs_preauth_cb(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct audio_device *device = user_data;
+
+       DBG("Headset disconnected during authorization");
+
+       audio_device_cancel_authorization(device, headset_auth_cb, device);
+
+       headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+
+       device->hs_preauth_id = 0;
+
+       return FALSE;
+}
+
+static void ag_confirm(GIOChannel *chan, gpointer data)
+{
+       const char *server_uuid, *remote_uuid;
+       struct audio_device *device;
+       gboolean hfp_active;
+       bdaddr_t src, dst;
+       int perr;
+       GError *err = NULL;
+       uint8_t ch;
+
+       bt_io_get(chan, BT_IO_RFCOMM, &err,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_CHANNEL, &ch,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               goto drop;
+       }
+
+       if (ch == DEFAULT_HS_AG_CHANNEL) {
+               hfp_active = FALSE;
+               server_uuid = HSP_AG_UUID;
+               remote_uuid = HSP_HS_UUID;
+       } else {
+               hfp_active = TRUE;
+               server_uuid = HFP_AG_UUID;
+               remote_uuid = HFP_HS_UUID;
+       }
+
+       device = manager_get_device(&src, &dst, TRUE);
+       if (!device)
+               goto drop;
+
+       if (!manager_allow_headset_connection(device)) {
+               DBG("Refusing headset: too many existing connections");
+               goto drop;
+       }
+
+       if (!device->headset) {
+               btd_device_add_uuid(device->btd_dev, remote_uuid);
+               if (!device->headset)
+                       goto drop;
+       }
+
+       if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) {
+               DBG("Refusing new connection since one already exists");
+               goto drop;
+       }
+
+       headset_set_hfp_active(device, hfp_active);
+       headset_set_rfcomm_initiator(device, TRUE);
+
+       if (headset_connect_rfcomm(device, chan) < 0) {
+               error("headset_connect_rfcomm failed");
+               goto drop;
+       }
+
+       headset_set_state(device, HEADSET_STATE_CONNECTING);
+
+       perr = audio_device_request_authorization(device, server_uuid,
+                                               headset_auth_cb, device);
+       if (perr < 0) {
+               DBG("Authorization denied: %s", strerror(-perr));
+               headset_set_state(device, HEADSET_STATE_DISCONNECTED);
+               return;
+       }
+
+       device->hs_preauth_id = g_io_add_watch(chan,
+                                       G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                       hs_preauth_cb, device);
+
+       device->auto_connect = auto_connect;
+
+       return;
+
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void gateway_auth_cb(DBusError *derr, void *user_data)
+{
+       struct audio_device *device = user_data;
+
+       if (derr && dbus_error_is_set(derr)) {
+               error("Access denied: %s", derr->message);
+               gateway_set_state(device, GATEWAY_STATE_DISCONNECTED);
+       } else {
+               char ag_address[18];
+
+               ba2str(&device->dst, ag_address);
+               DBG("Accepted AG connection from %s for %s",
+                       ag_address, device->path);
+
+               gateway_start_service(device);
+       }
+}
+
+static void hf_io_cb(GIOChannel *chan, gpointer data)
+{
+       bdaddr_t src, dst;
+       GError *err = NULL;
+       uint8_t ch;
+       const char *server_uuid, *remote_uuid;
+       struct audio_device *device;
+       int perr;
+
+       bt_io_get(chan, BT_IO_RFCOMM, &err,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_CHANNEL, &ch,
+                       BT_IO_OPT_INVALID);
+
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               return;
+       }
+
+       server_uuid = HFP_HS_UUID;
+       remote_uuid = HFP_AG_UUID;
+
+       device = manager_get_device(&src, &dst, TRUE);
+       if (!device)
+               goto drop;
+
+       if (!device->gateway) {
+               btd_device_add_uuid(device->btd_dev, remote_uuid);
+               if (!device->gateway)
+                       goto drop;
+       }
+
+       if (gateway_is_active(device)) {
+               DBG("Refusing new connection since one already exists");
+               goto drop;
+       }
+
+       if (gateway_connect_rfcomm(device, chan) < 0) {
+               error("Allocating new GIOChannel failed!");
+               goto drop;
+       }
+
+       perr = audio_device_request_authorization(device, server_uuid,
+                                               gateway_auth_cb, device);
+       if (perr < 0) {
+               DBG("Authorization denied: %s", strerror(-perr));
+               gateway_set_state(device, GATEWAY_STATE_DISCONNECTED);
+       }
+
+       return;
+
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static int headset_server_init(struct audio_adapter *adapter)
+{
+       uint8_t chan = DEFAULT_HS_AG_CHANNEL;
+       sdp_record_t *record;
+       gboolean master = TRUE;
+       GError *err = NULL;
+       uint32_t features;
+       GIOChannel *io;
+       bdaddr_t src;
+
+       if (config) {
+               gboolean tmp;
+
+               tmp = g_key_file_get_boolean(config, "General", "Master",
+                                               &err);
+               if (err) {
+                       DBG("audio.conf: %s", err->message);
+                       g_clear_error(&err);
+               } else
+                       master = tmp;
+       }
+
+       adapter_get_address(adapter->btd_adapter, &src);
+
+       io =  bt_io_listen(BT_IO_RFCOMM, NULL, ag_confirm, adapter, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &src,
+                               BT_IO_OPT_CHANNEL, chan,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_MASTER, master,
+                               BT_IO_OPT_INVALID);
+       if (!io)
+               goto failed;
+
+       adapter->hsp_ag_server = io;
+
+       record = hsp_ag_record(chan);
+       if (!record) {
+               error("Unable to allocate new service record");
+               goto failed;
+       }
+
+       if (add_record_to_server(&src, record) < 0) {
+               error("Unable to register HS AG service record");
+               sdp_record_free(record);
+               goto failed;
+       }
+       adapter->hsp_ag_record_id = record->handle;
+
+       features = headset_config_init(config);
+
+       if (!enabled.hfp)
+               return 0;
+
+       chan = DEFAULT_HF_AG_CHANNEL;
+
+       io = bt_io_listen(BT_IO_RFCOMM, NULL, ag_confirm, adapter, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &src,
+                               BT_IO_OPT_CHANNEL, chan,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_MASTER, master,
+                               BT_IO_OPT_INVALID);
+       if (!io)
+               goto failed;
+
+       adapter->hfp_ag_server = io;
+
+       record = hfp_ag_record(chan, features);
+       if (!record) {
+               error("Unable to allocate new service record");
+               goto failed;
+       }
+
+       if (add_record_to_server(&src, record) < 0) {
+               error("Unable to register HF AG service record");
+               sdp_record_free(record);
+               goto failed;
+       }
+       adapter->hfp_ag_record_id = record->handle;
+
+       return 0;
+
+failed:
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+       }
+
+       if (adapter->hsp_ag_server) {
+               g_io_channel_shutdown(adapter->hsp_ag_server, TRUE, NULL);
+               g_io_channel_unref(adapter->hsp_ag_server);
+               adapter->hsp_ag_server = NULL;
+       }
+
+       if (adapter->hfp_ag_server) {
+               g_io_channel_shutdown(adapter->hfp_ag_server, TRUE, NULL);
+               g_io_channel_unref(adapter->hfp_ag_server);
+               adapter->hfp_ag_server = NULL;
+       }
+
+       return -1;
+}
+
+static int gateway_server_init(struct audio_adapter *adapter)
+{
+       uint8_t chan = DEFAULT_HFP_HS_CHANNEL;
+       sdp_record_t *record;
+       gboolean master = TRUE;
+       GError *err = NULL;
+       GIOChannel *io;
+       bdaddr_t src;
+
+       if (config) {
+               gboolean tmp;
+
+               tmp = g_key_file_get_boolean(config, "General", "Master",
+                                               &err);
+               if (err) {
+                       DBG("audio.conf: %s", err->message);
+                       g_clear_error(&err);
+               } else
+                       master = tmp;
+       }
+
+       adapter_get_address(adapter->btd_adapter, &src);
+
+       io = bt_io_listen(BT_IO_RFCOMM, NULL, hf_io_cb, adapter, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &src,
+                               BT_IO_OPT_CHANNEL, chan,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_MASTER, master,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("%s", err->message);
+               g_error_free(err);
+               return -1;
+       }
+
+       adapter->hfp_hs_server = io;
+       record = hfp_hs_record(chan);
+       if (!record) {
+               error("Unable to allocate new service record");
+               goto failed;
+       }
+
+       if (add_record_to_server(&src, record) < 0) {
+               error("Unable to register HFP HS service record");
+               sdp_record_free(record);
+               goto failed;
+       }
+
+       adapter->hfp_hs_record_id = record->handle;
+
+       return 0;
+
+failed:
+       g_io_channel_shutdown(adapter->hfp_hs_server, TRUE, NULL);
+       g_io_channel_unref(adapter->hfp_hs_server);
+       adapter->hfp_hs_server = NULL;
+       return -1;
+}
+
+static int audio_probe(struct btd_device *device, GSList *uuids)
+{
+       struct btd_adapter *adapter = device_get_adapter(device);
+       bdaddr_t src, dst;
+       struct audio_device *audio_dev;
+
+       adapter_get_address(adapter, &src);
+       device_get_address(device, &dst, NULL);
+
+       audio_dev = manager_get_device(&src, &dst, TRUE);
+       if (!audio_dev) {
+               DBG("unable to get a device object");
+               return -1;
+       }
+
+       g_slist_foreach(uuids, (GFunc) handle_uuid, audio_dev);
+
+       return 0;
+}
+
+static void audio_remove(struct btd_device *device)
+{
+       struct audio_device *dev;
+       const char *path;
+
+       path = device_get_path(device);
+
+       dev = manager_find_device(path, NULL, NULL, NULL, FALSE);
+       if (!dev)
+               return;
+
+       devices = g_slist_remove(devices, dev);
+
+       audio_device_unregister(dev);
+
+}
+
+static struct audio_adapter *audio_adapter_ref(struct audio_adapter *adp)
+{
+       adp->ref++;
+
+       DBG("%p: ref=%d", adp, adp->ref);
+
+       return adp;
+}
+
+static void audio_adapter_unref(struct audio_adapter *adp)
+{
+       adp->ref--;
+
+       DBG("%p: ref=%d", adp, adp->ref);
+
+       if (adp->ref > 0)
+               return;
+
+       adapters = g_slist_remove(adapters, adp);
+       btd_adapter_unref(adp->btd_adapter);
+       g_free(adp);
+}
+
+static struct audio_adapter *audio_adapter_create(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+
+       adp = g_new0(struct audio_adapter, 1);
+       adp->btd_adapter = btd_adapter_ref(adapter);
+
+       return audio_adapter_ref(adp);
+}
+
+static struct audio_adapter *audio_adapter_get(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+
+       adp = find_adapter(adapters, adapter);
+       if (!adp) {
+               adp = audio_adapter_create(adapter);
+               adapters = g_slist_append(adapters, adp);
+       } else
+               audio_adapter_ref(adp);
+
+       return adp;
+}
+
+static void state_changed(struct btd_adapter *adapter, gboolean powered)
+{
+       struct audio_adapter *adp;
+       static gboolean telephony = FALSE;
+       GSList *l;
+
+       DBG("%s powered %s", adapter_get_path(adapter),
+                                               powered ? "on" : "off");
+
+       /* ignore powered change, adapter is powering down */
+       if (powered && adapter_powering_down(adapter))
+               return;
+
+       adp = find_adapter(adapters, adapter);
+       if (!adp)
+               return;
+
+       adp->powered = powered;
+
+       if (powered) {
+               /* telephony driver already initialized*/
+               if (telephony == TRUE)
+                       return;
+               telephony_init();
+               telephony = TRUE;
+               return;
+       }
+
+       /* telephony not initialized just ignore power down */
+       if (telephony == FALSE)
+               return;
+
+       for (l = adapters; l; l = l->next) {
+               adp = l->data;
+
+               if (adp->powered == TRUE)
+                       return;
+       }
+
+       telephony_exit();
+       telephony = FALSE;
+}
+
+static int headset_server_probe(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+       const gchar *path = adapter_get_path(adapter);
+       int err;
+
+       DBG("path %s", path);
+
+       adp = audio_adapter_get(adapter);
+       if (!adp)
+               return -EINVAL;
+
+       err = headset_server_init(adp);
+       if (err < 0) {
+               audio_adapter_unref(adp);
+               return err;
+       }
+
+       btd_adapter_register_powered_callback(adapter, state_changed);
+
+       return 0;
+}
+
+static void headset_server_remove(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+       const gchar *path = adapter_get_path(adapter);
+
+       DBG("path %s", path);
+
+       btd_adapter_unregister_powered_callback(adapter, state_changed);
+
+       adp = find_adapter(adapters, adapter);
+       if (!adp)
+               return;
+
+       if (adp->hsp_ag_record_id) {
+               remove_record_from_server(adp->hsp_ag_record_id);
+               adp->hsp_ag_record_id = 0;
+       }
+
+       if (adp->hsp_ag_server) {
+               g_io_channel_shutdown(adp->hsp_ag_server, TRUE, NULL);
+               g_io_channel_unref(adp->hsp_ag_server);
+               adp->hsp_ag_server = NULL;
+       }
+
+       if (adp->hfp_ag_record_id) {
+               remove_record_from_server(adp->hfp_ag_record_id);
+               adp->hfp_ag_record_id = 0;
+       }
+
+       if (adp->hfp_ag_server) {
+               g_io_channel_shutdown(adp->hfp_ag_server, TRUE, NULL);
+               g_io_channel_unref(adp->hfp_ag_server);
+               adp->hfp_ag_server = NULL;
+       }
+
+       audio_adapter_unref(adp);
+}
+
+static int gateway_server_probe(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+       int err;
+
+       adp = audio_adapter_get(adapter);
+       if (!adp)
+               return -EINVAL;
+
+       err = gateway_server_init(adp);
+       if (err < 0)
+               audio_adapter_unref(adp);
+
+       return err;
+}
+
+static void gateway_server_remove(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+       const gchar *path = adapter_get_path(adapter);
+
+       DBG("path %s", path);
+
+       adp = find_adapter(adapters, adapter);
+       if (!adp)
+               return;
+
+       if (adp->hfp_hs_record_id) {
+               remove_record_from_server(adp->hfp_hs_record_id);
+               adp->hfp_hs_record_id = 0;
+       }
+
+       if (adp->hfp_hs_server) {
+               g_io_channel_shutdown(adp->hfp_hs_server, TRUE, NULL);
+               g_io_channel_unref(adp->hfp_hs_server);
+               adp->hfp_hs_server = NULL;
+       }
+
+       audio_adapter_unref(adp);
+}
+
+static int a2dp_server_probe(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+       const gchar *path = adapter_get_path(adapter);
+       bdaddr_t src;
+       int err;
+
+       DBG("path %s", path);
+
+       adp = audio_adapter_get(adapter);
+       if (!adp)
+               return -EINVAL;
+
+       adapter_get_address(adapter, &src);
+
+       err = a2dp_register(connection, &src, config);
+       if (err < 0)
+               audio_adapter_unref(adp);
+
+       return err;
+}
+
+static void a2dp_server_remove(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+       const gchar *path = adapter_get_path(adapter);
+       bdaddr_t src;
+
+       DBG("path %s", path);
+
+       adp = find_adapter(adapters, adapter);
+       if (!adp)
+               return;
+
+       adapter_get_address(adapter, &src);
+       a2dp_unregister(&src);
+       audio_adapter_unref(adp);
+}
+
+static int avrcp_server_probe(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+       const gchar *path = adapter_get_path(adapter);
+       bdaddr_t src;
+       int err;
+
+       DBG("path %s", path);
+
+       adp = audio_adapter_get(adapter);
+       if (!adp)
+               return -EINVAL;
+
+       adapter_get_address(adapter, &src);
+
+       err = avrcp_register(connection, &src, config);
+       if (err < 0)
+               audio_adapter_unref(adp);
+
+       return err;
+}
+
+static void avrcp_server_remove(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+       const gchar *path = adapter_get_path(adapter);
+       bdaddr_t src;
+
+       DBG("path %s", path);
+
+       adp = find_adapter(adapters, adapter);
+       if (!adp)
+               return;
+
+       adapter_get_address(adapter, &src);
+       avrcp_unregister(&src);
+       audio_adapter_unref(adp);
+}
+
+static int media_server_probe(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+       const gchar *path = adapter_get_path(adapter);
+       bdaddr_t src;
+       int err;
+
+       DBG("path %s", path);
+
+       adp = audio_adapter_get(adapter);
+       if (!adp)
+               return -EINVAL;
+
+       adapter_get_address(adapter, &src);
+
+       err = media_register(connection, path, &src);
+       if (err < 0)
+               audio_adapter_unref(adp);
+
+       return err;
+}
+
+static void media_server_remove(struct btd_adapter *adapter)
+{
+       struct audio_adapter *adp;
+       const gchar *path = adapter_get_path(adapter);
+
+       DBG("path %s", path);
+
+       adp = find_adapter(adapters, adapter);
+       if (!adp)
+               return;
+
+       media_unregister(path);
+       audio_adapter_unref(adp);
+}
+
+static struct btd_device_driver audio_driver = {
+       .name   = "audio",
+       .uuids  = BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID, HSP_AG_UUID, HFP_AG_UUID,
+                       ADVANCED_AUDIO_UUID, A2DP_SOURCE_UUID, A2DP_SINK_UUID,
+                       AVRCP_TARGET_UUID, AVRCP_REMOTE_UUID),
+       .probe  = audio_probe,
+       .remove = audio_remove,
+};
+
+static struct btd_adapter_driver headset_server_driver = {
+       .name   = "audio-headset",
+       .probe  = headset_server_probe,
+       .remove = headset_server_remove,
+};
+
+static struct btd_adapter_driver gateway_server_driver = {
+       .name   = "audio-gateway",
+       .probe  = gateway_server_probe,
+       .remove = gateway_server_remove,
+};
+
+static struct btd_adapter_driver a2dp_server_driver = {
+       .name   = "audio-a2dp",
+       .probe  = a2dp_server_probe,
+       .remove = a2dp_server_remove,
+};
+
+static struct btd_adapter_driver avrcp_server_driver = {
+       .name   = "audio-control",
+       .probe  = avrcp_server_probe,
+       .remove = avrcp_server_remove,
+};
+
+static struct btd_adapter_driver media_server_driver = {
+       .name   = "media",
+       .probe  = media_server_probe,
+       .remove = media_server_remove,
+};
+
+int audio_manager_init(DBusConnection *conn, GKeyFile *conf,
+                                                       gboolean *enable_sco)
+{
+       char **list;
+       int i;
+       gboolean b;
+       GError *err = NULL;
+
+       connection = dbus_connection_ref(conn);
+
+       if (!conf)
+               goto proceed;
+
+       config = conf;
+
+       list = g_key_file_get_string_list(config, "General", "Enable",
+                                               NULL, NULL);
+       for (i = 0; list && list[i] != NULL; i++) {
+               if (g_str_equal(list[i], "Headset"))
+                       enabled.headset = TRUE;
+               else if (g_str_equal(list[i], "Gateway"))
+                       enabled.gateway = TRUE;
+               else if (g_str_equal(list[i], "Sink"))
+                       enabled.sink = TRUE;
+               else if (g_str_equal(list[i], "Source"))
+                       enabled.source = TRUE;
+               else if (g_str_equal(list[i], "Control"))
+                       enabled.control = TRUE;
+               else if (g_str_equal(list[i], "Socket"))
+                       enabled.socket = TRUE;
+               else if (g_str_equal(list[i], "Media"))
+                       enabled.media = TRUE;
+
+       }
+       g_strfreev(list);
+
+       list = g_key_file_get_string_list(config, "General", "Disable",
+                                               NULL, NULL);
+       for (i = 0; list && list[i] != NULL; i++) {
+               if (g_str_equal(list[i], "Headset"))
+                       enabled.headset = FALSE;
+               else if (g_str_equal(list[i], "Gateway"))
+                       enabled.gateway = FALSE;
+               else if (g_str_equal(list[i], "Sink"))
+                       enabled.sink = FALSE;
+               else if (g_str_equal(list[i], "Source"))
+                       enabled.source = FALSE;
+               else if (g_str_equal(list[i], "Control"))
+                       enabled.control = FALSE;
+               else if (g_str_equal(list[i], "Socket"))
+                       enabled.socket = FALSE;
+               else if (g_str_equal(list[i], "Media"))
+                       enabled.media = FALSE;
+       }
+       g_strfreev(list);
+
+       b = g_key_file_get_boolean(config, "General", "AutoConnect", &err);
+       if (err) {
+               DBG("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else
+               auto_connect = b;
+
+       b = g_key_file_get_boolean(config, "Headset", "HFP",
+                                       &err);
+       if (err)
+               g_clear_error(&err);
+       else
+               enabled.hfp = b;
+
+       err = NULL;
+       i = g_key_file_get_integer(config, "Headset", "MaxConnected",
+                                       &err);
+       if (err) {
+               DBG("audio.conf: %s", err->message);
+               g_clear_error(&err);
+       } else
+               max_connected_headsets = i;
+
+proceed:
+       if (enabled.socket)
+               unix_init();
+
+       if (enabled.media)
+               btd_register_adapter_driver(&media_server_driver);
+
+       if (enabled.headset)
+               btd_register_adapter_driver(&headset_server_driver);
+
+       if (enabled.gateway)
+               btd_register_adapter_driver(&gateway_server_driver);
+
+       if (enabled.source || enabled.sink)
+               btd_register_adapter_driver(&a2dp_server_driver);
+
+       if (enabled.control)
+               btd_register_adapter_driver(&avrcp_server_driver);
+
+       btd_register_device_driver(&audio_driver);
+
+       *enable_sco = (enabled.gateway || enabled.headset);
+
+       return 0;
+}
+
+void audio_manager_exit(void)
+{
+       /* Bail out early if we haven't been initialized */
+       if (connection == NULL)
+               return;
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+
+       if (config) {
+               g_key_file_free(config);
+               config = NULL;
+       }
+
+       if (enabled.socket)
+               unix_exit();
+
+       if (enabled.media)
+               btd_unregister_adapter_driver(&media_server_driver);
+
+       if (enabled.headset)
+               btd_unregister_adapter_driver(&headset_server_driver);
+
+       if (enabled.gateway)
+               btd_unregister_adapter_driver(&gateway_server_driver);
+
+       if (enabled.source || enabled.sink)
+               btd_unregister_adapter_driver(&a2dp_server_driver);
+
+       if (enabled.control)
+               btd_unregister_adapter_driver(&avrcp_server_driver);
+
+       btd_unregister_device_driver(&audio_driver);
+}
+
+GSList *manager_find_devices(const char *path,
+                                       const bdaddr_t *src,
+                                       const bdaddr_t *dst,
+                                       const char *interface,
+                                       gboolean connected)
+{
+       GSList *result = NULL;
+       GSList *l;
+
+       for (l = devices; l != NULL; l = l->next) {
+               struct audio_device *dev = l->data;
+
+               if ((path && (strcmp(path, "")) && strcmp(dev->path, path)))
+                       continue;
+
+               if ((src && bacmp(src, BDADDR_ANY)) && bacmp(&dev->src, src))
+                       continue;
+
+               if ((dst && bacmp(dst, BDADDR_ANY)) && bacmp(&dev->dst, dst))
+                       continue;
+
+               if (interface && !strcmp(AUDIO_HEADSET_INTERFACE, interface)
+                               && !dev->headset)
+                       continue;
+
+               if (interface && !strcmp(AUDIO_GATEWAY_INTERFACE, interface)
+                               && !dev->gateway)
+                       continue;
+
+               if (interface && !strcmp(AUDIO_SINK_INTERFACE, interface)
+                               && !dev->sink)
+                       continue;
+
+               if (interface && !strcmp(AUDIO_SOURCE_INTERFACE, interface)
+                               && !dev->source)
+                       continue;
+
+               if (interface && !strcmp(AUDIO_CONTROL_INTERFACE, interface)
+                               && !dev->control)
+                       continue;
+
+               if (connected && !audio_device_is_active(dev, interface))
+                       continue;
+
+               result = g_slist_append(result, dev);
+       }
+
+       return result;
+}
+
+struct audio_device *manager_find_device(const char *path,
+                                       const bdaddr_t *src,
+                                       const bdaddr_t *dst,
+                                       const char *interface,
+                                       gboolean connected)
+{
+       struct audio_device *result;
+       GSList *l;
+
+       l = manager_find_devices(path, src, dst, interface, connected);
+       if (l == NULL)
+               return NULL;
+
+       result = l->data;
+       g_slist_free(l);
+       return result;
+}
+
+struct audio_device *manager_get_device(const bdaddr_t *src,
+                                       const bdaddr_t *dst,
+                                       gboolean create)
+{
+       struct audio_device *dev;
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       char addr[18];
+       const char *path;
+
+       dev = manager_find_device(NULL, src, dst, NULL, FALSE);
+       if (dev)
+               return dev;
+
+       if (!create)
+               return NULL;
+
+       ba2str(src, addr);
+
+       adapter = manager_find_adapter(src);
+       if (!adapter) {
+               error("Unable to get a btd_adapter object for %s",
+                               addr);
+               return NULL;
+       }
+
+       ba2str(dst, addr);
+
+       device = adapter_get_device(connection, adapter, addr);
+       if (!device) {
+               error("Unable to get btd_device object for %s", addr);
+               return NULL;
+       }
+
+       path = device_get_path(device);
+
+       dev = audio_device_register(connection, device, path, src, dst);
+       if (!dev)
+               return NULL;
+
+       devices = g_slist_append(devices, dev);
+
+       return dev;
+}
+
+gboolean manager_allow_headset_connection(struct audio_device *device)
+{
+       GSList *l;
+       int connected = 0;
+
+       for (l = devices; l != NULL; l = l->next) {
+               struct audio_device *dev = l->data;
+               struct headset *hs = dev->headset;
+
+               if (dev == device)
+                       continue;
+
+               if (device && bacmp(&dev->src, &device->src) != 0)
+                       continue;
+
+               if (!hs)
+                       continue;
+
+               if (headset_get_state(dev) > HEADSET_STATE_DISCONNECTED)
+                       connected++;
+
+               if (connected >= max_connected_headsets)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+void manager_set_fast_connectable(gboolean enable)
+{
+       GSList *l;
+
+       if (enable && !manager_allow_headset_connection(NULL)) {
+               DBG("Refusing enabling fast connectable");
+               return;
+       }
+
+       for (l = adapters; l != NULL; l = l->next) {
+               struct audio_adapter *adapter = l->data;
+
+               if (btd_adapter_set_fast_connectable(adapter->btd_adapter,
+                                                               enable))
+                       error("Changing fast connectable for hci%d failed",
+                               adapter_get_dev_id(adapter->btd_adapter));
+       }
+}
diff --git a/audio/manager.h b/audio/manager.h
new file mode 100644 (file)
index 0000000..f1d3021
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct enabled_interfaces {
+       gboolean hfp;
+       gboolean headset;
+       gboolean gateway;
+       gboolean sink;
+       gboolean source;
+       gboolean control;
+       gboolean socket;
+       gboolean media;
+       gboolean media_player;
+};
+
+int audio_manager_init(DBusConnection *conn, GKeyFile *config,
+                                                       gboolean *enable_sco);
+void audio_manager_exit(void);
+
+gboolean server_is_enabled(bdaddr_t *src, uint16_t svc);
+
+struct audio_device *manager_find_device(const char *path,
+                                       const bdaddr_t *src,
+                                       const bdaddr_t *dst,
+                                       const char *interface,
+                                       gboolean connected);
+
+GSList *manager_find_devices(const char *path,
+                                       const bdaddr_t *src,
+                                       const bdaddr_t *dst,
+                                       const char *interface,
+                                       gboolean connected);
+
+struct audio_device *manager_get_device(const bdaddr_t *src,
+                                       const bdaddr_t *dst,
+                                       gboolean create);
+
+gboolean manager_allow_headset_connection(struct audio_device *device);
+
+/* TRUE to enable fast connectable and FALSE to disable fast connectable for all
+ * audio adapters. */
+void manager_set_fast_connectable(gboolean enable);
diff --git a/audio/media.c b/audio/media.c
new file mode 100644 (file)
index 0000000..1956653
--- /dev/null
@@ -0,0 +1,1904 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/adapter.h"
+#include "../src/dbus-common.h"
+
+#include "glib-helper.h"
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "avrcp.h"
+#include "headset.h"
+#include "gateway.h"
+#include "manager.h"
+
+#define MEDIA_INTERFACE "org.bluez.Media"
+#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint"
+#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
+
+#define REQUEST_TIMEOUT (3 * 1000)             /* 3 seconds */
+
+struct media_adapter {
+       bdaddr_t                src;            /* Adapter address */
+       char                    *path;          /* Adapter path */
+       DBusConnection          *conn;          /* Adapter connection */
+       GSList                  *endpoints;     /* Endpoints list */
+       GSList                  *players;       /* Players list */
+};
+
+struct endpoint_request {
+       struct media_endpoint   *endpoint;
+       DBusMessage             *msg;
+       DBusPendingCall         *call;
+       media_endpoint_cb_t     cb;
+       GDestroyNotify          destroy;
+       void                    *user_data;
+};
+
+struct media_endpoint {
+       struct a2dp_sep         *sep;
+       char                    *sender;        /* Endpoint DBus bus id */
+       char                    *path;          /* Endpoint object path */
+       char                    *uuid;          /* Endpoint property UUID */
+       uint8_t                 codec;          /* Endpoint codec */
+       uint8_t                 *capabilities;  /* Endpoint property capabilities */
+       size_t                  size;           /* Endpoint capabilities size */
+       guint                   hs_watch;
+       guint                   ag_watch;
+       guint                   watch;
+       GSList                  *requests;
+       struct media_adapter    *adapter;
+       GSList                  *transports;
+};
+
+struct media_player {
+       struct media_adapter    *adapter;
+       struct avrcp_player     *player;
+       char                    *sender;        /* Player DBus bus id */
+       char                    *path;          /* Player object path */
+       GHashTable              *settings;      /* Player settings */
+       GHashTable              *track;         /* Player current track */
+       guint                   watch;
+       guint                   property_watch;
+       guint                   track_watch;
+       uint8_t                 status;
+       uint32_t                position;
+       uint8_t                 volume;
+       GTimer                  *timer;
+};
+
+struct metadata_value {
+       int                     type;
+       union {
+               char            *str;
+               uint32_t        num;
+       } value;
+};
+
+static GSList *adapters = NULL;
+
+static void endpoint_request_free(struct endpoint_request *request)
+{
+       if (request->call)
+               dbus_pending_call_unref(request->call);
+
+       if (request->destroy)
+               request->destroy(request->user_data);
+
+       dbus_message_unref(request->msg);
+       g_free(request);
+}
+
+static void media_endpoint_cancel(struct endpoint_request *request)
+{
+       struct media_endpoint *endpoint = request->endpoint;
+
+       if (request->call)
+               dbus_pending_call_cancel(request->call);
+
+       endpoint->requests = g_slist_remove(endpoint->requests, request);
+
+       endpoint_request_free(request);
+}
+
+static void media_endpoint_cancel_all(struct media_endpoint *endpoint)
+{
+       while (endpoint->requests != NULL)
+               media_endpoint_cancel(endpoint->requests->data);
+}
+
+static void media_endpoint_destroy(struct media_endpoint *endpoint)
+{
+       struct media_adapter *adapter = endpoint->adapter;
+
+       DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
+
+       if (endpoint->hs_watch)
+               headset_remove_state_cb(endpoint->hs_watch);
+
+       if (endpoint->ag_watch)
+               gateway_remove_state_cb(endpoint->ag_watch);
+
+       media_endpoint_cancel_all(endpoint);
+
+       g_slist_free_full(endpoint->transports,
+                               (GDestroyNotify) media_transport_destroy);
+
+       g_dbus_remove_watch(adapter->conn, endpoint->watch);
+       g_free(endpoint->capabilities);
+       g_free(endpoint->sender);
+       g_free(endpoint->path);
+       g_free(endpoint->uuid);
+       g_free(endpoint);
+}
+
+static void media_endpoint_remove(struct media_endpoint *endpoint)
+{
+       struct media_adapter *adapter = endpoint->adapter;
+
+       if (endpoint->sep) {
+               a2dp_remove_sep(endpoint->sep);
+               return;
+       }
+
+       info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
+                       endpoint->path);
+
+       adapter->endpoints = g_slist_remove(adapter->endpoints, endpoint);
+
+       media_endpoint_destroy(endpoint);
+}
+
+static void media_endpoint_exit(DBusConnection *connection, void *user_data)
+{
+       struct media_endpoint *endpoint = user_data;
+
+       endpoint->watch = 0;
+       media_endpoint_remove(endpoint);
+}
+
+static void headset_setconf_cb(struct media_endpoint *endpoint, void *ret,
+                                               int size, void *user_data)
+{
+       struct audio_device *dev = user_data;
+
+       if (ret != NULL)
+               return;
+
+       headset_shutdown(dev);
+}
+
+static void clear_configuration(struct media_endpoint *endpoint,
+                                       struct media_transport *transport)
+{
+       DBusConnection *conn;
+       DBusMessage *msg;
+       const char *path;
+
+       conn = endpoint->adapter->conn;
+
+       msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+                                               MEDIA_ENDPOINT_INTERFACE,
+                                               "ClearConfiguration");
+       if (msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               goto done;
+       }
+
+       path = media_transport_get_path(transport);
+       dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+       g_dbus_send_message(conn, msg);
+done:
+       endpoint->transports = g_slist_remove(endpoint->transports, transport);
+       media_transport_destroy(transport);
+}
+
+static void clear_endpoint(struct media_endpoint *endpoint)
+{
+       media_endpoint_cancel_all(endpoint);
+
+       while (endpoint->transports != NULL)
+               clear_configuration(endpoint, endpoint->transports->data);
+}
+
+static void endpoint_reply(DBusPendingCall *call, void *user_data)
+{
+       struct endpoint_request *request = user_data;
+       struct media_endpoint *endpoint = request->endpoint;
+       DBusMessage *reply;
+       DBusError err;
+       gboolean value;
+       void *ret = NULL;
+       int size = -1;
+
+       /* steal_reply will always return non-NULL since the callback
+        * is only called after a reply has been received */
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("Endpoint replied with an error: %s",
+                               err.name);
+
+               /* Clear endpoint configuration in case of NO_REPLY error */
+               if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+                       if (request->cb)
+                               request->cb(endpoint, NULL, size,
+                                                       request->user_data);
+                       clear_endpoint(endpoint);
+                       dbus_message_unref(reply);
+                       dbus_error_free(&err);
+                       return;
+               }
+
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE,
+                               "SelectConfiguration")) {
+               DBusMessageIter args, array;
+               uint8_t *configuration;
+
+               dbus_message_iter_init(reply, &args);
+
+               dbus_message_iter_recurse(&args, &array);
+
+               dbus_message_iter_get_fixed_array(&array, &configuration, &size);
+
+               ret = configuration;
+               goto done;
+       } else  if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) {
+               error("Wrong reply signature: %s", err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       size = 1;
+       value = TRUE;
+       ret = &value;
+
+done:
+       dbus_message_unref(reply);
+
+       if (request->cb)
+               request->cb(endpoint, ret, size, request->user_data);
+
+       endpoint->requests = g_slist_remove(endpoint->requests, request);
+       endpoint_request_free(request);
+}
+
+static gboolean media_endpoint_async_call(DBusConnection *conn,
+                                       DBusMessage *msg,
+                                       struct media_endpoint *endpoint,
+                                       media_endpoint_cb_t cb,
+                                       void *user_data,
+                                       GDestroyNotify destroy)
+{
+       struct endpoint_request *request;
+
+       request = g_new0(struct endpoint_request, 1);
+
+       /* Timeout should be less than avdtp request timeout (4 seconds) */
+       if (dbus_connection_send_with_reply(conn, msg, &request->call,
+                                               REQUEST_TIMEOUT) == FALSE) {
+               error("D-Bus send failed");
+               g_free(request);
+               return FALSE;
+       }
+
+       dbus_pending_call_set_notify(request->call, endpoint_reply, request,
+                                                                       NULL);
+
+       request->endpoint = endpoint;
+       request->msg = msg;
+       request->cb = cb;
+       request->destroy = destroy;
+       request->user_data = user_data;
+
+       endpoint->requests = g_slist_append(endpoint->requests, request);
+
+       DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
+                       dbus_message_get_destination(msg),
+                       dbus_message_get_path(msg));
+
+       return TRUE;
+}
+
+static gboolean select_configuration(struct media_endpoint *endpoint,
+                                               uint8_t *capabilities,
+                                               size_t length,
+                                               media_endpoint_cb_t cb,
+                                               void *user_data,
+                                               GDestroyNotify destroy)
+{
+       DBusConnection *conn;
+       DBusMessage *msg;
+
+       conn = endpoint->adapter->conn;
+
+       msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+                                               MEDIA_ENDPOINT_INTERFACE,
+                                               "SelectConfiguration");
+       if (msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return FALSE;
+       }
+
+       dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                                       &capabilities, length,
+                                       DBUS_TYPE_INVALID);
+
+       return media_endpoint_async_call(conn, msg, endpoint, cb, user_data,
+                                                               destroy);
+}
+
+static gint transport_device_cmp(gconstpointer data, gconstpointer user_data)
+{
+       struct media_transport *transport = (struct media_transport *) data;
+       const struct audio_device *device = user_data;
+
+       if (device == media_transport_get_dev(transport))
+               return 0;
+
+       return -1;
+}
+
+static struct media_transport *find_device_transport(
+                                       struct media_endpoint *endpoint,
+                                       struct audio_device *device)
+{
+       GSList *match;
+
+       match = g_slist_find_custom(endpoint->transports, device,
+                                                       transport_device_cmp);
+       if (match == NULL)
+               return NULL;
+
+       return match->data;
+}
+
+static gboolean set_configuration(struct media_endpoint *endpoint,
+                                       struct audio_device *device,
+                                       uint8_t *configuration, size_t size,
+                                       media_endpoint_cb_t cb,
+                                       void *user_data,
+                                       GDestroyNotify destroy)
+{
+       DBusConnection *conn;
+       DBusMessage *msg;
+       const char *path;
+       DBusMessageIter iter;
+       struct media_transport *transport;
+
+       transport = find_device_transport(endpoint, device);
+
+       if (transport != NULL)
+               return FALSE;
+
+       conn = endpoint->adapter->conn;
+
+       transport = media_transport_create(conn, endpoint, device,
+                                               configuration, size);
+       if (transport == NULL)
+               return FALSE;
+
+       msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+                                               MEDIA_ENDPOINT_INTERFACE,
+                                               "SetConfiguration");
+       if (msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               media_transport_destroy(transport);
+               return FALSE;
+       }
+
+       endpoint->transports = g_slist_append(endpoint->transports, transport);
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       path = media_transport_get_path(transport);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+       transport_get_properties(transport, &iter);
+
+       return media_endpoint_async_call(conn, msg, endpoint, cb, user_data,
+                                                               destroy);
+}
+
+static void release_endpoint(struct media_endpoint *endpoint)
+{
+       DBusMessage *msg;
+
+       DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
+
+       /* already exit */
+       if (endpoint->watch == 0)
+               goto done;
+
+       msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
+                                               MEDIA_ENDPOINT_INTERFACE,
+                                               "Release");
+       if (msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return;
+       }
+
+       g_dbus_send_message(endpoint->adapter->conn, msg);
+
+done:
+       media_endpoint_remove(endpoint);
+}
+
+static void headset_state_changed(struct audio_device *dev,
+                                       headset_state_t old_state,
+                                       headset_state_t new_state,
+                                       void *user_data)
+{
+       struct media_endpoint *endpoint = user_data;
+       struct media_transport *transport;
+
+       DBG("");
+
+       if (bacmp(&endpoint->adapter->src, &dev->src) != 0)
+               return;
+
+       switch (new_state) {
+       case HEADSET_STATE_DISCONNECTED:
+               transport = find_device_transport(endpoint, dev);
+
+               if (transport != NULL) {
+                       DBG("Clear endpoint %p", endpoint);
+                       clear_configuration(endpoint, transport);
+               }
+               break;
+       case HEADSET_STATE_CONNECTING:
+               set_configuration(endpoint, dev, NULL, 0, headset_setconf_cb,
+                                                               dev, NULL);
+               break;
+       case HEADSET_STATE_CONNECTED:
+               break;
+       case HEADSET_STATE_PLAY_IN_PROGRESS:
+               break;
+       case HEADSET_STATE_PLAYING:
+               break;
+       }
+}
+
+static const char *get_name(struct a2dp_sep *sep, void *user_data)
+{
+       struct media_endpoint *endpoint = user_data;
+
+       return endpoint->sender;
+}
+
+static size_t get_capabilities(struct a2dp_sep *sep, uint8_t **capabilities,
+                                                       void *user_data)
+{
+       struct media_endpoint *endpoint = user_data;
+
+       *capabilities = endpoint->capabilities;
+       return endpoint->size;
+}
+
+struct a2dp_config_data {
+       struct a2dp_setup *setup;
+       a2dp_endpoint_config_t cb;
+};
+
+struct a2dp_select_data {
+       struct a2dp_setup *setup;
+       a2dp_endpoint_select_t cb;
+};
+
+static void select_cb(struct media_endpoint *endpoint, void *ret, int size,
+                                                       void *user_data)
+{
+       struct a2dp_select_data *data = user_data;
+
+       data->cb(data->setup, ret, size);
+}
+
+static int select_config(struct a2dp_sep *sep, uint8_t *capabilities,
+                               size_t length, struct a2dp_setup *setup,
+                               a2dp_endpoint_select_t cb, void *user_data)
+{
+       struct media_endpoint *endpoint = user_data;
+       struct a2dp_select_data *data;
+
+       data = g_new0(struct a2dp_select_data, 1);
+       data->setup = setup;
+       data->cb = cb;
+
+       if (select_configuration(endpoint, capabilities, length,
+                                       select_cb, data, g_free) == TRUE)
+               return 0;
+
+       g_free(data);
+       return -ENOMEM;
+}
+
+static void config_cb(struct media_endpoint *endpoint, void *ret, int size,
+                                                       void *user_data)
+{
+       struct a2dp_config_data *data = user_data;
+
+       data->cb(data->setup, ret ? TRUE : FALSE);
+}
+
+static int set_config(struct a2dp_sep *sep, struct audio_device *dev,
+                               uint8_t *configuration, size_t length,
+                               struct a2dp_setup *setup,
+                               a2dp_endpoint_config_t cb,
+                               void *user_data)
+{
+       struct media_endpoint *endpoint = user_data;
+       struct a2dp_config_data *data;
+
+       data = g_new0(struct a2dp_config_data, 1);
+       data->setup = setup;
+       data->cb = cb;
+
+       if (set_configuration(endpoint, dev, configuration, length,
+                                       config_cb, data, g_free) == TRUE)
+               return 0;
+
+       g_free(data);
+       return -ENOMEM;
+}
+
+static void clear_config(struct a2dp_sep *sep, void *user_data)
+{
+       struct media_endpoint *endpoint = user_data;
+
+       clear_endpoint(endpoint);
+}
+
+static void set_delay(struct a2dp_sep *sep, uint16_t delay, void *user_data)
+{
+       struct media_endpoint *endpoint = user_data;
+
+       if (endpoint->transports == NULL)
+               return;
+
+       media_transport_update_delay(endpoint->transports->data, delay);
+}
+
+static struct a2dp_endpoint a2dp_endpoint = {
+       .get_name = get_name,
+       .get_capabilities = get_capabilities,
+       .select_configuration = select_config,
+       .set_configuration = set_config,
+       .clear_configuration = clear_config,
+       .set_delay = set_delay
+};
+
+static void a2dp_destroy_endpoint(void *user_data)
+{
+       struct media_endpoint *endpoint = user_data;
+
+       clear_endpoint(endpoint);
+
+       endpoint->sep = NULL;
+       release_endpoint(endpoint);
+}
+
+static void gateway_setconf_cb(struct media_endpoint *endpoint, void *ret,
+                                               int size, void *user_data)
+{
+       struct audio_device *dev = user_data;
+
+       if (ret != NULL)
+               return;
+
+       gateway_set_state(dev, GATEWAY_STATE_DISCONNECTED);
+}
+
+static void gateway_state_changed(struct audio_device *dev,
+                                       gateway_state_t old_state,
+                                       gateway_state_t new_state,
+                                       void *user_data)
+{
+       struct media_endpoint *endpoint = user_data;
+       struct media_transport *transport;
+
+       DBG("");
+
+       if (bacmp(&endpoint->adapter->src, &dev->src) != 0)
+               return;
+
+       switch (new_state) {
+       case GATEWAY_STATE_DISCONNECTED:
+               transport = find_device_transport(endpoint, dev);
+               if (transport != NULL) {
+                       DBG("Clear endpoint %p", endpoint);
+                       clear_configuration(endpoint, transport);
+               }
+               break;
+       case GATEWAY_STATE_CONNECTING:
+               set_configuration(endpoint, dev, NULL, 0,
+                                       gateway_setconf_cb, dev, NULL);
+               break;
+       case GATEWAY_STATE_CONNECTED:
+               break;
+       case GATEWAY_STATE_PLAYING:
+               break;
+       }
+}
+
+static gboolean endpoint_init_a2dp_source(struct media_endpoint *endpoint,
+                                               gboolean delay_reporting,
+                                               int *err)
+{
+       endpoint->sep = a2dp_add_sep(&endpoint->adapter->src,
+                                       AVDTP_SEP_TYPE_SOURCE, endpoint->codec,
+                                       delay_reporting, &a2dp_endpoint,
+                                       endpoint, a2dp_destroy_endpoint, err);
+       if (endpoint->sep == NULL)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean endpoint_init_a2dp_sink(struct media_endpoint *endpoint,
+                                               gboolean delay_reporting,
+                                               int *err)
+{
+       endpoint->sep = a2dp_add_sep(&endpoint->adapter->src,
+                                       AVDTP_SEP_TYPE_SINK, endpoint->codec,
+                                       delay_reporting, &a2dp_endpoint,
+                                       endpoint, a2dp_destroy_endpoint, err);
+       if (endpoint->sep == NULL)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean endpoint_init_ag(struct media_endpoint *endpoint, int *err)
+{
+       GSList *list;
+       GSList *l;
+
+       endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
+                                                               endpoint);
+       list = manager_find_devices(NULL, &endpoint->adapter->src, BDADDR_ANY,
+                                               AUDIO_HEADSET_INTERFACE, TRUE);
+
+       for (l = list; l != NULL; l = l->next) {
+               struct audio_device *dev = l->data;
+
+               set_configuration(endpoint, dev, NULL, 0,
+                                               headset_setconf_cb, dev, NULL);
+       }
+
+       g_slist_free(list);
+
+       return TRUE;
+}
+
+static gboolean endpoint_init_hs(struct media_endpoint *endpoint, int *err)
+{
+       GSList *list;
+       GSList *l;
+
+       endpoint->ag_watch = gateway_add_state_cb(gateway_state_changed,
+                                                               endpoint);
+       list = manager_find_devices(NULL, &endpoint->adapter->src, BDADDR_ANY,
+                                               AUDIO_GATEWAY_INTERFACE, TRUE);
+
+       for (l = list; l != NULL; l = l->next) {
+               struct audio_device *dev = l->data;
+
+               set_configuration(endpoint, dev, NULL, 0,
+                                               gateway_setconf_cb, dev, NULL);
+       }
+
+       g_slist_free(list);
+
+       return TRUE;
+}
+
+static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
+                                               const char *sender,
+                                               const char *path,
+                                               const char *uuid,
+                                               gboolean delay_reporting,
+                                               uint8_t codec,
+                                               uint8_t *capabilities,
+                                               int size,
+                                               int *err)
+{
+       struct media_endpoint *endpoint;
+       gboolean succeeded;
+
+       endpoint = g_new0(struct media_endpoint, 1);
+       endpoint->sender = g_strdup(sender);
+       endpoint->path = g_strdup(path);
+       endpoint->uuid = g_strdup(uuid);
+       endpoint->codec = codec;
+
+       if (size > 0) {
+               endpoint->capabilities = g_new(uint8_t, size);
+               memcpy(endpoint->capabilities, capabilities, size);
+               endpoint->size = size;
+       }
+
+       endpoint->adapter = adapter;
+
+       if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0)
+               succeeded = endpoint_init_a2dp_source(endpoint,
+                                                       delay_reporting, err);
+       else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0)
+               succeeded = endpoint_init_a2dp_sink(endpoint,
+                                                       delay_reporting, err);
+       else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
+                                       strcasecmp(uuid, HSP_AG_UUID) == 0)
+               succeeded = endpoint_init_ag(endpoint, err);
+       else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
+                                       strcasecmp(uuid, HSP_HS_UUID) == 0)
+               succeeded = endpoint_init_hs(endpoint, err);
+       else {
+               succeeded = FALSE;
+
+               if (err)
+                       *err = -EINVAL;
+       }
+
+       if (!succeeded) {
+               g_free(endpoint);
+               return NULL;
+       }
+
+       endpoint->watch = g_dbus_add_disconnect_watch(adapter->conn, sender,
+                                               media_endpoint_exit, endpoint,
+                                               NULL);
+
+       adapter->endpoints = g_slist_append(adapter->endpoints, endpoint);
+       info("Endpoint registered: sender=%s path=%s", sender, path);
+
+       if (err)
+               *err = 0;
+       return endpoint;
+}
+
+static struct media_endpoint *media_adapter_find_endpoint(
+                                               struct media_adapter *adapter,
+                                               const char *sender,
+                                               const char *path,
+                                               const char *uuid)
+{
+       GSList *l;
+
+       for (l = adapter->endpoints; l; l = l->next) {
+               struct media_endpoint *endpoint = l->data;
+
+               if (sender && g_strcmp0(endpoint->sender, sender) != 0)
+                       continue;
+
+               if (path && g_strcmp0(endpoint->path, path) != 0)
+                       continue;
+
+               if (uuid && g_strcmp0(endpoint->uuid, uuid) != 0)
+                       continue;
+
+               return endpoint;
+       }
+
+       return NULL;
+}
+
+static int parse_properties(DBusMessageIter *props, const char **uuid,
+                               gboolean *delay_reporting, uint8_t *codec,
+                               uint8_t **capabilities, int *size)
+{
+       gboolean has_uuid = FALSE;
+       gboolean has_codec = FALSE;
+
+       while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
+               const char *key;
+               DBusMessageIter value, entry;
+               int var;
+
+               dbus_message_iter_recurse(props, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               var = dbus_message_iter_get_arg_type(&value);
+               if (strcasecmp(key, "UUID") == 0) {
+                       if (var != DBUS_TYPE_STRING)
+                               return -EINVAL;
+                       dbus_message_iter_get_basic(&value, uuid);
+                       has_uuid = TRUE;
+               } else if (strcasecmp(key, "Codec") == 0) {
+                       if (var != DBUS_TYPE_BYTE)
+                               return -EINVAL;
+                       dbus_message_iter_get_basic(&value, codec);
+                       has_codec = TRUE;
+               } else if (strcasecmp(key, "DelayReporting") == 0) {
+                       if (var != DBUS_TYPE_BOOLEAN)
+                               return -EINVAL;
+                       dbus_message_iter_get_basic(&value, delay_reporting);
+               } else if (strcasecmp(key, "Capabilities") == 0) {
+                       DBusMessageIter array;
+
+                       if (var != DBUS_TYPE_ARRAY)
+                               return -EINVAL;
+
+                       dbus_message_iter_recurse(&value, &array);
+                       dbus_message_iter_get_fixed_array(&array, capabilities,
+                                                       size);
+               }
+
+               dbus_message_iter_next(props);
+       }
+
+       return (has_uuid && has_codec) ? 0 : -EINVAL;
+}
+
+static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct media_adapter *adapter = data;
+       DBusMessageIter args, props;
+       const char *sender, *path, *uuid;
+       gboolean delay_reporting = FALSE;
+       uint8_t codec;
+       uint8_t *capabilities;
+       int size = 0;
+       int err;
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_iter_init(msg, &args);
+
+       dbus_message_iter_get_basic(&args, &path);
+       dbus_message_iter_next(&args);
+
+       if (media_adapter_find_endpoint(adapter, sender, path, NULL) != NULL)
+               return btd_error_already_exists(msg);
+
+       dbus_message_iter_recurse(&args, &props);
+       if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
+               return btd_error_invalid_args(msg);
+
+       if (parse_properties(&props, &uuid, &delay_reporting, &codec,
+                                               &capabilities, &size) < 0)
+               return btd_error_invalid_args(msg);
+
+       if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
+                               codec, capabilities, size, &err) == NULL) {
+               if (err == -EPROTONOSUPPORT)
+                       return btd_error_not_supported(msg);
+               else
+                       return btd_error_invalid_args(msg);
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct media_adapter *adapter = data;
+       struct media_endpoint *endpoint;
+       const char *sender, *path;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID))
+               return NULL;
+
+       sender = dbus_message_get_sender(msg);
+
+       endpoint = media_adapter_find_endpoint(adapter, sender, path, NULL);
+       if (endpoint == NULL)
+               return btd_error_does_not_exist(msg);
+
+       media_endpoint_remove(endpoint);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static struct media_player *media_adapter_find_player(
+                                               struct media_adapter *adapter,
+                                               const char *sender,
+                                               const char *path)
+{
+       GSList *l;
+
+       for (l = adapter->players; l; l = l->next) {
+               struct media_player *mp = l->data;
+
+               if (sender && g_strcmp0(mp->sender, sender) != 0)
+                       continue;
+
+               if (path && g_strcmp0(mp->path, path) != 0)
+                       continue;
+
+               return mp;
+       }
+
+       return NULL;
+}
+
+static void release_player(struct media_player *mp)
+{
+       DBusMessage *msg;
+
+       DBG("sender=%s path=%s", mp->sender, mp->path);
+
+       msg = dbus_message_new_method_call(mp->sender, mp->path,
+                                               MEDIA_PLAYER_INTERFACE,
+                                               "Release");
+       if (msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return;
+       }
+
+       g_dbus_send_message(mp->adapter->conn, msg);
+}
+
+static void media_player_free(gpointer data)
+{
+       struct media_player *mp = data;
+       struct media_adapter *adapter = mp->adapter;
+
+       if (mp->player) {
+               adapter->players = g_slist_remove(adapter->players, mp);
+               release_player(mp);
+       }
+
+       g_dbus_remove_watch(adapter->conn, mp->watch);
+       g_dbus_remove_watch(adapter->conn, mp->property_watch);
+       g_dbus_remove_watch(adapter->conn, mp->track_watch);
+
+       if (mp->track)
+               g_hash_table_unref(mp->track);
+
+       if (mp->settings)
+               g_hash_table_unref(mp->settings);
+
+       g_timer_destroy(mp->timer);
+       g_free(mp->sender);
+       g_free(mp->path);
+       g_free(mp);
+}
+
+static void media_player_destroy(struct media_player *mp)
+{
+       struct media_adapter *adapter = mp->adapter;
+
+       DBG("sender=%s path=%s", mp->sender, mp->path);
+
+       if (mp->player) {
+               struct avrcp_player *player = mp->player;
+               mp->player = NULL;
+               adapter->players = g_slist_remove(adapter->players, mp);
+               avrcp_unregister_player(player);
+               return;
+       }
+
+       media_player_free(mp);
+}
+
+static void media_player_remove(struct media_player *mp)
+{
+       info("Player unregistered: sender=%s path=%s", mp->sender, mp->path);
+
+       media_player_destroy(mp);
+}
+
+static const char *attrval_to_str(uint8_t attr, uint8_t value)
+{
+       switch (attr) {
+       case AVRCP_ATTRIBUTE_EQUALIZER:
+               switch (value) {
+               case AVRCP_EQUALIZER_ON:
+                       return "on";
+               case AVRCP_EQUALIZER_OFF:
+                       return "off";
+               }
+
+               break;
+       case AVRCP_ATTRIBUTE_REPEAT_MODE:
+               switch (value) {
+               case AVRCP_REPEAT_MODE_OFF:
+                       return "off";
+               case AVRCP_REPEAT_MODE_SINGLE:
+                       return "singletrack";
+               case AVRCP_REPEAT_MODE_ALL:
+                       return "alltracks";
+               case AVRCP_REPEAT_MODE_GROUP:
+                       return "group";
+               }
+
+               break;
+       /* Shuffle and scan have the same values */
+       case AVRCP_ATTRIBUTE_SHUFFLE:
+       case AVRCP_ATTRIBUTE_SCAN:
+               switch (value) {
+               case AVRCP_SCAN_OFF:
+                       return "off";
+               case AVRCP_SCAN_ALL:
+                       return "alltracks";
+               case AVRCP_SCAN_GROUP:
+                       return "group";
+               }
+
+               break;
+       }
+
+       return NULL;
+}
+
+static int attrval_to_val(uint8_t attr, const char *value)
+{
+       int ret;
+
+       switch (attr) {
+       case AVRCP_ATTRIBUTE_EQUALIZER:
+               if (!strcmp(value, "off"))
+                       ret = AVRCP_EQUALIZER_OFF;
+               else if (!strcmp(value, "on"))
+                       ret = AVRCP_EQUALIZER_ON;
+               else
+                       ret = -EINVAL;
+
+               return ret;
+       case AVRCP_ATTRIBUTE_REPEAT_MODE:
+               if (!strcmp(value, "off"))
+                       ret = AVRCP_REPEAT_MODE_OFF;
+               else if (!strcmp(value, "singletrack"))
+                       ret = AVRCP_REPEAT_MODE_SINGLE;
+               else if (!strcmp(value, "alltracks"))
+                       ret = AVRCP_REPEAT_MODE_ALL;
+               else if (!strcmp(value, "group"))
+                       ret = AVRCP_REPEAT_MODE_GROUP;
+               else
+                       ret = -EINVAL;
+
+               return ret;
+       case AVRCP_ATTRIBUTE_SHUFFLE:
+               if (!strcmp(value, "off"))
+                       ret = AVRCP_SHUFFLE_OFF;
+               else if (!strcmp(value, "alltracks"))
+                       ret = AVRCP_SHUFFLE_ALL;
+               else if (!strcmp(value, "group"))
+                       ret = AVRCP_SHUFFLE_GROUP;
+               else
+                       ret = -EINVAL;
+
+               return ret;
+       case AVRCP_ATTRIBUTE_SCAN:
+               if (!strcmp(value, "off"))
+                       ret = AVRCP_SCAN_OFF;
+               else if (!strcmp(value, "alltracks"))
+                       ret = AVRCP_SCAN_ALL;
+               else if (!strcmp(value, "group"))
+                       ret = AVRCP_SCAN_GROUP;
+               else
+                       ret = -EINVAL;
+
+               return ret;
+       }
+
+       return -EINVAL;
+}
+
+static const char *attr_to_str(uint8_t attr)
+{
+       switch (attr) {
+       case AVRCP_ATTRIBUTE_EQUALIZER:
+               return "Equalizer";
+       case AVRCP_ATTRIBUTE_REPEAT_MODE:
+               return "Repeat";
+       case AVRCP_ATTRIBUTE_SHUFFLE:
+               return "Shuffle";
+       case AVRCP_ATTRIBUTE_SCAN:
+               return "Scan";
+       }
+
+       return NULL;
+}
+
+static int attr_to_val(const char *str)
+{
+       if (!strcasecmp(str, "Equalizer"))
+               return AVRCP_ATTRIBUTE_EQUALIZER;
+       else if (!strcasecmp(str, "Repeat"))
+               return AVRCP_ATTRIBUTE_REPEAT_MODE;
+       else if (!strcasecmp(str, "Shuffle"))
+               return AVRCP_ATTRIBUTE_SHUFFLE;
+       else if (!strcasecmp(str, "Scan"))
+               return AVRCP_ATTRIBUTE_SCAN;
+
+       return -EINVAL;
+}
+
+static int play_status_to_val(const char *status)
+{
+       if (!strcasecmp(status, "stopped"))
+               return AVRCP_PLAY_STATUS_STOPPED;
+       else if (!strcasecmp(status, "playing"))
+               return AVRCP_PLAY_STATUS_PLAYING;
+       else if (!strcasecmp(status, "paused"))
+               return AVRCP_PLAY_STATUS_PAUSED;
+       else if (!strcasecmp(status, "forward-seek"))
+               return AVRCP_PLAY_STATUS_FWD_SEEK;
+       else if (!strcasecmp(status, "reverse-seek"))
+               return AVRCP_PLAY_STATUS_REV_SEEK;
+       else if (!strcasecmp(status, "error"))
+               return AVRCP_PLAY_STATUS_ERROR;
+
+       return -EINVAL;
+}
+
+static int metadata_to_val(const char *str)
+{
+       if (!strcasecmp(str, "Title"))
+               return AVRCP_MEDIA_ATTRIBUTE_TITLE;
+       else if (!strcasecmp(str, "Artist"))
+               return AVRCP_MEDIA_ATTRIBUTE_ARTIST;
+       else if (!strcasecmp(str, "Album"))
+               return AVRCP_MEDIA_ATTRIBUTE_ALBUM;
+       else if (!strcasecmp(str, "Genre"))
+               return AVRCP_MEDIA_ATTRIBUTE_GENRE;
+       else if (!strcasecmp(str, "NumberOfTracks"))
+               return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS;
+       else if (!strcasecmp(str, "Number"))
+               return AVRCP_MEDIA_ATTRIBUTE_TRACK;
+       else if (!strcasecmp(str, "Duration"))
+               return AVRCP_MEDIA_ATTRIBUTE_DURATION;
+
+       return -EINVAL;
+}
+
+static const char *metadata_to_str(uint32_t id)
+{
+       switch (id) {
+       case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+               return "Title";
+       case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+               return "Artist";
+       case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+               return "Album";
+       case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+               return "Genre";
+       case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+               return "Track";
+       case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+               return "NumberOfTracks";
+       case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+               return "Duration";
+       }
+
+       return NULL;
+}
+
+static int get_setting(uint8_t attr, void *user_data)
+{
+       struct media_player *mp = user_data;
+       guint attr_uint = attr;
+       void *value;
+
+       DBG("%s", attr_to_str(attr));
+
+       value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint));
+       if (!value)
+               return -EINVAL;
+
+       return GPOINTER_TO_UINT(value);
+}
+
+static int set_setting(uint8_t attr, uint8_t val, void *user_data)
+{
+       struct media_player *mp = user_data;
+       struct media_adapter *adapter = mp->adapter;
+       const char *property, *value;
+       guint attr_uint = attr;
+       DBusMessage *msg;
+       DBusMessageIter iter, var;
+
+       property = attr_to_str(attr);
+       value = attrval_to_str(attr, val);
+
+       DBG("%s = %s", property, value);
+
+       if (property == NULL || value == NULL)
+               return -EINVAL;
+
+       if (!g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint)))
+               return -EINVAL;
+
+       msg = dbus_message_new_method_call(mp->sender, mp->path,
+                                               MEDIA_PLAYER_INTERFACE,
+                                               "SetProperty");
+       if (msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return -ENOMEM;
+       }
+
+       dbus_message_iter_init_append(msg, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                               DBUS_TYPE_STRING_AS_STRING,
+                                               &var);
+       dbus_message_iter_append_basic(&var, DBUS_TYPE_STRING, &value);
+       dbus_message_iter_close_container(&iter, &var);
+
+       g_dbus_send_message(adapter->conn, msg);
+
+       return 0;
+}
+
+static GList *list_metadata(void *user_data)
+{
+       struct media_player *mp = user_data;
+
+       DBG("");
+
+       if (mp->track == NULL)
+               return NULL;
+
+       return g_hash_table_get_keys(mp->track);
+}
+
+static uint64_t get_uid(void *user_data)
+{
+       struct media_player *mp = user_data;
+
+       DBG("%p", mp->track);
+
+       if (mp->track == NULL)
+               return UINT64_MAX;
+
+       return 0;
+}
+
+static void *get_metadata(uint32_t id, void *user_data)
+{
+       struct media_player *mp = user_data;
+       struct metadata_value *value;
+
+       DBG("%s", metadata_to_str(id));
+
+       if (mp->track == NULL)
+               return NULL;
+
+       value = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id));
+       if (!value)
+               return NULL;
+
+       switch (value->type) {
+       case DBUS_TYPE_STRING:
+               return value->value.str;
+       case DBUS_TYPE_UINT32:
+               return GUINT_TO_POINTER(value->value.num);
+       }
+
+       return NULL;
+}
+
+static uint8_t get_status(void *user_data)
+{
+       struct media_player *mp = user_data;
+
+       return mp->status;
+}
+
+static uint32_t get_position(void *user_data)
+{
+       struct media_player *mp = user_data;
+       double timedelta;
+       uint32_t sec, msec;
+
+       if (mp->status != AVRCP_PLAY_STATUS_PLAYING)
+               return mp->position;
+
+       timedelta = g_timer_elapsed(mp->timer, NULL);
+
+       sec = (uint32_t) timedelta;
+       msec = (uint32_t) ((timedelta - sec) * 1000);
+
+       return mp->position + sec * 1000 + msec;
+}
+
+static void set_volume(uint8_t volume, struct audio_device *dev, void *user_data)
+{
+       struct media_player *mp = user_data;
+       GSList *l;
+
+       if (mp->volume == volume)
+               return;
+
+       mp->volume = volume;
+
+       for (l = mp->adapter->endpoints; l; l = l->next) {
+               struct media_endpoint *endpoint = l->data;
+               struct media_transport *transport;
+
+               /* Volume is A2DP only */
+               if (endpoint->sep == NULL)
+                       continue;
+
+               transport = find_device_transport(endpoint, dev);
+               if (transport == NULL)
+                       continue;
+
+               media_transport_update_volume(transport, volume);
+       }
+}
+
+static struct avrcp_player_cb player_cb = {
+       .get_setting = get_setting,
+       .set_setting = set_setting,
+       .list_metadata = list_metadata,
+       .get_uid = get_uid,
+       .get_metadata = get_metadata,
+       .get_position = get_position,
+       .get_status = get_status,
+       .set_volume = set_volume
+};
+
+static void media_player_exit(DBusConnection *connection, void *user_data)
+{
+       struct media_player *mp = user_data;
+
+       mp->watch = 0;
+       media_player_remove(mp);
+}
+
+static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
+{
+       const char *value;
+       int val;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+               return FALSE;
+
+       dbus_message_iter_get_basic(iter, &value);
+       DBG("Status=%s", value);
+
+       val = play_status_to_val(value);
+       if (val < 0) {
+               error("Invalid status");
+               return FALSE;
+       }
+
+       if (mp->status == val)
+               return TRUE;
+
+       mp->position = get_position(mp);
+       g_timer_start(mp->timer);
+
+       mp->status = val;
+
+       avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, &val);
+
+       return TRUE;
+}
+
+static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
+{
+       uint32_t value;
+       struct metadata_value *duration;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+                       return FALSE;
+
+       dbus_message_iter_get_basic(iter, &value);
+       DBG("Position=%u", value);
+
+       mp->position = value;
+       g_timer_start(mp->timer);
+
+       if (!mp->position) {
+               avrcp_player_event(mp->player,
+                                       AVRCP_EVENT_TRACK_REACHED_START, NULL);
+               return TRUE;
+       }
+
+       duration = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(
+                                       AVRCP_MEDIA_ATTRIBUTE_DURATION));
+
+       /*
+        * If position is the maximum value allowed or greater than track's
+        * duration, we send a track-reached-end event.
+        */
+       if (mp->position == UINT32_MAX ||
+                       (duration && mp->position >= duration->value.num))
+               avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END,
+                                                                       NULL);
+
+       return TRUE;
+}
+
+static gboolean set_property(struct media_player *mp, const char *key,
+                                                       DBusMessageIter *entry)
+{
+       DBusMessageIter var;
+       const char *value;
+       int attr, val;
+
+       if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+               return FALSE;
+
+       dbus_message_iter_recurse(entry, &var);
+
+       if (strcasecmp(key, "Status") == 0)
+               return set_status(mp, &var);
+
+       if (strcasecmp(key, "Position") == 0)
+               return set_position(mp, &var);
+
+       attr = attr_to_val(key);
+       if (attr < 0)
+               return FALSE;
+
+       if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+               return FALSE;
+
+       dbus_message_iter_get_basic(&var, &value);
+
+       val = attrval_to_val(attr, value);
+       if (val < 0)
+               return FALSE;
+
+       DBG("%s=%s", key, value);
+
+       g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr),
+                                               GUINT_TO_POINTER(val));
+
+       return TRUE;
+}
+
+static gboolean property_changed(DBusConnection *connection, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct media_player *mp = user_data;
+       DBusMessageIter iter;
+       const char *property;
+
+       DBG("sender=%s path=%s", mp->sender, mp->path);
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+               error("Unexpected signature in %s.%s signal",
+                                       dbus_message_get_interface(msg),
+                                       dbus_message_get_member(msg));
+               return TRUE;
+       }
+
+       dbus_message_iter_get_basic(&iter, &property);
+
+       dbus_message_iter_next(&iter);
+
+       set_property(mp, property, &iter);
+
+       return TRUE;
+}
+
+static void metadata_value_free(gpointer data)
+{
+       struct metadata_value *value = data;
+
+       switch (value->type) {
+       case DBUS_TYPE_STRING:
+               g_free(value->value.str);
+               break;
+       }
+
+       g_free(value);
+}
+
+static gboolean parse_player_metadata(struct media_player *mp,
+                                                       DBusMessageIter *iter)
+{
+       DBusMessageIter dict;
+       DBusMessageIter var;
+       GHashTable *track;
+       int ctype;
+       gboolean title = FALSE;
+       uint64_t uid;
+
+       ctype = dbus_message_iter_get_arg_type(iter);
+       if (ctype != DBUS_TYPE_ARRAY)
+               return FALSE;
+
+       dbus_message_iter_recurse(iter, &dict);
+
+       track = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+                                                       metadata_value_free);
+
+       while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+                                                       DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key;
+               struct metadata_value *value;
+               int id;
+
+               if (ctype != DBUS_TYPE_DICT_ENTRY)
+                       goto parse_error;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       goto parse_error;
+
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+
+               id = metadata_to_val(key);
+               if (id < 0)
+                       goto parse_error;
+
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
+                       goto parse_error;
+
+               dbus_message_iter_recurse(&entry, &var);
+
+               value = g_new0(struct metadata_value, 1);
+               value->type = dbus_message_iter_get_arg_type(&var);
+
+               switch (id) {
+               case AVRCP_MEDIA_ATTRIBUTE_TITLE:
+                       title = TRUE;
+               case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
+               case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
+               case AVRCP_MEDIA_ATTRIBUTE_GENRE:
+                       if (value->type != DBUS_TYPE_STRING) {
+                               g_free(value);
+                               goto parse_error;
+                       }
+
+                       dbus_message_iter_get_basic(&var, &value->value.str);
+                       break;
+               case AVRCP_MEDIA_ATTRIBUTE_TRACK:
+               case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS:
+               case AVRCP_MEDIA_ATTRIBUTE_DURATION:
+                       if (value->type != DBUS_TYPE_UINT32) {
+                               g_free(value);
+                               goto parse_error;
+                       }
+
+                       dbus_message_iter_get_basic(&var, &value->value.num);
+                       break;
+               default:
+                       goto parse_error;
+               }
+
+               switch (value->type) {
+               case DBUS_TYPE_STRING:
+                       value->value.str = g_strdup(value->value.str);
+                       DBG("%s=%s", key, value->value.str);
+                       break;
+               default:
+                       DBG("%s=%u", key, value->value.num);
+               }
+
+               g_hash_table_replace(track, GUINT_TO_POINTER(id), value);
+               dbus_message_iter_next(&dict);
+       }
+
+       if (g_hash_table_size(track) == 0) {
+               g_hash_table_unref(track);
+               track = NULL;
+       } else if (title == FALSE) {
+               struct metadata_value *value = g_new(struct metadata_value, 1);
+               uint32_t id = AVRCP_MEDIA_ATTRIBUTE_TITLE;
+
+               value->type = DBUS_TYPE_STRING;
+               value->value.str = g_strdup("");
+               g_hash_table_insert(track, GUINT_TO_POINTER(id), value);
+       }
+
+       if (mp->track != NULL)
+               g_hash_table_unref(mp->track);
+
+       mp->track = track;
+       mp->position = 0;
+       g_timer_start(mp->timer);
+       uid = get_uid(mp);
+
+       avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, &uid);
+       avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START,
+                                                               NULL);
+
+       return TRUE;
+
+parse_error:
+       if (track)
+               g_hash_table_unref(track);
+
+       return FALSE;
+}
+
+static gboolean track_changed(DBusConnection *connection, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct media_player *mp = user_data;
+       DBusMessageIter iter;
+
+       DBG("sender=%s path=%s", mp->sender, mp->path);
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (parse_player_metadata(mp, &iter) == FALSE) {
+               error("Unexpected signature in %s.%s signal",
+                                       dbus_message_get_interface(msg),
+                                       dbus_message_get_member(msg));
+       }
+
+       return TRUE;
+}
+
+static struct media_player *media_player_create(struct media_adapter *adapter,
+                                               const char *sender,
+                                               const char *path,
+                                               int *err)
+{
+       struct media_player *mp;
+
+       mp = g_new0(struct media_player, 1);
+       mp->adapter = adapter;
+       mp->sender = g_strdup(sender);
+       mp->path = g_strdup(path);
+       mp->timer = g_timer_new();
+
+       mp->watch = g_dbus_add_disconnect_watch(adapter->conn, sender,
+                                               media_player_exit, mp,
+                                               NULL);
+       mp->property_watch = g_dbus_add_signal_watch(adapter->conn, sender,
+                                               path, MEDIA_PLAYER_INTERFACE,
+                                               "PropertyChanged",
+                                               property_changed,
+                                               mp, NULL);
+       mp->track_watch = g_dbus_add_signal_watch(adapter->conn, sender,
+                                               path, MEDIA_PLAYER_INTERFACE,
+                                               "TrackChanged",
+                                               track_changed,
+                                               mp, NULL);
+       mp->player = avrcp_register_player(&adapter->src, &player_cb, mp,
+                                               media_player_free);
+       if (!mp->player) {
+               if (err)
+                       *err = -EPROTONOSUPPORT;
+               media_player_destroy(mp);
+               return NULL;
+       }
+
+       mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+       adapter->players = g_slist_append(adapter->players, mp);
+
+       info("Player registered: sender=%s path=%s", sender, path);
+
+       if (err)
+               *err = 0;
+
+       return mp;
+}
+
+static gboolean parse_player_properties(struct media_player *mp,
+                                                       DBusMessageIter *iter)
+{
+       DBusMessageIter dict;
+       int ctype;
+
+       ctype = dbus_message_iter_get_arg_type(iter);
+       if (ctype != DBUS_TYPE_ARRAY)
+               return FALSE;
+
+       dbus_message_iter_recurse(iter, &dict);
+
+       while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+                                                       DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key;
+
+               if (ctype != DBUS_TYPE_DICT_ENTRY)
+                       return FALSE;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       return FALSE;
+
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+
+               if (set_property(mp, key, &entry) == FALSE)
+                       return FALSE;
+
+               dbus_message_iter_next(&dict);
+       }
+
+       return TRUE;
+}
+
+static DBusMessage *register_player(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct media_adapter *adapter = data;
+       struct media_player *mp;
+       DBusMessageIter args;
+       const char *sender, *path;
+       int err;
+
+       sender = dbus_message_get_sender(msg);
+
+       dbus_message_iter_init(msg, &args);
+
+       dbus_message_iter_get_basic(&args, &path);
+       dbus_message_iter_next(&args);
+
+       if (media_adapter_find_player(adapter, sender, path) != NULL)
+               return btd_error_already_exists(msg);
+
+       mp = media_player_create(adapter, sender, path, &err);
+       if (mp == NULL) {
+               if (err == -EPROTONOSUPPORT)
+                       return btd_error_not_supported(msg);
+               else
+                       return btd_error_invalid_args(msg);
+       }
+
+       if (parse_player_properties(mp, &args) == FALSE) {
+               media_player_destroy(mp);
+               return btd_error_invalid_args(msg);
+       }
+
+       dbus_message_iter_next(&args);
+
+       if (parse_player_metadata(mp, &args) == FALSE) {
+               media_player_destroy(mp);
+               return btd_error_invalid_args(msg);
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *unregister_player(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct media_adapter *adapter = data;
+       struct media_player *player;
+       const char *sender, *path;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID))
+               return NULL;
+
+       sender = dbus_message_get_sender(msg);
+
+       player = media_adapter_find_player(adapter, sender, path);
+       if (player == NULL)
+               return btd_error_does_not_exist(msg);
+
+       media_player_remove(player);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable media_methods[] = {
+       { GDBUS_METHOD("RegisterEndpoint",
+               GDBUS_ARGS({ "endpoint", "o" }, { "properties", "a{sv}" }),
+               NULL, register_endpoint) },
+       { GDBUS_METHOD("UnregisterEndpoint",
+               GDBUS_ARGS({ "endpoint", "o" }), NULL, unregister_endpoint) },
+       { GDBUS_METHOD("RegisterPlayer",
+               GDBUS_ARGS({ "player", "o" }, { "properties", "a{sv}" },
+                                               { "metadata", "a{sv}" }),
+               NULL, register_player) },
+       { GDBUS_METHOD("UnregisterPlayer",
+               GDBUS_ARGS({ "player", "o" }), NULL, unregister_player) },
+       { },
+};
+
+static void path_free(void *data)
+{
+       struct media_adapter *adapter = data;
+
+       while (adapter->endpoints)
+               release_endpoint(adapter->endpoints->data);
+
+       dbus_connection_unref(adapter->conn);
+
+       adapters = g_slist_remove(adapters, adapter);
+
+       g_free(adapter->path);
+       g_free(adapter);
+}
+
+int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src)
+{
+       struct media_adapter *adapter;
+
+       adapter = g_new0(struct media_adapter, 1);
+       adapter->conn = dbus_connection_ref(conn);
+       bacpy(&adapter->src, src);
+       adapter->path = g_strdup(path);
+
+       if (!g_dbus_register_interface(conn, path, MEDIA_INTERFACE,
+                                       media_methods, NULL, NULL,
+                                       adapter, path_free)) {
+               error("D-Bus failed to register %s path", path);
+               path_free(adapter);
+               return -1;
+       }
+
+       adapters = g_slist_append(adapters, adapter);
+
+       return 0;
+}
+
+void media_unregister(const char *path)
+{
+       GSList *l;
+
+       for (l = adapters; l; l = l->next) {
+               struct media_adapter *adapter = l->data;
+
+               if (g_strcmp0(path, adapter->path) == 0) {
+                       g_dbus_unregister_interface(adapter->conn, path,
+                                                       MEDIA_INTERFACE);
+                       return;
+               }
+       }
+}
+
+struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint)
+{
+       return endpoint->sep;
+}
+
+const char *media_endpoint_get_uuid(struct media_endpoint *endpoint)
+{
+       return endpoint->uuid;
+}
+
+uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
+{
+       return endpoint->codec;
+}
diff --git a/audio/media.h b/audio/media.h
new file mode 100644 (file)
index 0000000..ee9a51e
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct media_endpoint;
+
+typedef void (*media_endpoint_cb_t) (struct media_endpoint *endpoint,
+                                       void *ret, int size, void *user_data);
+
+int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src);
+void media_unregister(const char *path);
+
+struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint);
+const char *media_endpoint_get_uuid(struct media_endpoint *endpoint);
+uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint);
+struct media_transport *media_endpoint_get_transport(
+                                       struct media_endpoint *endpoint);
diff --git a/audio/pcm_bluetooth.c b/audio/pcm_bluetooth.c
new file mode 100644 (file)
index 0000000..b9da805
--- /dev/null
@@ -0,0 +1,1785 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <signal.h>
+#include <limits.h>
+
+#include <netinet/in.h>
+
+#include <alsa/asoundlib.h>
+#include <alsa/pcm_external.h>
+
+#include "ipc.h"
+#include "sbc.h"
+#include "rtp.h"
+
+/* #define ENABLE_DEBUG */
+
+#define UINT_SECS_MAX (UINT_MAX / 1000000 - 1)
+
+#define MIN_PERIOD_TIME 1
+
+#define BUFFER_SIZE 2048
+
+#ifdef ENABLE_DEBUG
+#define DBG(fmt, arg...)  printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg)
+#else
+#define DBG(fmt, arg...)
+#endif
+
+#ifndef SOL_SCO
+#define SOL_SCO 17
+#endif
+
+#ifndef SCO_TXBUFS
+#define SCO_TXBUFS 0x03
+#endif
+
+#ifndef SCO_RXBUFS
+#define SCO_RXBUFS 0x04
+#endif
+
+#ifndef MIN
+# define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+/* adapted from glibc sys/time.h timersub() macro */
+#define priv_timespecsub(a, b, result)                                 \
+       do {                                                            \
+               (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;           \
+               (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec;        \
+               if ((result)->tv_nsec < 0) {                            \
+                       --(result)->tv_sec;                             \
+                       (result)->tv_nsec += 1000000000;                \
+               }                                                       \
+       } while (0)
+
+struct bluetooth_a2dp {
+       sbc_capabilities_t sbc_capabilities;
+       sbc_t sbc;                              /* Codec data */
+       int sbc_initialized;                    /* Keep track if the encoder is initialized */
+       unsigned int codesize;                  /* SBC codesize */
+       int samples;                            /* Number of encoded samples */
+       uint8_t buffer[BUFFER_SIZE];            /* Codec transfer buffer */
+       unsigned int count;                     /* Codec transfer buffer counter */
+
+       int nsamples;                           /* Cumulative number of codec samples */
+       uint16_t seq_num;                       /* Cumulative packet sequence */
+       int frame_count;                        /* Current frames in buffer*/
+};
+
+struct bluetooth_alsa_config {
+       char device[18];                /* Address of the remote Device */
+       int has_device;
+       uint8_t transport;              /* Requested transport */
+       int has_transport;
+       uint16_t rate;
+       int has_rate;
+       uint8_t channel_mode;           /* A2DP only */
+       int has_channel_mode;
+       uint8_t allocation_method;      /* A2DP only */
+       int has_allocation_method;
+       uint8_t subbands;               /* A2DP only */
+       int has_subbands;
+       uint8_t block_length;           /* A2DP only */
+       int has_block_length;
+       uint8_t bitpool;                /* A2DP only */
+       int has_bitpool;
+       int autoconnect;
+};
+
+struct bluetooth_data {
+       snd_pcm_ioplug_t io;
+       struct bluetooth_alsa_config alsa_config;       /* ALSA resource file parameters */
+       volatile snd_pcm_sframes_t hw_ptr;
+       int transport;                                  /* chosen transport SCO or AD2P */
+       unsigned int link_mtu;                          /* MTU for selected transport channel */
+       volatile struct pollfd stream;                  /* Audio stream filedescriptor */
+       struct pollfd server;                           /* Audio daemon filedescriptor */
+       uint8_t buffer[BUFFER_SIZE];            /* Encoded transfer buffer */
+       unsigned int count;                             /* Transfer buffer counter */
+       struct bluetooth_a2dp a2dp;                     /* A2DP data */
+
+       pthread_t hw_thread;                            /* Makes virtual hw pointer move */
+       int pipefd[2];                                  /* Inter thread communication */
+       int stopped;
+       sig_atomic_t reset;                             /* Request XRUN handling */
+};
+
+static int audioservice_send(int sk, const bt_audio_msg_header_t *msg);
+static int audioservice_expect(int sk, bt_audio_msg_header_t *outmsg,
+                                                       int expected_type);
+
+static int bluetooth_start(snd_pcm_ioplug_t *io)
+{
+       DBG("bluetooth_start %p", io);
+
+       return 0;
+}
+
+static int bluetooth_stop(snd_pcm_ioplug_t *io)
+{
+       DBG("bluetooth_stop %p", io);
+
+       return 0;
+}
+
+static void *playback_hw_thread(void *param)
+{
+       struct bluetooth_data *data = param;
+       unsigned int prev_periods;
+       double period_time;
+       struct timespec start;
+       struct pollfd fds[2];
+       int poll_timeout;
+
+       data->server.events = POLLIN;
+       /* note: only errors for data->stream.events */
+
+       fds[0] = data->server;
+       fds[1] = data->stream;
+
+       prev_periods = 0;
+       period_time = 1000000.0 * data->io.period_size / data->io.rate;
+       if (period_time > (int) (MIN_PERIOD_TIME * 1000))
+               poll_timeout = (int) (period_time / 1000.0f);
+       else
+               poll_timeout = MIN_PERIOD_TIME;
+
+       clock_gettime(CLOCK_MONOTONIC, &start);
+
+       while (1) {
+               unsigned int dtime, periods;
+               struct timespec cur, delta;
+               int ret;
+
+               if (data->stopped)
+                       goto iter_sleep;
+
+               if (data->reset) {
+                       DBG("Handle XRUN in hw-thread.");
+                       data->reset = 0;
+                       clock_gettime(CLOCK_MONOTONIC, &start);
+                       prev_periods = 0;
+               }
+
+               clock_gettime(CLOCK_MONOTONIC, &cur);
+
+               priv_timespecsub(&cur, &start, &delta);
+
+               dtime = delta.tv_sec * 1000000 + delta.tv_nsec / 1000;
+               periods = 1.0 * dtime / period_time;
+
+               if (periods > prev_periods) {
+                       char c = 'w';
+                       int frags = periods - prev_periods, n;
+
+                       data->hw_ptr += frags * data->io.period_size;
+                       data->hw_ptr %= data->io.buffer_size;
+
+                       for (n = 0; n < frags; n++) {
+                               /* Notify user that hardware pointer
+                                * has moved * */
+                               if (write(data->pipefd[1], &c, 1) < 0)
+                                       pthread_testcancel();
+                       }
+
+                       /* Reset point of reference to avoid too big values
+                        * that wont fit an unsigned int */
+                       if ((unsigned int) delta.tv_sec < UINT_SECS_MAX)
+                               prev_periods = periods;
+                       else {
+                               prev_periods = 0;
+                               clock_gettime(CLOCK_MONOTONIC, &start);
+                       }
+               }
+
+iter_sleep:
+               /* sleep up to one period interval */
+               ret = poll(fds, 2, poll_timeout);
+
+               if (ret < 0) {
+                       if (errno != EINTR) {
+                               SNDERR("poll error: %s (%d)", strerror(errno),
+                                                               errno);
+                               break;
+                       }
+               } else if (ret > 0) {
+                       ret = (fds[0].revents) ? 0 : 1;
+                       SNDERR("poll fd %d revents %d", ret, fds[ret].revents);
+                       if (fds[ret].revents & (POLLERR | POLLHUP | POLLNVAL))
+                               break;
+               }
+
+               /* Offer opportunity to be canceled by main thread */
+               pthread_testcancel();
+       }
+
+       data->hw_thread = 0;
+       pthread_exit(NULL);
+}
+
+static int bluetooth_playback_start(snd_pcm_ioplug_t *io)
+{
+       struct bluetooth_data *data = io->private_data;
+       int err;
+
+       DBG("%p", io);
+
+       data->stopped = 0;
+
+       if (data->hw_thread)
+               return 0;
+
+       err = pthread_create(&data->hw_thread, 0, playback_hw_thread, data);
+
+       return -err;
+}
+
+static int bluetooth_playback_stop(snd_pcm_ioplug_t *io)
+{
+       struct bluetooth_data *data = io->private_data;
+
+       DBG("%p", io);
+
+       data->stopped = 1;
+
+       return 0;
+}
+
+static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io)
+{
+       struct bluetooth_data *data = io->private_data;
+
+       return data->hw_ptr;
+}
+
+static void bluetooth_exit(struct bluetooth_data *data)
+{
+       struct bluetooth_a2dp *a2dp = &data->a2dp;
+
+       if (data->server.fd >= 0)
+               bt_audio_service_close(data->server.fd);
+
+       if (data->stream.fd >= 0)
+               close(data->stream.fd);
+
+       if (data->hw_thread) {
+               pthread_cancel(data->hw_thread);
+               pthread_join(data->hw_thread, 0);
+       }
+
+       if (a2dp->sbc_initialized)
+               sbc_finish(&a2dp->sbc);
+
+       if (data->pipefd[0] > 0)
+               close(data->pipefd[0]);
+
+       if (data->pipefd[1] > 0)
+               close(data->pipefd[1]);
+
+       free(data);
+}
+
+static int bluetooth_close(snd_pcm_ioplug_t *io)
+{
+       struct bluetooth_data *data = io->private_data;
+
+       DBG("%p", io);
+
+       bluetooth_exit(data);
+
+       return 0;
+}
+
+static int bluetooth_prepare(snd_pcm_ioplug_t *io)
+{
+       struct bluetooth_data *data = io->private_data;
+       char c = 'w';
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_start_stream_req *req = (void *) buf;
+       struct bt_start_stream_rsp *rsp = (void *) buf;
+       struct bt_new_stream_ind *ind = (void *) buf;
+       uint32_t period_count = io->buffer_size / io->period_size;
+       int opt_name, err;
+       struct timeval t = { 0, period_count };
+
+       DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",
+                                       io->period_size, io->buffer_size);
+
+       data->reset = 0;
+
+       /* As we're gonna receive messages on the server socket, we have to stop the
+          hw thread that is polling on it, if any */
+       if (data->hw_thread) {
+               pthread_cancel(data->hw_thread);
+               pthread_join(data->hw_thread, 0);
+               data->hw_thread = 0;
+       }
+
+       if (io->stream == SND_PCM_STREAM_PLAYBACK)
+               /* If not null for playback, xmms doesn't display time
+                * correctly */
+               data->hw_ptr = 0;
+       else
+               /* ALSA library is really picky on the fact hw_ptr is not null.
+                * If it is, capture won't start */
+               data->hw_ptr = io->period_size;
+
+       /* send start */
+       memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+       req->h.type = BT_REQUEST;
+       req->h.name = BT_START_STREAM;
+       req->h.length = sizeof(*req);
+
+       err = audioservice_send(data->server.fd, &req->h);
+       if (err < 0)
+               return err;
+
+       rsp->h.length = sizeof(*rsp);
+       err = audioservice_expect(data->server.fd, &rsp->h,
+                                       BT_START_STREAM);
+       if (err < 0)
+               return err;
+
+       ind->h.length = sizeof(*ind);
+       err = audioservice_expect(data->server.fd, &ind->h,
+                                       BT_NEW_STREAM);
+       if (err < 0)
+               return err;
+
+       if (data->stream.fd >= 0)
+               close(data->stream.fd);
+
+       data->stream.fd = bt_audio_service_get_data_fd(data->server.fd);
+       if (data->stream.fd < 0) {
+               return -errno;
+       }
+
+       if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+               opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
+                                               SO_SNDTIMEO : SO_RCVTIMEO;
+
+               if (setsockopt(data->stream.fd, SOL_SOCKET, opt_name, &t,
+                                                       sizeof(t)) < 0)
+                       return -errno;
+       } else {
+               opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
+                                               SCO_TXBUFS : SCO_RXBUFS;
+
+               if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count,
+                                               sizeof(period_count)) == 0)
+                       return 0;
+
+               opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ?
+                                               SO_SNDBUF : SO_RCVBUF;
+
+               if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count,
+                                               sizeof(period_count)) == 0)
+                       return 0;
+
+               /* FIXME : handle error codes */
+       }
+
+       /* wake up any client polling at us */
+       if (write(data->pipefd[1], &c, 1) < 0) {
+               err = -errno;
+               return err;
+       }
+
+       return 0;
+}
+
+static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io,
+                                       snd_pcm_hw_params_t *params)
+{
+       struct bluetooth_data *data = io->private_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_open_req *open_req = (void *) buf;
+       struct bt_open_rsp *open_rsp = (void *) buf;
+       struct bt_set_configuration_req *req = (void *) buf;
+       struct bt_set_configuration_rsp *rsp = (void *) buf;
+       int err;
+
+       DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",
+                                       io->period_size, io->buffer_size);
+
+       memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+       open_req->h.type = BT_REQUEST;
+       open_req->h.name = BT_OPEN;
+       open_req->h.length = sizeof(*open_req);
+
+       strncpy(open_req->destination, data->alsa_config.device, 18);
+       open_req->seid = BT_A2DP_SEID_RANGE + 1;
+       open_req->lock = (io->stream == SND_PCM_STREAM_PLAYBACK ?
+                       BT_WRITE_LOCK : BT_READ_LOCK);
+
+       err = audioservice_send(data->server.fd, &open_req->h);
+       if (err < 0)
+               return err;
+
+       open_rsp->h.length = sizeof(*open_rsp);
+       err = audioservice_expect(data->server.fd, &open_rsp->h,
+                                       BT_OPEN);
+       if (err < 0)
+               return err;
+
+       memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+       req->h.type = BT_REQUEST;
+       req->h.name = BT_SET_CONFIGURATION;
+       req->h.length = sizeof(*req);
+
+       req->codec.transport = BT_CAPABILITIES_TRANSPORT_SCO;
+       req->codec.seid = BT_A2DP_SEID_RANGE + 1;
+       req->codec.length = sizeof(pcm_capabilities_t);
+
+       req->h.length += req->codec.length - sizeof(req->codec);
+       err = audioservice_send(data->server.fd, &req->h);
+       if (err < 0)
+               return err;
+
+       rsp->h.length = sizeof(*rsp);
+       err = audioservice_expect(data->server.fd, &rsp->h,
+                                       BT_SET_CONFIGURATION);
+       if (err < 0)
+               return err;
+
+       data->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+       data->link_mtu = rsp->link_mtu;
+
+       return 0;
+}
+
+static uint8_t default_bitpool(uint8_t freq, uint8_t mode)
+{
+       switch (freq) {
+       case BT_SBC_SAMPLING_FREQ_16000:
+       case BT_SBC_SAMPLING_FREQ_32000:
+               return 53;
+       case BT_SBC_SAMPLING_FREQ_44100:
+               switch (mode) {
+               case BT_A2DP_CHANNEL_MODE_MONO:
+               case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+                       return 31;
+               case BT_A2DP_CHANNEL_MODE_STEREO:
+               case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+                       return 53;
+               default:
+                       DBG("Invalid channel mode %u", mode);
+                       return 53;
+               }
+       case BT_SBC_SAMPLING_FREQ_48000:
+               switch (mode) {
+               case BT_A2DP_CHANNEL_MODE_MONO:
+               case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+                       return 29;
+               case BT_A2DP_CHANNEL_MODE_STEREO:
+               case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+                       return 51;
+               default:
+                       DBG("Invalid channel mode %u", mode);
+                       return 51;
+               }
+       default:
+               DBG("Invalid sampling freq %u", freq);
+               return 53;
+       }
+}
+
+static int bluetooth_a2dp_init(struct bluetooth_data *data,
+                                       snd_pcm_hw_params_t *params)
+{
+       struct bluetooth_alsa_config *cfg = &data->alsa_config;
+       sbc_capabilities_t *cap = &data->a2dp.sbc_capabilities;
+       unsigned int max_bitpool, min_bitpool, rate, channels;
+       int dir;
+
+       snd_pcm_hw_params_get_rate(params, &rate, &dir);
+       snd_pcm_hw_params_get_channels(params, &channels);
+
+       switch (rate) {
+       case 48000:
+               cap->frequency = BT_SBC_SAMPLING_FREQ_48000;
+               break;
+       case 44100:
+               cap->frequency = BT_SBC_SAMPLING_FREQ_44100;
+               break;
+       case 32000:
+               cap->frequency = BT_SBC_SAMPLING_FREQ_32000;
+               break;
+       case 16000:
+               cap->frequency = BT_SBC_SAMPLING_FREQ_16000;
+               break;
+       default:
+               DBG("Rate %d not supported", rate);
+               return -1;
+       }
+
+       if (cfg->has_channel_mode)
+               cap->channel_mode = cfg->channel_mode;
+       else if (channels == 2) {
+               if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+                       cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+               else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+                       cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+               else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+                       cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+       } else {
+               if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+                       cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+       }
+
+       if (!cap->channel_mode) {
+               DBG("No supported channel modes");
+               return -1;
+       }
+
+       if (cfg->has_block_length)
+               cap->block_length = cfg->block_length;
+       else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
+               cap->block_length = BT_A2DP_BLOCK_LENGTH_16;
+       else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)
+               cap->block_length = BT_A2DP_BLOCK_LENGTH_12;
+       else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8)
+               cap->block_length = BT_A2DP_BLOCK_LENGTH_8;
+       else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4)
+               cap->block_length = BT_A2DP_BLOCK_LENGTH_4;
+       else {
+               DBG("No supported block lengths");
+               return -1;
+       }
+
+       if (cfg->has_subbands)
+               cap->subbands = cfg->subbands;
+       if (cap->subbands & BT_A2DP_SUBBANDS_8)
+               cap->subbands = BT_A2DP_SUBBANDS_8;
+       else if (cap->subbands & BT_A2DP_SUBBANDS_4)
+               cap->subbands = BT_A2DP_SUBBANDS_4;
+       else {
+               DBG("No supported subbands");
+               return -1;
+       }
+
+       if (cfg->has_allocation_method)
+               cap->allocation_method = cfg->allocation_method;
+       if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)
+               cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+       else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
+               cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
+
+       if (cfg->has_bitpool)
+               min_bitpool = max_bitpool = cfg->bitpool;
+       else {
+               min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool);
+               max_bitpool = MIN(default_bitpool(cap->frequency,
+                                       cap->channel_mode),
+                                       cap->max_bitpool);
+       }
+
+       cap->min_bitpool = min_bitpool;
+       cap->max_bitpool = max_bitpool;
+
+       return 0;
+}
+
+static void bluetooth_a2dp_setup(struct bluetooth_a2dp *a2dp)
+{
+       sbc_capabilities_t active_capabilities = a2dp->sbc_capabilities;
+
+       if (a2dp->sbc_initialized)
+               sbc_reinit(&a2dp->sbc, 0);
+       else
+               sbc_init(&a2dp->sbc, 0);
+       a2dp->sbc_initialized = 1;
+
+       if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000)
+               a2dp->sbc.frequency = SBC_FREQ_16000;
+
+       if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000)
+               a2dp->sbc.frequency = SBC_FREQ_32000;
+
+       if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100)
+               a2dp->sbc.frequency = SBC_FREQ_44100;
+
+       if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000)
+               a2dp->sbc.frequency = SBC_FREQ_48000;
+
+       if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+               a2dp->sbc.mode = SBC_MODE_MONO;
+
+       if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+               a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
+
+       if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+               a2dp->sbc.mode = SBC_MODE_STEREO;
+
+       if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+               a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+
+       a2dp->sbc.allocation = active_capabilities.allocation_method
+                               == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR
+                               : SBC_AM_LOUDNESS;
+
+       switch (active_capabilities.subbands) {
+       case BT_A2DP_SUBBANDS_4:
+               a2dp->sbc.subbands = SBC_SB_4;
+               break;
+       case BT_A2DP_SUBBANDS_8:
+               a2dp->sbc.subbands = SBC_SB_8;
+               break;
+       }
+
+       switch (active_capabilities.block_length) {
+       case BT_A2DP_BLOCK_LENGTH_4:
+               a2dp->sbc.blocks = SBC_BLK_4;
+               break;
+       case BT_A2DP_BLOCK_LENGTH_8:
+               a2dp->sbc.blocks = SBC_BLK_8;
+               break;
+       case BT_A2DP_BLOCK_LENGTH_12:
+               a2dp->sbc.blocks = SBC_BLK_12;
+               break;
+       case BT_A2DP_BLOCK_LENGTH_16:
+               a2dp->sbc.blocks = SBC_BLK_16;
+               break;
+       }
+
+       a2dp->sbc.bitpool = active_capabilities.max_bitpool;
+       a2dp->codesize = sbc_get_codesize(&a2dp->sbc);
+       a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+}
+
+static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io,
+                                       snd_pcm_hw_params_t *params)
+{
+       struct bluetooth_data *data = io->private_data;
+       struct bluetooth_a2dp *a2dp = &data->a2dp;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_open_req *open_req = (void *) buf;
+       struct bt_open_rsp *open_rsp = (void *) buf;
+       struct bt_set_configuration_req *req = (void *) buf;
+       struct bt_set_configuration_rsp *rsp = (void *) buf;
+       int err;
+
+       DBG("Preparing with io->period_size=%lu io->buffer_size=%lu",
+                                       io->period_size, io->buffer_size);
+
+       memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+       open_req->h.type = BT_REQUEST;
+       open_req->h.name = BT_OPEN;
+       open_req->h.length = sizeof(*open_req);
+
+       strncpy(open_req->destination, data->alsa_config.device, 18);
+       open_req->seid = a2dp->sbc_capabilities.capability.seid;
+       open_req->lock = (io->stream == SND_PCM_STREAM_PLAYBACK ?
+                       BT_WRITE_LOCK : BT_READ_LOCK);
+
+       err = audioservice_send(data->server.fd, &open_req->h);
+       if (err < 0)
+               return err;
+
+       open_rsp->h.length = sizeof(*open_rsp);
+       err = audioservice_expect(data->server.fd, &open_rsp->h,
+                                       BT_OPEN);
+       if (err < 0)
+               return err;
+
+       err = bluetooth_a2dp_init(data, params);
+       if (err < 0)
+               return err;
+
+       memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+       req->h.type = BT_REQUEST;
+       req->h.name = BT_SET_CONFIGURATION;
+       req->h.length = sizeof(*req);
+
+       memcpy(&req->codec, &a2dp->sbc_capabilities,
+                       sizeof(a2dp->sbc_capabilities));
+
+       req->codec.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+       req->codec.length = sizeof(a2dp->sbc_capabilities);
+       req->h.length += req->codec.length - sizeof(req->codec);
+
+       err = audioservice_send(data->server.fd, &req->h);
+       if (err < 0)
+               return err;
+
+       rsp->h.length = sizeof(*rsp);
+       err = audioservice_expect(data->server.fd, &rsp->h,
+                                       BT_SET_CONFIGURATION);
+       if (err < 0)
+               return err;
+
+       data->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+       data->link_mtu = rsp->link_mtu;
+
+       /* Setup SBC encoder now we agree on parameters */
+       bluetooth_a2dp_setup(a2dp);
+
+       DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
+               a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks,
+               a2dp->sbc.bitpool);
+
+       return 0;
+}
+
+static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io,
+                                       struct pollfd *pfd, unsigned int space)
+{
+       struct bluetooth_data *data = io->private_data;
+
+       assert(io);
+
+       if (space < 1)
+               return 0;
+
+       pfd[0].fd = data->stream.fd;
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+
+       return 1;
+}
+
+static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED,
+                                       struct pollfd *pfds, unsigned int nfds,
+                                       unsigned short *revents)
+{
+       assert(pfds && nfds == 1 && revents);
+
+       *revents = pfds[0].revents;
+
+       return 0;
+}
+
+static int bluetooth_playback_poll_descriptors_count(snd_pcm_ioplug_t *io)
+{
+       return 2;
+}
+
+static int bluetooth_playback_poll_descriptors(snd_pcm_ioplug_t *io,
+                                       struct pollfd *pfd, unsigned int space)
+{
+       struct bluetooth_data *data = io->private_data;
+
+       DBG("");
+
+       assert(data->pipefd[0] >= 0);
+
+       if (space < 2)
+               return 0;
+
+       pfd[0].fd = data->pipefd[0];
+       pfd[0].events = POLLIN;
+       pfd[0].revents = 0;
+       pfd[1].fd = data->stream.fd;
+       pfd[1].events = POLLERR | POLLHUP | POLLNVAL;
+       pfd[1].revents = 0;
+
+       return 2;
+}
+
+static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io,
+                                       struct pollfd *pfds, unsigned int nfds,
+                                       unsigned short *revents)
+{
+       static char buf[1];
+
+       DBG("");
+
+       assert(pfds);
+       assert(nfds == 2);
+       assert(revents);
+       assert(pfds[0].fd >= 0);
+       assert(pfds[1].fd >= 0);
+
+       if (io->state != SND_PCM_STATE_PREPARED)
+               if (read(pfds[0].fd, buf, 1) < 0)
+                       SYSERR("read error: %s (%d)", strerror(errno), errno);
+
+       if (pfds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
+               io->state = SND_PCM_STATE_DISCONNECTED;
+
+       *revents = (pfds[0].revents & POLLIN) ? POLLOUT : 0;
+
+       return 0;
+}
+
+
+static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io,
+                               const snd_pcm_channel_area_t *areas,
+                               snd_pcm_uframes_t offset,
+                               snd_pcm_uframes_t size)
+{
+       struct bluetooth_data *data = io->private_data;
+       snd_pcm_uframes_t frames_to_write, ret;
+       unsigned char *buff;
+       unsigned int frame_size = 0;
+       int nrecv;
+
+       DBG("areas->step=%u areas->first=%u offset=%lu size=%lu io->nonblock=%u",
+                       areas->step, areas->first, offset, size, io->nonblock);
+
+       frame_size = areas->step / 8;
+
+       if (data->count > 0)
+               goto proceed;
+
+       nrecv = recv(data->stream.fd, data->buffer, data->link_mtu,
+                                       io->nonblock ? MSG_DONTWAIT : 0);
+
+       if (nrecv < 0) {
+               ret = (errno == EPIPE) ? -EIO : -errno;
+               goto done;
+       }
+
+       if ((unsigned int) nrecv != data->link_mtu) {
+               ret = -EIO;
+               SNDERR(strerror(-ret));
+               goto done;
+       }
+
+       /* Increment hardware transmition pointer */
+       data->hw_ptr = (data->hw_ptr + data->link_mtu / frame_size) %
+                               io->buffer_size;
+
+proceed:
+       buff = (unsigned char *) areas->addr +
+                       (areas->first + areas->step * offset) / 8;
+
+       if ((data->count + size * frame_size) <= data->link_mtu)
+               frames_to_write = size;
+       else
+               frames_to_write = (data->link_mtu - data->count) / frame_size;
+
+       memcpy(buff, data->buffer + data->count, frame_size * frames_to_write);
+       data->count += (frame_size * frames_to_write);
+       data->count %= data->link_mtu;
+
+       /* Return written frames count */
+       ret = frames_to_write;
+
+done:
+       DBG("returning %lu", ret);
+       return ret;
+}
+
+static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io,
+                               const snd_pcm_channel_area_t *areas,
+                               snd_pcm_uframes_t offset,
+                               snd_pcm_uframes_t size)
+{
+       struct bluetooth_data *data = io->private_data;
+       snd_pcm_sframes_t ret = 0;
+       snd_pcm_uframes_t frames_to_read;
+       uint8_t *buff;
+       int rsend, frame_size;
+
+       DBG("areas->step=%u areas->first=%u offset=%lu, size=%lu io->nonblock=%u",
+                       areas->step, areas->first, offset, size, io->nonblock);
+
+       if (io->hw_ptr > io->appl_ptr) {
+               ret = bluetooth_playback_stop(io);
+               if (ret == 0)
+                       ret = -EPIPE;
+               goto done;
+       }
+
+       frame_size = areas->step / 8;
+       if ((data->count + size * frame_size) <= data->link_mtu)
+               frames_to_read = size;
+       else
+               frames_to_read = (data->link_mtu - data->count) / frame_size;
+
+       DBG("count=%d frames_to_read=%lu", data->count, frames_to_read);
+
+       /* Ready for more data */
+       buff = (uint8_t *) areas->addr +
+                       (areas->first + areas->step * offset) / 8;
+       memcpy(data->buffer + data->count, buff, frame_size * frames_to_read);
+
+       /* Remember we have some frames in the pipe now */
+       data->count += frames_to_read * frame_size;
+       if (data->count != data->link_mtu) {
+               ret = frames_to_read;
+               goto done;
+       }
+
+       rsend = send(data->stream.fd, data->buffer, data->link_mtu,
+                       io->nonblock ? MSG_DONTWAIT : 0);
+       if (rsend > 0) {
+               /* Reset count pointer */
+               data->count = 0;
+
+               ret = frames_to_read;
+       } else if (rsend < 0)
+               ret = (errno == EPIPE) ? -EIO : -errno;
+       else
+               ret = -EIO;
+
+done:
+       DBG("returning %ld", ret);
+       return ret;
+}
+
+static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io,
+                               const snd_pcm_channel_area_t *areas,
+                               snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
+{
+       snd_pcm_uframes_t ret = 0;
+       return ret;
+}
+
+static int avdtp_write(struct bluetooth_data *data)
+{
+       int err;
+       struct rtp_header *header;
+       struct rtp_payload *payload;
+       struct bluetooth_a2dp *a2dp = &data->a2dp;
+
+       header = (void *) a2dp->buffer;
+       payload = (void *) (a2dp->buffer + sizeof(*header));
+
+       memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
+
+       payload->frame_count = a2dp->frame_count;
+       header->v = 2;
+       header->pt = 1;
+       header->sequence_number = htons(a2dp->seq_num);
+       header->timestamp = htonl(a2dp->nsamples);
+       header->ssrc = htonl(1);
+
+       err = send(data->stream.fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT);
+       if (err < 0) {
+               err = -errno;
+               DBG("send failed: %s (%d)", strerror(-err), -err);
+       }
+
+       /* Reset buffer of data to send */
+       a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
+       a2dp->frame_count = 0;
+       a2dp->samples = 0;
+       a2dp->seq_num++;
+
+       return err;
+}
+
+static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,
+                               const snd_pcm_channel_area_t *areas,
+                               snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
+{
+       struct bluetooth_data *data = io->private_data;
+       struct bluetooth_a2dp *a2dp = &data->a2dp;
+       snd_pcm_sframes_t ret = 0;
+       unsigned int bytes_left;
+       int frame_size, encoded;
+       ssize_t written;
+       uint8_t *buff;
+
+       DBG("areas->step=%u areas->first=%u offset=%lu size=%lu",
+                               areas->step, areas->first, offset, size);
+       DBG("hw_ptr=%lu appl_ptr=%lu diff=%lu", io->hw_ptr, io->appl_ptr,
+                       io->appl_ptr - io->hw_ptr);
+
+       /* Calutate starting pointers */
+       frame_size = areas->step / 8;
+       bytes_left = size * frame_size;
+       buff = (uint8_t *) areas->addr +
+                               (areas->first + areas->step * (offset)) / 8;
+
+       /* Check for underrun */
+       if (io->hw_ptr > io->appl_ptr) {
+               ret = bluetooth_playback_stop(io);
+               if (ret == 0)
+                       ret = -EPIPE;
+               data->reset = 1;
+               return ret;
+       }
+
+       /* Check if we should autostart */
+       if (io->state == SND_PCM_STATE_PREPARED) {
+               snd_pcm_sw_params_t *swparams;
+               snd_pcm_uframes_t threshold;
+
+               snd_pcm_sw_params_malloc(&swparams);
+               if (!snd_pcm_sw_params_current(io->pcm, swparams) &&
+                               !snd_pcm_sw_params_get_start_threshold(swparams,
+                                                               &threshold)) {
+                       if (io->appl_ptr >= threshold) {
+                               ret = snd_pcm_start(io->pcm);
+                               if (ret != 0)
+                                       return ret;
+                       }
+               }
+
+               snd_pcm_sw_params_free(swparams);
+       }
+
+       /* Check if we have any left over data from the last write */
+       if (data->count > 0) {
+               unsigned int additional_bytes_needed =
+                                               a2dp->codesize - data->count;
+               if (additional_bytes_needed > bytes_left)
+                       goto out;
+
+               memcpy(data->buffer + data->count, buff,
+                                               additional_bytes_needed);
+
+               /* Enough data to encode (sbc wants 1k blocks) */
+               encoded = sbc_encode(&a2dp->sbc, data->buffer, a2dp->codesize,
+                                       a2dp->buffer + a2dp->count,
+                                       sizeof(a2dp->buffer) - a2dp->count,
+                                                               &written);
+               if (encoded <= 0) {
+                       DBG("Encoding error %d", encoded);
+                       goto done;
+               }
+
+               /* Increment a2dp buffers */
+               a2dp->count += written;
+               a2dp->frame_count++;
+               a2dp->samples += encoded / frame_size;
+               a2dp->nsamples += encoded / frame_size;
+
+               /* No space left for another frame then send */
+               if (a2dp->count + written >= data->link_mtu) {
+                       avdtp_write(data);
+                       DBG("sending packet %d, count %d, link_mtu %u",
+                                       a2dp->seq_num, a2dp->count,
+                                                       data->link_mtu);
+               }
+
+               /* Increment up buff pointer to take into account
+                * the data processed */
+               buff += additional_bytes_needed;
+               bytes_left -= additional_bytes_needed;
+
+               /* Since data has been process mark it as zero */
+               data->count = 0;
+       }
+
+
+       /* Process this buffer in full chunks */
+       while (bytes_left >= a2dp->codesize) {
+               /* Enough data to encode (sbc wants 1k blocks) */
+               encoded = sbc_encode(&a2dp->sbc, buff, a2dp->codesize,
+                                       a2dp->buffer + a2dp->count,
+                                       sizeof(a2dp->buffer) - a2dp->count,
+                                                               &written);
+               if (encoded <= 0) {
+                       DBG("Encoding error %d", encoded);
+                       goto done;
+               }
+
+               /* Increment up buff pointer to take into account
+                * the data processed */
+               buff += a2dp->codesize;
+               bytes_left -= a2dp->codesize;
+
+               /* Increment a2dp buffers */
+               a2dp->count += written;
+               a2dp->frame_count++;
+               a2dp->samples += encoded / frame_size;
+               a2dp->nsamples += encoded / frame_size;
+
+               /* No space left for another frame then send */
+               if (a2dp->count + written >= data->link_mtu) {
+                       avdtp_write(data);
+                       DBG("sending packet %d, count %d, link_mtu %u",
+                                               a2dp->seq_num, a2dp->count,
+                                                       data->link_mtu);
+               }
+       }
+
+out:
+       /* Copy the extra to our temp buffer for the next write */
+       if (bytes_left > 0) {
+               memcpy(data->buffer + data->count, buff, bytes_left);
+               data->count += bytes_left;
+               bytes_left = 0;
+       }
+
+done:
+       DBG("returning %ld", size - bytes_left / frame_size);
+
+       return size - bytes_left / frame_size;
+}
+
+static int bluetooth_playback_delay(snd_pcm_ioplug_t *io,
+                                       snd_pcm_sframes_t *delayp)
+{
+       DBG("");
+
+       /* This updates io->hw_ptr value using pointer() function */
+       snd_pcm_hwsync(io->pcm);
+
+       *delayp = io->appl_ptr - io->hw_ptr;
+       if ((io->state == SND_PCM_STATE_RUNNING) && (*delayp < 0)) {
+               io->callback->stop(io);
+               io->state = SND_PCM_STATE_XRUN;
+               *delayp = 0;
+       }
+
+       /* This should never fail, ALSA API is really not
+       prepared to handle a non zero return value */
+       return 0;
+}
+
+static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = {
+       .start                  = bluetooth_playback_start,
+       .stop                   = bluetooth_playback_stop,
+       .pointer                = bluetooth_pointer,
+       .close                  = bluetooth_close,
+       .hw_params              = bluetooth_hsp_hw_params,
+       .prepare                = bluetooth_prepare,
+       .transfer               = bluetooth_hsp_write,
+       .poll_descriptors_count = bluetooth_playback_poll_descriptors_count,
+       .poll_descriptors       = bluetooth_playback_poll_descriptors,
+       .poll_revents           = bluetooth_playback_poll_revents,
+       .delay                  = bluetooth_playback_delay,
+};
+
+static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = {
+       .start                  = bluetooth_start,
+       .stop                   = bluetooth_stop,
+       .pointer                = bluetooth_pointer,
+       .close                  = bluetooth_close,
+       .hw_params              = bluetooth_hsp_hw_params,
+       .prepare                = bluetooth_prepare,
+       .transfer               = bluetooth_hsp_read,
+       .poll_descriptors       = bluetooth_poll_descriptors,
+       .poll_revents           = bluetooth_poll_revents,
+};
+
+static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = {
+       .start                  = bluetooth_playback_start,
+       .stop                   = bluetooth_playback_stop,
+       .pointer                = bluetooth_pointer,
+       .close                  = bluetooth_close,
+       .hw_params              = bluetooth_a2dp_hw_params,
+       .prepare                = bluetooth_prepare,
+       .transfer               = bluetooth_a2dp_write,
+       .poll_descriptors_count = bluetooth_playback_poll_descriptors_count,
+       .poll_descriptors       = bluetooth_playback_poll_descriptors,
+       .poll_revents           = bluetooth_playback_poll_revents,
+       .delay                  = bluetooth_playback_delay,
+};
+
+static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = {
+       .start                  = bluetooth_start,
+       .stop                   = bluetooth_stop,
+       .pointer                = bluetooth_pointer,
+       .close                  = bluetooth_close,
+       .hw_params              = bluetooth_a2dp_hw_params,
+       .prepare                = bluetooth_prepare,
+       .transfer               = bluetooth_a2dp_read,
+       .poll_descriptors       = bluetooth_poll_descriptors,
+       .poll_revents           = bluetooth_poll_revents,
+};
+
+#define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0]))
+
+static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io)
+{
+       struct bluetooth_data *data = io->private_data;
+       snd_pcm_access_t access_list[] = {
+               SND_PCM_ACCESS_RW_INTERLEAVED,
+               /* Mmap access is really useless fo this driver, but we
+                * support it because some pieces of software out there
+                * insist on using it */
+               SND_PCM_ACCESS_MMAP_INTERLEAVED
+       };
+       unsigned int format_list[] = {
+               SND_PCM_FORMAT_S16
+       };
+       int err;
+
+       /* access type */
+       err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
+                                       ARRAY_NELEMS(access_list), access_list);
+       if (err < 0)
+               return err;
+
+       /* supported formats */
+       err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
+                                       ARRAY_NELEMS(format_list), format_list);
+       if (err < 0)
+               return err;
+
+       /* supported channels */
+       err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
+                                                       1, 1);
+       if (err < 0)
+               return err;
+
+       /* supported rate */
+       err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE,
+                                                       8000, 8000);
+       if (err < 0)
+               return err;
+
+       /* supported block size */
+       err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
+                                               data->link_mtu, data->link_mtu);
+       if (err < 0)
+               return err;
+
+       err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS,
+                                                                       2, 200);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io)
+{
+       struct bluetooth_data *data = io->private_data;
+       struct bluetooth_a2dp *a2dp = &data->a2dp;
+       struct bluetooth_alsa_config *cfg = &data->alsa_config;
+       snd_pcm_access_t access_list[] = {
+               SND_PCM_ACCESS_RW_INTERLEAVED,
+               /* Mmap access is really useless fo this driver, but we
+                * support it because some pieces of software out there
+                * insist on using it */
+               SND_PCM_ACCESS_MMAP_INTERLEAVED
+       };
+       unsigned int format_list[] = {
+               SND_PCM_FORMAT_S16
+       };
+       unsigned int rate_list[4];
+       unsigned int rate_count;
+       int err, min_channels, max_channels;
+       unsigned int period_list[] = {
+               2048,
+               4096, /* e.g. 23.2msec/period (stereo 16bit at 44.1kHz) */
+               8192
+       };
+
+       /* access type */
+       err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
+                                       ARRAY_NELEMS(access_list), access_list);
+       if (err < 0)
+               return err;
+
+       /* supported formats */
+       err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
+                                       ARRAY_NELEMS(format_list), format_list);
+       if (err < 0)
+               return err;
+
+       /* supported channels */
+       if (cfg->has_channel_mode)
+               a2dp->sbc_capabilities.channel_mode = cfg->channel_mode;
+
+       if (a2dp->sbc_capabilities.channel_mode &
+                       BT_A2DP_CHANNEL_MODE_MONO)
+               min_channels = 1;
+       else
+               min_channels = 2;
+
+       if (a2dp->sbc_capabilities.channel_mode &
+                       (~BT_A2DP_CHANNEL_MODE_MONO))
+               max_channels = 2;
+       else
+               max_channels = 1;
+
+       err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
+                                                       min_channels, max_channels);
+       if (err < 0)
+               return err;
+
+       /* supported buffer sizes
+        * (can be used as 3*8192, 6*4096, 12*2048, ...) */
+       err = snd_pcm_ioplug_set_param_minmax(io,
+                                               SND_PCM_IOPLUG_HW_BUFFER_BYTES,
+                                               8192*3, 8192*3);
+       if (err < 0)
+               return err;
+
+       /* supported block sizes: */
+       err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
+                               ARRAY_NELEMS(period_list), period_list);
+       if (err < 0)
+               return err;
+
+       /* supported rates */
+       rate_count = 0;
+       if (cfg->has_rate) {
+               rate_list[rate_count] = cfg->rate;
+               rate_count++;
+       } else {
+               if (a2dp->sbc_capabilities.frequency &
+                               BT_SBC_SAMPLING_FREQ_16000) {
+                       rate_list[rate_count] = 16000;
+                       rate_count++;
+               }
+
+               if (a2dp->sbc_capabilities.frequency &
+                               BT_SBC_SAMPLING_FREQ_32000) {
+                       rate_list[rate_count] = 32000;
+                       rate_count++;
+               }
+
+               if (a2dp->sbc_capabilities.frequency &
+                               BT_SBC_SAMPLING_FREQ_44100) {
+                       rate_list[rate_count] = 44100;
+                       rate_count++;
+               }
+
+               if (a2dp->sbc_capabilities.frequency &
+                               BT_SBC_SAMPLING_FREQ_48000) {
+                       rate_list[rate_count] = 48000;
+                       rate_count++;
+               }
+       }
+
+       err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE,
+                                               rate_count, rate_list);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int bluetooth_parse_config(snd_config_t *conf,
+                               struct bluetooth_alsa_config *bt_config)
+{
+       snd_config_iterator_t i, next;
+
+       memset(bt_config, 0, sizeof(struct bluetooth_alsa_config));
+
+       /* Set defaults */
+       bt_config->autoconnect = 1;
+
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id, *value;
+
+               if (snd_config_get_id(n, &id) < 0)
+                       continue;
+
+               if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
+                       continue;
+
+               if (strcmp(id, "autoconnect") == 0) {
+                       int b;
+
+                       b = snd_config_get_bool(n);
+                       if (b < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+
+                       bt_config->autoconnect = b;
+                       continue;
+               }
+
+               if (strcmp(id, "device") == 0 || strcmp(id, "bdaddr") == 0) {
+                       if (snd_config_get_string(n, &value) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+
+                       bt_config->has_device = 1;
+                       strncpy(bt_config->device, value, 18);
+                       continue;
+               }
+
+               if (strcmp(id, "profile") == 0) {
+                       if (snd_config_get_string(n, &value) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+
+                       if (strcmp(value, "auto") == 0) {
+                               bt_config->transport = BT_CAPABILITIES_TRANSPORT_ANY;
+                               bt_config->has_transport = 1;
+                       } else if (strcmp(value, "voice") == 0 ||
+                                               strcmp(value, "hfp") == 0) {
+                               bt_config->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+                               bt_config->has_transport = 1;
+                       } else if (strcmp(value, "hifi") == 0 ||
+                                               strcmp(value, "a2dp") == 0) {
+                               bt_config->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+                               bt_config->has_transport = 1;
+                       }
+                       continue;
+               }
+
+               if (strcmp(id, "rate") == 0) {
+                       if (snd_config_get_string(n, &value) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+
+                       bt_config->rate = atoi(value);
+                       bt_config->has_rate = 1;
+                       continue;
+               }
+
+               if (strcmp(id, "mode") == 0) {
+                       if (snd_config_get_string(n, &value) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+
+                       if (strcmp(value, "mono") == 0) {
+                               bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+                               bt_config->has_channel_mode = 1;
+                       } else if (strcmp(value, "dual") == 0) {
+                               bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+                               bt_config->has_channel_mode = 1;
+                       } else if (strcmp(value, "stereo") == 0) {
+                               bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+                               bt_config->has_channel_mode = 1;
+                       } else if (strcmp(value, "joint") == 0) {
+                               bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+                               bt_config->has_channel_mode = 1;
+                       }
+                       continue;
+               }
+
+               if (strcmp(id, "allocation") == 0) {
+                       if (snd_config_get_string(n, &value) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+
+                       if (strcmp(value, "loudness") == 0) {
+                               bt_config->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+                               bt_config->has_allocation_method = 1;
+                       } else if (strcmp(value, "snr") == 0) {
+                               bt_config->allocation_method = BT_A2DP_ALLOCATION_SNR;
+                               bt_config->has_allocation_method = 1;
+                       }
+                       continue;
+               }
+
+               if (strcmp(id, "subbands") == 0) {
+                       if (snd_config_get_string(n, &value) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+
+                       bt_config->subbands = atoi(value);
+                       bt_config->has_subbands = 1;
+                       continue;
+               }
+
+               if (strcmp(id, "blocks") == 0) {
+                       if (snd_config_get_string(n, &value) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+
+                       bt_config->block_length = atoi(value);
+                       bt_config->has_block_length = 1;
+                       continue;
+               }
+
+               if (strcmp(id, "bitpool") == 0) {
+                       if (snd_config_get_string(n, &value) < 0) {
+                               SNDERR("Invalid type for %s", id);
+                               return -EINVAL;
+                       }
+
+                       bt_config->bitpool = atoi(value);
+                       bt_config->has_bitpool = 1;
+                       continue;
+               }
+
+               SNDERR("Unknown field %s", id);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int audioservice_send(int sk, const bt_audio_msg_header_t *msg)
+{
+       int err;
+       uint16_t length;
+
+       length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
+
+       DBG("sending %s:%s", bt_audio_strtype(msg->type),
+               bt_audio_strname(msg->name));
+       if (send(sk, msg, length, 0) > 0)
+               err = 0;
+       else {
+               err = -errno;
+               SNDERR("Error sending data to audio service: %s(%d)",
+                       strerror(-err), -err);
+       }
+
+       return err;
+}
+
+static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg)
+{
+       int err;
+       ssize_t ret;
+       const char *type, *name;
+       uint16_t length;
+
+       length = inmsg->length ? inmsg->length : BT_SUGGESTED_BUFFER_SIZE;
+
+       DBG("trying to receive msg from audio service...");
+
+       ret = recv(sk, inmsg, length, 0);
+       if (ret < 0) {
+               err = -errno;
+               SNDERR("Error receiving IPC data from bluetoothd: %s (%d)",
+                                               strerror(-err), -err);
+       } else if ((size_t) ret < sizeof(bt_audio_msg_header_t)) {
+               SNDERR("Too short (%d bytes) IPC packet from bluetoothd", ret);
+               err = -EINVAL;
+       } else {
+               type = bt_audio_strtype(inmsg->type);
+               name = bt_audio_strname(inmsg->name);
+               if (type && name) {
+                       DBG("Received %s - %s", type, name);
+                       err = 0;
+               } else {
+                       err = -EINVAL;
+                       SNDERR("Bogus message type %d - name %d"
+                                       " received from audio service",
+                                       inmsg->type, inmsg->name);
+               }
+
+       }
+
+       return err;
+}
+
+static int audioservice_expect(int sk, bt_audio_msg_header_t *rsp,
+                                                       int expected_name)
+{
+       bt_audio_error_t *error;
+       int err = audioservice_recv(sk, rsp);
+
+       if (err != 0)
+               return err;
+
+       if (rsp->name != expected_name) {
+               err = -EINVAL;
+               SNDERR("Bogus message %s received while %s was expected",
+                               bt_audio_strname(rsp->name),
+                               bt_audio_strname(expected_name));
+       }
+
+       if (rsp->type == BT_ERROR) {
+               error = (void *) rsp;
+               SNDERR("%s failed : %s(%d)",
+                                       bt_audio_strname(rsp->name),
+                                       strerror(error->posix_errno),
+                                       error->posix_errno);
+               return -error->posix_errno;
+       }
+
+       return err;
+}
+
+static int bluetooth_parse_capabilities(struct bluetooth_data *data,
+                                       struct bt_get_capabilities_rsp *rsp)
+{
+       int bytes_left = rsp->h.length - sizeof(*rsp);
+       codec_capabilities_t *codec = (void *) rsp->data;
+
+       data->transport = codec->transport;
+
+       if (codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP)
+               return 0;
+
+       while (bytes_left > 0) {
+               if ((codec->type == BT_A2DP_SBC_SINK) &&
+                               !(codec->lock & BT_WRITE_LOCK))
+                       break;
+
+               bytes_left -= codec->length;
+               codec = (void *) codec + codec->length;
+       }
+
+       if (bytes_left <= 0 ||
+                       codec->length != sizeof(data->a2dp.sbc_capabilities))
+               return -EINVAL;
+
+       memcpy(&data->a2dp.sbc_capabilities, codec, codec->length);
+
+       return 0;
+}
+
+static int bluetooth_init(struct bluetooth_data *data,
+                               snd_pcm_stream_t stream, snd_config_t *conf)
+{
+       int sk, err;
+       struct bluetooth_alsa_config *alsa_conf = &data->alsa_config;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_get_capabilities_req *req = (void *) buf;
+       struct bt_get_capabilities_rsp *rsp = (void *) buf;
+
+       memset(data, 0, sizeof(struct bluetooth_data));
+
+       err = bluetooth_parse_config(conf, alsa_conf);
+       if (err < 0)
+               return err;
+
+       data->server.fd = -1;
+       data->stream.fd = -1;
+
+       sk = bt_audio_service_open();
+       if (sk < 0) {
+               err = -errno;
+               goto failed;
+       }
+
+       data->server.fd = sk;
+       data->server.events = POLLIN;
+
+       data->pipefd[0] = -1;
+       data->pipefd[1] = -1;
+
+       if (pipe(data->pipefd) < 0) {
+               err = -errno;
+               goto failed;
+       }
+       if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0) {
+               err = -errno;
+               goto failed;
+       }
+       if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) {
+               err = -errno;
+               goto failed;
+       }
+
+       memset(req, 0, BT_SUGGESTED_BUFFER_SIZE);
+       req->h.type = BT_REQUEST;
+       req->h.name = BT_GET_CAPABILITIES;
+       req->h.length = sizeof(*req);
+
+       if (alsa_conf->autoconnect)
+               req->flags |= BT_FLAG_AUTOCONNECT;
+       strncpy(req->destination, alsa_conf->device, 18);
+       if (alsa_conf->has_transport)
+               req->transport = alsa_conf->transport;
+       else
+               req->transport = BT_CAPABILITIES_TRANSPORT_ANY;
+
+       err = audioservice_send(data->server.fd, &req->h);
+       if (err < 0)
+               goto failed;
+
+       rsp->h.length = 0;
+       err = audioservice_expect(data->server.fd, &rsp->h,
+                                       BT_GET_CAPABILITIES);
+       if (err < 0)
+               goto failed;
+
+       bluetooth_parse_capabilities(data, rsp);
+
+       return 0;
+
+failed:
+       if (sk >= 0)
+               bt_audio_service_close(sk);
+       return err;
+}
+
+SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth);
+
+SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth)
+{
+       struct bluetooth_data *data;
+       int err;
+
+       DBG("Bluetooth PCM plugin (%s)",
+               stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture");
+
+       data = malloc(sizeof(struct bluetooth_data));
+       if (!data) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       err = bluetooth_init(data, stream, conf);
+       if (err < 0)
+               goto error;
+
+       data->io.version = SND_PCM_IOPLUG_VERSION;
+       data->io.name = "Bluetooth Audio Device";
+       data->io.mmap_rw = 0; /* No direct mmap communication */
+       data->io.private_data = data;
+
+       if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+               data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?
+                       &bluetooth_a2dp_playback :
+                       &bluetooth_a2dp_capture;
+       else
+               data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ?
+                       &bluetooth_hsp_playback :
+                       &bluetooth_hsp_capture;
+
+       err = snd_pcm_ioplug_create(&data->io, name, stream, mode);
+       if (err < 0)
+               goto error;
+
+       if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+               err = bluetooth_a2dp_hw_constraint(&data->io);
+       else
+               err = bluetooth_hsp_hw_constraint(&data->io);
+
+       if (err < 0) {
+               snd_pcm_ioplug_delete(&data->io);
+               goto error;
+       }
+
+       *pcmp = data->io.pcm;
+
+       return 0;
+
+error:
+       if (data)
+               bluetooth_exit(data);
+
+       return err;
+}
+
+SND_PCM_PLUGIN_SYMBOL(bluetooth);
diff --git a/audio/rtp.h b/audio/rtp.h
new file mode 100644 (file)
index 0000000..45fddcf
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct rtp_header {
+       unsigned cc:4;
+       unsigned x:1;
+       unsigned p:1;
+       unsigned v:2;
+
+       unsigned pt:7;
+       unsigned m:1;
+
+       uint16_t sequence_number;
+       uint32_t timestamp;
+       uint32_t ssrc;
+       uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+       unsigned frame_count:4;
+       unsigned rfa0:1;
+       unsigned is_last_fragment:1;
+       unsigned is_first_fragment:1;
+       unsigned is_fragmented:1;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct rtp_header {
+       unsigned v:2;
+       unsigned p:1;
+       unsigned x:1;
+       unsigned cc:4;
+
+       unsigned m:1;
+       unsigned pt:7;
+
+       uint16_t sequence_number;
+       uint32_t timestamp;
+       uint32_t ssrc;
+       uint32_t csrc[0];
+} __attribute__ ((packed));
+
+struct rtp_payload {
+       unsigned is_fragmented:1;
+       unsigned is_first_fragment:1;
+       unsigned is_last_fragment:1;
+       unsigned rfa0:1;
+       unsigned frame_count:4;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
diff --git a/audio/sink.c b/audio/sink.c
new file mode 100644 (file)
index 0000000..6b21e47
--- /dev/null
@@ -0,0 +1,745 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+#include "error.h"
+#include "sink.h"
+#include "dbus-common.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#define STREAM_SETUP_RETRY_TIMER 2
+
+struct pending_request {
+       DBusConnection *conn;
+       DBusMessage *msg;
+       unsigned int id;
+};
+
+struct sink {
+       struct audio_device *dev;
+       struct avdtp *session;
+       struct avdtp_stream *stream;
+       unsigned int cb_id;
+       guint retry_id;
+       avdtp_session_state_t session_state;
+       avdtp_state_t stream_state;
+       sink_state_t state;
+       struct pending_request *connect;
+       struct pending_request *disconnect;
+       DBusConnection *conn;
+};
+
+struct sink_state_callback {
+       sink_state_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+static GSList *sink_callbacks = NULL;
+
+static unsigned int avdtp_callback_id = 0;
+
+static char *str_state[] = {
+       "SINK_STATE_DISCONNECTED",
+       "SINK_STATE_CONNECTING",
+       "SINK_STATE_CONNECTED",
+       "SINK_STATE_PLAYING",
+};
+
+static const char *state2str(sink_state_t state)
+{
+       switch (state) {
+       case SINK_STATE_DISCONNECTED:
+               return "disconnected";
+       case SINK_STATE_CONNECTING:
+               return "connecting";
+       case SINK_STATE_CONNECTED:
+               return "connected";
+       case SINK_STATE_PLAYING:
+               return "playing";
+       default:
+               error("Invalid sink state %d", state);
+               return NULL;
+       }
+}
+
+static void sink_set_state(struct audio_device *dev, sink_state_t new_state)
+{
+       struct sink *sink = dev->sink;
+       const char *state_str;
+       sink_state_t old_state = sink->state;
+       GSList *l;
+
+       sink->state = new_state;
+
+       state_str = state2str(new_state);
+       if (state_str)
+               emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_SINK_INTERFACE, "State",
+                                       DBUS_TYPE_STRING, &state_str);
+
+       DBG("State changed %s: %s -> %s", dev->path, str_state[old_state],
+               str_state[new_state]);
+
+       for (l = sink_callbacks; l != NULL; l = l->next) {
+               struct sink_state_callback *cb = l->data;
+               cb->cb(dev, old_state, new_state, cb->user_data);
+       }
+}
+
+static void avdtp_state_callback(struct audio_device *dev,
+                                       struct avdtp *session,
+                                       avdtp_session_state_t old_state,
+                                       avdtp_session_state_t new_state,
+                                       void *user_data)
+{
+       struct sink *sink = dev->sink;
+
+       if (sink == NULL)
+               return;
+
+       switch (new_state) {
+       case AVDTP_SESSION_STATE_DISCONNECTED:
+               if (sink->state != SINK_STATE_CONNECTING) {
+                       gboolean value = FALSE;
+                       g_dbus_emit_signal(dev->conn, dev->path,
+                                       AUDIO_SINK_INTERFACE, "Disconnected",
+                                       DBUS_TYPE_INVALID);
+                       emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_SINK_INTERFACE, "Connected",
+                                       DBUS_TYPE_BOOLEAN, &value);
+               }
+               sink_set_state(dev, SINK_STATE_DISCONNECTED);
+               break;
+       case AVDTP_SESSION_STATE_CONNECTING:
+               sink_set_state(dev, SINK_STATE_CONNECTING);
+               break;
+       case AVDTP_SESSION_STATE_CONNECTED:
+               break;
+       }
+
+       sink->session_state = new_state;
+}
+
+static void pending_request_free(struct audio_device *dev,
+                                       struct pending_request *pending)
+{
+       if (pending->conn)
+               dbus_connection_unref(pending->conn);
+       if (pending->msg)
+               dbus_message_unref(pending->msg);
+       if (pending->id)
+               a2dp_cancel(dev, pending->id);
+
+       g_free(pending);
+}
+
+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 audio_device *dev = user_data;
+       struct sink *sink = dev->sink;
+       gboolean value;
+
+       if (err)
+               return;
+
+       switch (new_state) {
+       case AVDTP_STATE_IDLE:
+               if (sink->disconnect) {
+                       DBusMessage *reply;
+                       struct pending_request *p;
+
+                       p = sink->disconnect;
+                       sink->disconnect = NULL;
+
+                       reply = dbus_message_new_method_return(p->msg);
+                       g_dbus_send_message(p->conn, reply);
+                       pending_request_free(dev, p);
+               }
+
+               if (sink->session) {
+                       avdtp_unref(sink->session);
+                       sink->session = NULL;
+               }
+               sink->stream = NULL;
+               sink->cb_id = 0;
+               break;
+       case AVDTP_STATE_OPEN:
+               if (old_state == AVDTP_STATE_CONFIGURED &&
+                               sink->state == SINK_STATE_CONNECTING) {
+                       value = TRUE;
+                       g_dbus_emit_signal(dev->conn, dev->path,
+                                               AUDIO_SINK_INTERFACE,
+                                               "Connected",
+                                               DBUS_TYPE_INVALID);
+                       emit_property_changed(dev->conn, dev->path,
+                                               AUDIO_SINK_INTERFACE,
+                                               "Connected",
+                                               DBUS_TYPE_BOOLEAN, &value);
+               } else if (old_state == AVDTP_STATE_STREAMING) {
+                       value = FALSE;
+                       g_dbus_emit_signal(dev->conn, dev->path,
+                                               AUDIO_SINK_INTERFACE,
+                                               "Stopped",
+                                               DBUS_TYPE_INVALID);
+                       emit_property_changed(dev->conn, dev->path,
+                                               AUDIO_SINK_INTERFACE,
+                                               "Playing",
+                                               DBUS_TYPE_BOOLEAN, &value);
+               }
+               sink_set_state(dev, SINK_STATE_CONNECTED);
+               break;
+       case AVDTP_STATE_STREAMING:
+               value = TRUE;
+               g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE,
+                                       "Playing", DBUS_TYPE_INVALID);
+               emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_SINK_INTERFACE, "Playing",
+                                       DBUS_TYPE_BOOLEAN, &value);
+               sink_set_state(dev, SINK_STATE_PLAYING);
+               break;
+       case AVDTP_STATE_CONFIGURED:
+       case AVDTP_STATE_CLOSING:
+       case AVDTP_STATE_ABORTING:
+       default:
+               break;
+       }
+
+       sink->stream_state = new_state;
+}
+
+static void error_failed(DBusConnection *conn, DBusMessage *msg,
+                                                       const char *desc)
+{
+       DBusMessage *reply = btd_error_failed(msg, desc);
+       g_dbus_send_message(conn, reply);
+}
+
+static gboolean stream_setup_retry(gpointer user_data)
+{
+       struct sink *sink = user_data;
+       struct pending_request *pending = sink->connect;
+
+       sink->retry_id = 0;
+
+       if (sink->stream_state >= AVDTP_STATE_OPEN) {
+               DBG("Stream successfully created, after XCASE connect:connect");
+               if (pending->msg) {
+                       DBusMessage *reply;
+                       reply = dbus_message_new_method_return(pending->msg);
+                       g_dbus_send_message(pending->conn, reply);
+               }
+       } else {
+               DBG("Stream setup failed, after XCASE connect:connect");
+               if (pending->msg)
+                       error_failed(pending->conn, pending->msg, "Stream setup failed");
+       }
+
+       sink->connect = NULL;
+       pending_request_free(sink->dev, pending);
+
+       return FALSE;
+}
+
+static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_error *err, void *user_data)
+{
+       struct sink *sink = user_data;
+       struct pending_request *pending;
+
+       pending = sink->connect;
+
+       pending->id = 0;
+
+       if (stream) {
+               DBG("Stream successfully created");
+
+               if (pending->msg) {
+                       DBusMessage *reply;
+                       reply = dbus_message_new_method_return(pending->msg);
+                       g_dbus_send_message(pending->conn, reply);
+               }
+
+               sink->connect = NULL;
+               pending_request_free(sink->dev, pending);
+
+               return;
+       }
+
+       avdtp_unref(sink->session);
+       sink->session = NULL;
+       if (avdtp_error_category(err) == AVDTP_ERRNO
+                       && avdtp_error_posix_errno(err) != EHOSTDOWN) {
+               DBG("connect:connect XCASE detected");
+               sink->retry_id = g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER,
+                                                       stream_setup_retry,
+                                                       sink);
+       } else {
+               if (pending->msg)
+                       error_failed(pending->conn, pending->msg, "Stream setup failed");
+               sink->connect = NULL;
+               pending_request_free(sink->dev, pending);
+               DBG("Stream setup failed : %s", avdtp_strerror(err));
+       }
+}
+
+static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
+                       GSList *caps, void *user_data)
+{
+       struct sink *sink = user_data;
+       struct pending_request *pending;
+       int id;
+
+       pending = sink->connect;
+       pending->id = 0;
+
+       id = a2dp_config(session, sep, stream_setup_complete, caps, sink);
+       if (id == 0)
+               goto failed;
+
+       pending->id = id;
+       return;
+
+failed:
+       if (pending->msg)
+               error_failed(pending->conn, pending->msg, "Stream setup failed");
+       pending_request_free(sink->dev, pending);
+       sink->connect = NULL;
+       avdtp_unref(sink->session);
+       sink->session = NULL;
+}
+
+static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
+                               void *user_data)
+{
+       struct sink *sink = user_data;
+       struct pending_request *pending;
+       int id;
+
+       if (!sink->connect) {
+               avdtp_unref(sink->session);
+               sink->session = NULL;
+               return;
+       }
+
+       pending = sink->connect;
+
+       if (err) {
+               avdtp_unref(sink->session);
+               sink->session = NULL;
+               if (avdtp_error_category(err) == AVDTP_ERRNO
+                               && avdtp_error_posix_errno(err) != EHOSTDOWN) {
+                       DBG("connect:connect XCASE detected");
+                       sink->retry_id =
+                               g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER,
+                                                       stream_setup_retry,
+                                                       sink);
+               } else
+                       goto failed;
+               return;
+       }
+
+       DBG("Discovery complete");
+
+       id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL,
+                                               select_complete, sink);
+       if (id == 0)
+               goto failed;
+
+       pending->id = id;
+       return;
+
+failed:
+       if (pending->msg)
+               error_failed(pending->conn, pending->msg, "Stream setup failed");
+       pending_request_free(sink->dev, pending);
+       sink->connect = NULL;
+       avdtp_unref(sink->session);
+       sink->session = NULL;
+}
+
+gboolean sink_setup_stream(struct sink *sink, struct avdtp *session)
+{
+       if (sink->connect || sink->disconnect)
+               return FALSE;
+
+       if (session && !sink->session)
+               sink->session = avdtp_ref(session);
+
+       if (!sink->session)
+               return FALSE;
+
+       avdtp_set_auto_disconnect(sink->session, FALSE);
+
+       if (avdtp_discover(sink->session, discovery_complete, sink) < 0)
+               return FALSE;
+
+       sink->connect = g_new0(struct pending_request, 1);
+
+       return TRUE;
+}
+
+static DBusMessage *sink_connect(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct audio_device *dev = data;
+       struct sink *sink = dev->sink;
+       struct pending_request *pending;
+
+       if (!sink->session)
+               sink->session = avdtp_get(&dev->src, &dev->dst);
+
+       if (!sink->session)
+               return btd_error_failed(msg, "Unable to get a session");
+
+       if (sink->connect || sink->disconnect)
+               return btd_error_busy(msg);
+
+       if (sink->stream_state >= AVDTP_STATE_OPEN)
+               return btd_error_already_connected(msg);
+
+       if (!sink_setup_stream(sink, NULL))
+               return btd_error_failed(msg, "Failed to create a stream");
+
+       dev->auto_connect = FALSE;
+
+       pending = sink->connect;
+
+       pending->conn = dbus_connection_ref(conn);
+       pending->msg = dbus_message_ref(msg);
+
+       DBG("stream creation in progress");
+
+       return NULL;
+}
+
+static DBusMessage *sink_disconnect(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct audio_device *device = data;
+       struct sink *sink = device->sink;
+       struct pending_request *pending;
+       int err;
+
+       if (!sink->session)
+               return btd_error_not_connected(msg);
+
+       if (sink->connect || sink->disconnect)
+               return btd_error_busy(msg);
+
+       if (sink->stream_state < AVDTP_STATE_OPEN) {
+               DBusMessage *reply = dbus_message_new_method_return(msg);
+               if (!reply)
+                       return NULL;
+               avdtp_unref(sink->session);
+               sink->session = NULL;
+               return reply;
+       }
+
+       err = avdtp_close(sink->session, sink->stream, FALSE);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       pending = g_new0(struct pending_request, 1);
+       pending->conn = dbus_connection_ref(conn);
+       pending->msg = dbus_message_ref(msg);
+       sink->disconnect = pending;
+
+       return NULL;
+}
+
+static DBusMessage *sink_is_connected(DBusConnection *conn,
+                                       DBusMessage *msg,
+                                       void *data)
+{
+       struct audio_device *device = data;
+       struct sink *sink = device->sink;
+       DBusMessage *reply;
+       dbus_bool_t connected;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       connected = (sink->stream_state >= AVDTP_STATE_CONFIGURED);
+
+       dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected,
+                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *sink_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct audio_device *device = data;
+       struct sink *sink = device->sink;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *state;
+       gboolean value;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       /* Playing */
+       value = (sink->stream_state == AVDTP_STATE_STREAMING);
+       dict_append_entry(&dict, "Playing", DBUS_TYPE_BOOLEAN, &value);
+
+       /* Connected */
+       value = (sink->stream_state >= AVDTP_STATE_CONFIGURED);
+       dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value);
+
+       /* State */
+       state = state2str(sink->state);
+       if (state)
+               dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static const GDBusMethodTable sink_methods[] = {
+       { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, sink_connect) },
+       { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, sink_disconnect) },
+       { GDBUS_DEPRECATED_METHOD("IsConnected",
+                       NULL, GDBUS_ARGS({ "connected", "b" }),
+                       sink_is_connected) },
+       { GDBUS_METHOD("GetProperties",
+                               NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                               sink_get_properties) },
+       { }
+};
+
+static const GDBusSignalTable sink_signals[] = {
+       { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) },
+       { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) },
+       { GDBUS_DEPRECATED_SIGNAL("Playing", NULL) },
+       { GDBUS_DEPRECATED_SIGNAL("Stopped", NULL) },
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static void sink_free(struct audio_device *dev)
+{
+       struct sink *sink = dev->sink;
+
+       if (sink->cb_id)
+               avdtp_stream_remove_cb(sink->session, sink->stream,
+                                       sink->cb_id);
+
+       if (sink->session)
+               avdtp_unref(sink->session);
+
+       if (sink->connect)
+               pending_request_free(dev, sink->connect);
+
+       if (sink->disconnect)
+               pending_request_free(dev, sink->disconnect);
+
+       if (sink->retry_id)
+               g_source_remove(sink->retry_id);
+
+       g_free(sink);
+       dev->sink = NULL;
+}
+
+static void path_unregister(void *data)
+{
+       struct audio_device *dev = data;
+
+       DBG("Unregistered interface %s on path %s",
+               AUDIO_SINK_INTERFACE, dev->path);
+
+       sink_free(dev);
+}
+
+void sink_unregister(struct audio_device *dev)
+{
+       g_dbus_unregister_interface(dev->conn, dev->path,
+               AUDIO_SINK_INTERFACE);
+}
+
+struct sink *sink_init(struct audio_device *dev)
+{
+       struct sink *sink;
+
+       if (!g_dbus_register_interface(dev->conn, dev->path,
+                                       AUDIO_SINK_INTERFACE,
+                                       sink_methods, sink_signals, NULL,
+                                       dev, path_unregister))
+               return NULL;
+
+       DBG("Registered interface %s on path %s",
+               AUDIO_SINK_INTERFACE, dev->path);
+
+       if (avdtp_callback_id == 0)
+               avdtp_callback_id = avdtp_add_state_cb(avdtp_state_callback,
+                                                                       NULL);
+
+       sink = g_new0(struct sink, 1);
+
+       sink->dev = dev;
+
+       return sink;
+}
+
+gboolean sink_is_active(struct audio_device *dev)
+{
+       struct sink *sink = dev->sink;
+
+       if (sink->session)
+               return TRUE;
+
+       return FALSE;
+}
+
+sink_state_t sink_get_state(struct audio_device *dev)
+{
+       struct sink *sink = dev->sink;
+
+       return sink->state;
+}
+
+gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session,
+                               struct avdtp_stream *stream)
+{
+       struct sink *sink = dev->sink;
+
+       if (sink->stream)
+               return FALSE;
+
+       if (!sink->session)
+               sink->session = avdtp_ref(session);
+
+       sink->stream = stream;
+
+       sink->cb_id = avdtp_stream_add_cb(session, stream,
+                                               stream_state_changed, dev);
+
+       return TRUE;
+}
+
+gboolean sink_shutdown(struct sink *sink)
+{
+       if (!sink->session)
+               return FALSE;
+
+       avdtp_set_device_disconnect(sink->session, TRUE);
+
+       /* cancel pending connect */
+       if (sink->connect) {
+               struct pending_request *pending = sink->connect;
+
+               if (pending->msg)
+                       error_failed(pending->conn, pending->msg,
+                                                       "Stream setup failed");
+               pending_request_free(sink->dev, pending);
+               sink->connect = NULL;
+
+               avdtp_unref(sink->session);
+               sink->session = NULL;
+
+               return TRUE;
+       }
+
+       /* disconnect already ongoing */
+       if (sink->disconnect)
+               return TRUE;
+
+       if (!sink->stream)
+               return FALSE;
+
+       if (avdtp_close(sink->session, sink->stream, FALSE) < 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+unsigned int sink_add_state_cb(sink_state_cb cb, void *user_data)
+{
+       struct sink_state_callback *state_cb;
+       static unsigned int id = 0;
+
+       state_cb = g_new(struct sink_state_callback, 1);
+       state_cb->cb = cb;
+       state_cb->user_data = user_data;
+       state_cb->id = ++id;
+
+       sink_callbacks = g_slist_append(sink_callbacks, state_cb);
+
+       return state_cb->id;
+}
+
+gboolean sink_remove_state_cb(unsigned int id)
+{
+       GSList *l;
+
+       for (l = sink_callbacks; l != NULL; l = l->next) {
+               struct sink_state_callback *cb = l->data;
+               if (cb && cb->id == id) {
+                       sink_callbacks = g_slist_remove(sink_callbacks, cb);
+                       g_free(cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
diff --git a/audio/sink.h b/audio/sink.h
new file mode 100644 (file)
index 0000000..4fea5ba
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define AUDIO_SINK_INTERFACE "org.bluez.AudioSink"
+
+typedef enum {
+       SINK_STATE_DISCONNECTED,
+       SINK_STATE_CONNECTING,
+       SINK_STATE_CONNECTED,
+       SINK_STATE_PLAYING,
+} sink_state_t;
+
+typedef void (*sink_state_cb) (struct audio_device *dev,
+                               sink_state_t old_state,
+                               sink_state_t new_state,
+                               void *user_data);
+
+unsigned int sink_add_state_cb(sink_state_cb cb, void *user_data);
+gboolean sink_remove_state_cb(unsigned int id);
+
+struct sink *sink_init(struct audio_device *dev);
+void sink_unregister(struct audio_device *dev);
+gboolean sink_is_active(struct audio_device *dev);
+sink_state_t sink_get_state(struct audio_device *dev);
+gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session,
+                               struct avdtp_stream *stream);
+gboolean sink_setup_stream(struct sink *sink, struct avdtp *session);
+gboolean sink_shutdown(struct sink *sink);
diff --git a/audio/source.c b/audio/source.c
new file mode 100644 (file)
index 0000000..dbba5b9
--- /dev/null
@@ -0,0 +1,634 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009  Joao Paulo Rechi Vita
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+#include "error.h"
+#include "source.h"
+#include "dbus-common.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#define STREAM_SETUP_RETRY_TIMER 2
+
+struct pending_request {
+       DBusConnection *conn;
+       DBusMessage *msg;
+       unsigned int id;
+};
+
+struct source {
+       struct audio_device *dev;
+       struct avdtp *session;
+       struct avdtp_stream *stream;
+       unsigned int cb_id;
+       guint retry_id;
+       avdtp_session_state_t session_state;
+       avdtp_state_t stream_state;
+       source_state_t state;
+       struct pending_request *connect;
+       struct pending_request *disconnect;
+       DBusConnection *conn;
+};
+
+struct source_state_callback {
+       source_state_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+static GSList *source_callbacks = NULL;
+
+static unsigned int avdtp_callback_id = 0;
+
+static const char *state2str(source_state_t state)
+{
+       switch (state) {
+       case SOURCE_STATE_DISCONNECTED:
+               return "disconnected";
+       case SOURCE_STATE_CONNECTING:
+               return "connecting";
+       case SOURCE_STATE_CONNECTED:
+               return "connected";
+       case SOURCE_STATE_PLAYING:
+               return "playing";
+       default:
+               error("Invalid source state %d", state);
+               return NULL;
+       }
+}
+
+static void source_set_state(struct audio_device *dev, source_state_t new_state)
+{
+       struct source *source = dev->source;
+       const char *state_str;
+       source_state_t old_state = source->state;
+       GSList *l;
+
+       source->state = new_state;
+
+       state_str = state2str(new_state);
+       if (state_str)
+               emit_property_changed(dev->conn, dev->path,
+                                       AUDIO_SOURCE_INTERFACE, "State",
+                                       DBUS_TYPE_STRING, &state_str);
+
+       for (l = source_callbacks; l != NULL; l = l->next) {
+               struct source_state_callback *cb = l->data;
+               cb->cb(dev, old_state, new_state, cb->user_data);
+       }
+}
+
+static void avdtp_state_callback(struct audio_device *dev,
+                                       struct avdtp *session,
+                                       avdtp_session_state_t old_state,
+                                       avdtp_session_state_t new_state,
+                                       void *user_data)
+{
+       struct source *source = dev->source;
+
+       if (source == NULL)
+               return;
+
+       switch (new_state) {
+       case AVDTP_SESSION_STATE_DISCONNECTED:
+               source_set_state(dev, SOURCE_STATE_DISCONNECTED);
+               break;
+       case AVDTP_SESSION_STATE_CONNECTING:
+               source_set_state(dev, SOURCE_STATE_CONNECTING);
+               break;
+       case AVDTP_SESSION_STATE_CONNECTED:
+               break;
+       }
+
+       source->session_state = new_state;
+}
+
+static void pending_request_free(struct audio_device *dev,
+                                       struct pending_request *pending)
+{
+       if (pending->conn)
+               dbus_connection_unref(pending->conn);
+       if (pending->msg)
+               dbus_message_unref(pending->msg);
+       if (pending->id)
+               a2dp_cancel(dev, pending->id);
+
+       g_free(pending);
+}
+
+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 audio_device *dev = user_data;
+       struct source *source = dev->source;
+
+       if (err)
+               return;
+
+       switch (new_state) {
+       case AVDTP_STATE_IDLE:
+               if (source->disconnect) {
+                       DBusMessage *reply;
+                       struct pending_request *p;
+
+                       p = source->disconnect;
+                       source->disconnect = NULL;
+
+                       reply = dbus_message_new_method_return(p->msg);
+                       g_dbus_send_message(p->conn, reply);
+                       pending_request_free(dev, p);
+               }
+
+               if (source->session) {
+                       avdtp_unref(source->session);
+                       source->session = NULL;
+               }
+               source->stream = NULL;
+               source->cb_id = 0;
+               break;
+       case AVDTP_STATE_OPEN:
+               source_set_state(dev, SOURCE_STATE_CONNECTED);
+               break;
+       case AVDTP_STATE_STREAMING:
+               source_set_state(dev, SOURCE_STATE_PLAYING);
+               break;
+       case AVDTP_STATE_CONFIGURED:
+       case AVDTP_STATE_CLOSING:
+       case AVDTP_STATE_ABORTING:
+       default:
+               break;
+       }
+
+       source->stream_state = new_state;
+}
+
+static void error_failed(DBusConnection *conn, DBusMessage *msg,
+                                                       const char *desc)
+{
+       DBusMessage *reply = btd_error_failed(msg, desc);
+       g_dbus_send_message(conn, reply);
+}
+
+static gboolean stream_setup_retry(gpointer user_data)
+{
+       struct source *source = user_data;
+       struct pending_request *pending = source->connect;
+
+       source->retry_id = 0;
+
+       if (source->stream_state >= AVDTP_STATE_OPEN) {
+               DBG("Stream successfully created, after XCASE connect:connect");
+               if (pending->msg) {
+                       DBusMessage *reply;
+                       reply = dbus_message_new_method_return(pending->msg);
+                       g_dbus_send_message(pending->conn, reply);
+               }
+       } else {
+               DBG("Stream setup failed, after XCASE connect:connect");
+               if (pending->msg)
+                       error_failed(pending->conn, pending->msg, "Stream setup failed");
+       }
+
+       source->connect = NULL;
+       pending_request_free(source->dev, pending);
+
+       return FALSE;
+}
+
+static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_error *err, void *user_data)
+{
+       struct source *source = user_data;
+       struct pending_request *pending;
+
+       pending = source->connect;
+
+       pending->id = 0;
+
+       if (stream) {
+               DBG("Stream successfully created");
+
+               if (pending->msg) {
+                       DBusMessage *reply;
+                       reply = dbus_message_new_method_return(pending->msg);
+                       g_dbus_send_message(pending->conn, reply);
+               }
+
+               source->connect = NULL;
+               pending_request_free(source->dev, pending);
+
+               return;
+       }
+
+       avdtp_unref(source->session);
+       source->session = NULL;
+       if (avdtp_error_category(err) == AVDTP_ERRNO
+                       && avdtp_error_posix_errno(err) != EHOSTDOWN) {
+               DBG("connect:connect XCASE detected");
+               source->retry_id = g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER,
+                                                       stream_setup_retry,
+                                                       source);
+       } else {
+               if (pending->msg)
+                       error_failed(pending->conn, pending->msg, "Stream setup failed");
+               source->connect = NULL;
+               pending_request_free(source->dev, pending);
+               DBG("Stream setup failed : %s", avdtp_strerror(err));
+       }
+}
+
+static void select_complete(struct avdtp *session, struct a2dp_sep *sep,
+                       GSList *caps, void *user_data)
+{
+       struct source *source = user_data;
+       struct pending_request *pending;
+       int id;
+
+       pending = source->connect;
+
+       pending->id = 0;
+
+       if (caps == NULL)
+               goto failed;
+
+       id = a2dp_config(session, sep, stream_setup_complete, caps, source);
+       if (id == 0)
+               goto failed;
+
+       pending->id = id;
+       return;
+
+failed:
+       if (pending->msg)
+               error_failed(pending->conn, pending->msg, "Stream setup failed");
+       pending_request_free(source->dev, pending);
+       source->connect = NULL;
+       avdtp_unref(source->session);
+       source->session = NULL;
+}
+
+static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err,
+                               void *user_data)
+{
+       struct source *source = user_data;
+       struct pending_request *pending;
+       int id;
+
+       pending = source->connect;
+
+       if (err) {
+               avdtp_unref(source->session);
+               source->session = NULL;
+               if (avdtp_error_category(err) == AVDTP_ERRNO
+                               && avdtp_error_posix_errno(err) != EHOSTDOWN) {
+                       DBG("connect:connect XCASE detected");
+                       source->retry_id =
+                               g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER,
+                                                       stream_setup_retry,
+                                                       source);
+               } else
+                       goto failed;
+               return;
+       }
+
+       DBG("Discovery complete");
+
+       id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, NULL,
+                                               select_complete, source);
+       if (id == 0)
+               goto failed;
+
+       pending->id = id;
+       return;
+
+failed:
+       if (pending->msg)
+               error_failed(pending->conn, pending->msg, "Stream setup failed");
+       pending_request_free(source->dev, pending);
+       source->connect = NULL;
+       avdtp_unref(source->session);
+       source->session = NULL;
+}
+
+gboolean source_setup_stream(struct source *source, struct avdtp *session)
+{
+       if (source->connect || source->disconnect)
+               return FALSE;
+
+       if (session && !source->session)
+               source->session = avdtp_ref(session);
+
+       if (!source->session)
+               return FALSE;
+
+       avdtp_set_auto_disconnect(source->session, FALSE);
+
+       if (avdtp_discover(source->session, discovery_complete, source) < 0)
+               return FALSE;
+
+       source->connect = g_new0(struct pending_request, 1);
+
+       return TRUE;
+}
+
+static DBusMessage *source_connect(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct audio_device *dev = data;
+       struct source *source = dev->source;
+       struct pending_request *pending;
+
+       if (!source->session)
+               source->session = avdtp_get(&dev->src, &dev->dst);
+
+       if (!source->session)
+               return btd_error_failed(msg, "Unable to get a session");
+
+       if (source->connect || source->disconnect)
+               return btd_error_busy(msg);
+
+       if (source->stream_state >= AVDTP_STATE_OPEN)
+               return btd_error_already_connected(msg);
+
+       if (!source_setup_stream(source, NULL))
+               return btd_error_failed(msg, "Failed to create a stream");
+
+       dev->auto_connect = FALSE;
+
+       pending = source->connect;
+
+       pending->conn = dbus_connection_ref(conn);
+       pending->msg = dbus_message_ref(msg);
+
+       DBG("stream creation in progress");
+
+       return NULL;
+}
+
+static DBusMessage *source_disconnect(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct audio_device *device = data;
+       struct source *source = device->source;
+       struct pending_request *pending;
+       int err;
+
+       if (!source->session)
+               return btd_error_not_connected(msg);
+
+       if (source->connect || source->disconnect)
+               return btd_error_busy(msg);
+
+       if (source->stream_state < AVDTP_STATE_OPEN) {
+               DBusMessage *reply = dbus_message_new_method_return(msg);
+               if (!reply)
+                       return NULL;
+               avdtp_unref(source->session);
+               source->session = NULL;
+               return reply;
+       }
+
+       err = avdtp_close(source->session, source->stream, FALSE);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       pending = g_new0(struct pending_request, 1);
+       pending->conn = dbus_connection_ref(conn);
+       pending->msg = dbus_message_ref(msg);
+       source->disconnect = pending;
+
+       return NULL;
+}
+
+static DBusMessage *source_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct audio_device *device = data;
+       struct source *source = device->source;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       const char *state;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       /* State */
+       state = state2str(source->state);
+       if (state)
+               dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static const GDBusMethodTable source_methods[] = {
+       { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, source_connect) },
+       { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, source_disconnect) },
+       { GDBUS_METHOD("GetProperties",
+                               NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                               source_get_properties) },
+       { }
+};
+
+static const GDBusSignalTable source_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static void source_free(struct audio_device *dev)
+{
+       struct source *source = dev->source;
+
+       if (source->cb_id)
+               avdtp_stream_remove_cb(source->session, source->stream,
+                                       source->cb_id);
+
+       if (source->session)
+               avdtp_unref(source->session);
+
+       if (source->connect)
+               pending_request_free(dev, source->connect);
+
+       if (source->disconnect)
+               pending_request_free(dev, source->disconnect);
+
+       if (source->retry_id)
+               g_source_remove(source->retry_id);
+
+       g_free(source);
+       dev->source = NULL;
+}
+
+static void path_unregister(void *data)
+{
+       struct audio_device *dev = data;
+
+       DBG("Unregistered interface %s on path %s",
+               AUDIO_SOURCE_INTERFACE, dev->path);
+
+       source_free(dev);
+}
+
+void source_unregister(struct audio_device *dev)
+{
+       g_dbus_unregister_interface(dev->conn, dev->path,
+               AUDIO_SOURCE_INTERFACE);
+}
+
+struct source *source_init(struct audio_device *dev)
+{
+       struct source *source;
+
+       if (!g_dbus_register_interface(dev->conn, dev->path,
+                                       AUDIO_SOURCE_INTERFACE,
+                                       source_methods, source_signals, NULL,
+                                       dev, path_unregister))
+               return NULL;
+
+       DBG("Registered interface %s on path %s",
+               AUDIO_SOURCE_INTERFACE, dev->path);
+
+       if (avdtp_callback_id == 0)
+               avdtp_callback_id = avdtp_add_state_cb(avdtp_state_callback,
+                                                                       NULL);
+
+       source = g_new0(struct source, 1);
+
+       source->dev = dev;
+
+       return source;
+}
+
+gboolean source_is_active(struct audio_device *dev)
+{
+       struct source *source = dev->source;
+
+       if (source->session)
+               return TRUE;
+
+       return FALSE;
+}
+
+source_state_t source_get_state(struct audio_device *dev)
+{
+       struct source *source = dev->source;
+
+       return source->state;
+}
+
+gboolean source_new_stream(struct audio_device *dev, struct avdtp *session,
+                               struct avdtp_stream *stream)
+{
+       struct source *source = dev->source;
+
+       if (source->stream)
+               return FALSE;
+
+       if (!source->session)
+               source->session = avdtp_ref(session);
+
+       source->stream = stream;
+
+       source->cb_id = avdtp_stream_add_cb(session, stream,
+                                               stream_state_changed, dev);
+
+       return TRUE;
+}
+
+gboolean source_shutdown(struct source *source)
+{
+       if (!source->stream)
+               return FALSE;
+
+       if (avdtp_close(source->session, source->stream, FALSE) < 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+unsigned int source_add_state_cb(source_state_cb cb, void *user_data)
+{
+       struct source_state_callback *state_cb;
+       static unsigned int id = 0;
+
+       state_cb = g_new(struct source_state_callback, 1);
+       state_cb->cb = cb;
+       state_cb->user_data = user_data;
+       state_cb->id = ++id;
+
+       source_callbacks = g_slist_append(source_callbacks, state_cb);
+
+       return state_cb->id;
+}
+
+gboolean source_remove_state_cb(unsigned int id)
+{
+       GSList *l;
+
+       for (l = source_callbacks; l != NULL; l = l->next) {
+               struct source_state_callback *cb = l->data;
+               if (cb && cb->id == id) {
+                       source_callbacks = g_slist_remove(source_callbacks, cb);
+                       g_free(cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
diff --git a/audio/source.h b/audio/source.h
new file mode 100644 (file)
index 0000000..695bded
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009  Joao Paulo Rechi Vita
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define AUDIO_SOURCE_INTERFACE "org.bluez.AudioSource"
+
+typedef enum {
+       SOURCE_STATE_DISCONNECTED,
+       SOURCE_STATE_CONNECTING,
+       SOURCE_STATE_CONNECTED,
+       SOURCE_STATE_PLAYING,
+} source_state_t;
+
+typedef void (*source_state_cb) (struct audio_device *dev,
+                               source_state_t old_state,
+                               source_state_t new_state,
+                               void *user_data);
+
+unsigned int source_add_state_cb(source_state_cb cb, void *user_data);
+gboolean source_remove_state_cb(unsigned int id);
+
+struct source *source_init(struct audio_device *dev);
+void source_unregister(struct audio_device *dev);
+gboolean source_is_active(struct audio_device *dev);
+source_state_t source_get_state(struct audio_device *dev);
+gboolean source_new_stream(struct audio_device *dev, struct avdtp *session,
+                               struct avdtp_stream *stream);
+gboolean source_setup_stream(struct source *source, struct avdtp *session);
+gboolean source_shutdown(struct source *source);
diff --git a/audio/telephony-dummy.c b/audio/telephony-dummy.c
new file mode 100644 (file)
index 0000000..2f89139
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "telephony.h"
+#include "error.h"
+
+#define TELEPHONY_DUMMY_IFACE "org.bluez.TelephonyTest"
+#define TELEPHONY_DUMMY_PATH "/org/bluez/test"
+
+static DBusConnection *connection = NULL;
+
+static const char *chld_str = "0,1,1x,2,2x,3,4";
+static char *subscriber_number = NULL;
+static char *active_call_number = NULL;
+static int active_call_status = 0;
+static int active_call_dir = 0;
+
+static gboolean events_enabled = FALSE;
+
+static struct indicator dummy_indicators[] =
+{
+       { "battchg",    "0-5",  5,      TRUE },
+       { "signal",     "0-5",  5,      TRUE },
+       { "service",    "0,1",  1,      TRUE },
+       { "call",       "0,1",  0,      TRUE },
+       { "callsetup",  "0-3",  0,      TRUE },
+       { "callheld",   "0-2",  0,      FALSE },
+       { "roam",       "0,1",  0,      TRUE },
+       { NULL }
+};
+
+void telephony_device_connected(void *telephony_device)
+{
+       DBG("telephony-dummy: device %p connected", telephony_device);
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+       DBG("telephony-dummy: device %p disconnected", telephony_device);
+       events_enabled = FALSE;
+}
+
+void telephony_event_reporting_req(void *telephony_device, int ind)
+{
+       events_enabled = ind == 1 ? TRUE : FALSE;
+
+       telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_response_and_hold_req(void *telephony_device, int rh)
+{
+       telephony_response_and_hold_rsp(telephony_device,
+                                               CME_ERROR_NOT_SUPPORTED);
+}
+
+void telephony_last_dialed_number_req(void *telephony_device)
+{
+       telephony_last_dialed_number_rsp(telephony_device, CME_ERROR_NONE);
+
+       /* Notify outgoing call set-up successfully initiated */
+       telephony_update_indicator(dummy_indicators, "callsetup",
+                                       EV_CALLSETUP_OUTGOING);
+       telephony_update_indicator(dummy_indicators, "callsetup",
+                                       EV_CALLSETUP_ALERTING);
+
+       active_call_status = CALL_STATUS_ALERTING;
+       active_call_dir = CALL_DIR_OUTGOING;
+}
+
+void telephony_terminate_call_req(void *telephony_device)
+{
+       g_free(active_call_number);
+       active_call_number = NULL;
+
+       telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
+
+       if (telephony_get_indicator(dummy_indicators, "callsetup") > 0)
+               telephony_update_indicator(dummy_indicators, "callsetup",
+                                               EV_CALLSETUP_INACTIVE);
+       else
+               telephony_update_indicator(dummy_indicators, "call",
+                                               EV_CALL_INACTIVE);
+}
+
+void telephony_answer_call_req(void *telephony_device)
+{
+       telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
+
+       telephony_update_indicator(dummy_indicators, "call", EV_CALL_ACTIVE);
+       telephony_update_indicator(dummy_indicators, "callsetup",
+                                       EV_CALLSETUP_INACTIVE);
+
+       active_call_status = CALL_STATUS_ACTIVE;
+}
+
+void telephony_dial_number_req(void *telephony_device, const char *number)
+{
+       g_free(active_call_number);
+       active_call_number = g_strdup(number);
+
+       DBG("telephony-dummy: dial request to %s", active_call_number);
+
+       telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
+
+       /* Notify outgoing call set-up successfully initiated */
+       telephony_update_indicator(dummy_indicators, "callsetup",
+                                       EV_CALLSETUP_OUTGOING);
+       telephony_update_indicator(dummy_indicators, "callsetup",
+                                       EV_CALLSETUP_ALERTING);
+
+       active_call_status = CALL_STATUS_ALERTING;
+       active_call_dir = CALL_DIR_OUTGOING;
+}
+
+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
+{
+       DBG("telephony-dummy: transmit dtmf: %c", tone);
+       telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_subscriber_number_req(void *telephony_device)
+{
+       DBG("telephony-dummy: subscriber number request");
+       if (subscriber_number)
+               telephony_subscriber_number_ind(subscriber_number,
+                                               NUMBER_TYPE_TELEPHONY,
+                                               SUBSCRIBER_SERVICE_VOICE);
+       telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_list_current_calls_req(void *telephony_device)
+{
+       DBG("telephony-dummy: list current calls request");
+       if (active_call_number)
+               telephony_list_current_call_ind(1, active_call_dir,
+                                               active_call_status,
+                                               CALL_MODE_VOICE,
+                                               CALL_MULTIPARTY_NO,
+                                               active_call_number,
+                                               NUMBER_TYPE_TELEPHONY);
+       telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_operator_selection_req(void *telephony_device)
+{
+       telephony_operator_selection_ind(OPERATOR_MODE_AUTO, "DummyOperator");
+       telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_call_hold_req(void *telephony_device, const char *cmd)
+{
+       DBG("telephony-dymmy: got call hold request %s", cmd);
+       telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
+{
+       DBG("telephony-dummy: got %s NR and EC request",
+                       enable ? "enable" : "disable");
+
+       telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
+{
+       DBG("telephony-dummy: got %s voice dial request",
+                       enable ? "enable" : "disable");
+
+       g_dbus_emit_signal(connection, TELEPHONY_DUMMY_PATH,
+                       TELEPHONY_DUMMY_IFACE, "VoiceDial",
+                       DBUS_TYPE_INVALID);
+
+       telephony_voice_dial_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_key_press_req(void *telephony_device, const char *keys)
+{
+       DBG("telephony-dummy: got key press request for %s", keys);
+       telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+/* D-Bus method handlers */
+static DBusMessage *outgoing_call(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       const char *number;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       DBG("telephony-dummy: outgoing call to %s", number);
+
+       g_free(active_call_number);
+       active_call_number = g_strdup(number);
+
+       telephony_update_indicator(dummy_indicators, "callsetup",
+                                       EV_CALLSETUP_OUTGOING);
+       telephony_update_indicator(dummy_indicators, "callsetup",
+                                       EV_CALLSETUP_ALERTING);
+
+       active_call_status = CALL_STATUS_ALERTING;
+       active_call_dir = CALL_DIR_OUTGOING;
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *incoming_call(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       const char *number;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       DBG("telephony-dummy: incoming call to %s", number);
+
+       g_free(active_call_number);
+       active_call_number = g_strdup(number);
+
+       telephony_update_indicator(dummy_indicators, "callsetup",
+                                       EV_CALLSETUP_INCOMING);
+
+       active_call_status = CALL_STATUS_INCOMING;
+       active_call_dir = CALL_DIR_INCOMING;
+
+       telephony_incoming_call_ind(number, NUMBER_TYPE_TELEPHONY);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *cancel_call(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       DBG("telephony-dummy: cancel call");
+
+       g_free(active_call_number);
+       active_call_number = NULL;
+
+       if (telephony_get_indicator(dummy_indicators, "callsetup") > 0) {
+               telephony_update_indicator(dummy_indicators, "callsetup",
+                                               EV_CALLSETUP_INACTIVE);
+               telephony_calling_stopped_ind();
+       }
+
+       if (telephony_get_indicator(dummy_indicators, "call") > 0)
+               telephony_update_indicator(dummy_indicators, "call",
+                                               EV_CALL_INACTIVE);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *signal_strength(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       dbus_uint32_t strength;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &strength,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (strength > 5)
+               return btd_error_invalid_args(msg);
+
+       telephony_update_indicator(dummy_indicators, "signal", strength);
+
+       DBG("telephony-dummy: signal strength set to %u", strength);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *battery_level(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       dbus_uint32_t level;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &level,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (level > 5)
+               return btd_error_invalid_args(msg);
+
+       telephony_update_indicator(dummy_indicators, "battchg", level);
+
+       DBG("telephony-dummy: battery level set to %u", level);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *roaming_status(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       dbus_bool_t roaming;
+       int val;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &roaming,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       val = roaming ? EV_ROAM_ACTIVE : EV_ROAM_INACTIVE;
+
+       telephony_update_indicator(dummy_indicators, "roam", val);
+
+       DBG("telephony-dummy: roaming status set to %d", val);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *registration_status(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       dbus_bool_t registration;
+       int val;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &registration,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       val = registration ? EV_SERVICE_PRESENT : EV_SERVICE_NONE;
+
+       telephony_update_indicator(dummy_indicators, "service", val);
+
+       DBG("telephony-dummy: registration status set to %d", val);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_subscriber_number(DBusConnection *conn,
+                                               DBusMessage *msg,
+                                               void *data)
+{
+       const char *number;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       g_free(subscriber_number);
+       subscriber_number = g_strdup(number);
+
+       DBG("telephony-dummy: subscriber number set to %s", number);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable dummy_methods[] = {
+       { GDBUS_METHOD("OutgoingCall",
+                       GDBUS_ARGS({ "number", "s" }), NULL,
+                       outgoing_call) },
+       { GDBUS_METHOD("IncomingCall",
+                       GDBUS_ARGS({ "number", "s" }), NULL,
+                       incoming_call) },
+       { GDBUS_METHOD("CancelCall", NULL, NULL, cancel_call) },
+       { GDBUS_METHOD("SignalStrength",
+                       GDBUS_ARGS({ "strength", "u" }), NULL,
+                       signal_strength) },
+       { GDBUS_METHOD("BatteryLevel",
+                       GDBUS_ARGS({ "level", "u" }), NULL,
+                       battery_level) },
+       { GDBUS_METHOD("RoamingStatus",
+                       GDBUS_ARGS({ "roaming", "b" }), NULL,
+                       roaming_status) },
+       { GDBUS_METHOD("RegistrationStatus",
+                       GDBUS_ARGS({ "registration", "b" }), NULL,
+                       registration_status) },
+       { GDBUS_METHOD("SetSubscriberNumber",
+                       GDBUS_ARGS({ "number", "s" }), NULL,
+                       set_subscriber_number) },
+       { }
+};
+
+static const GDBusSignalTable dummy_signals[] = {
+       { GDBUS_SIGNAL("VoiceDial", NULL) },
+       { }
+};
+
+int telephony_init(void)
+{
+       uint32_t features = AG_FEATURE_REJECT_A_CALL |
+                               AG_FEATURE_ENHANCED_CALL_STATUS |
+                               AG_FEATURE_EXTENDED_ERROR_RESULT_CODES;
+
+       DBG("");
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+       if (g_dbus_register_interface(connection, TELEPHONY_DUMMY_PATH,
+                                       TELEPHONY_DUMMY_IFACE,
+                                       dummy_methods, dummy_signals,
+                                       NULL, NULL, NULL) == FALSE) {
+               error("telephony-dummy interface %s init failed on path %s",
+                       TELEPHONY_DUMMY_IFACE, TELEPHONY_DUMMY_PATH);
+               return -1;
+       }
+
+       telephony_ready_ind(features, dummy_indicators, BTRH_NOT_SUPPORTED,
+                                                               chld_str);
+
+       return 0;
+}
+
+void telephony_exit(void)
+{
+       DBG("");
+
+       g_dbus_unregister_interface(connection, TELEPHONY_DUMMY_PATH,
+                                               TELEPHONY_DUMMY_IFACE);
+       dbus_connection_unref(connection);
+       connection = NULL;
+
+       telephony_deinit();
+}
diff --git a/audio/telephony-maemo5.c b/audio/telephony-maemo5.c
new file mode 100644 (file)
index 0000000..8a00296
--- /dev/null
@@ -0,0 +1,2105 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "telephony.h"
+#include "error.h"
+
+/* SSC D-Bus definitions */
+#define SSC_DBUS_NAME  "com.nokia.phone.SSC"
+#define SSC_DBUS_IFACE "com.nokia.phone.SSC"
+#define SSC_DBUS_PATH  "/com/nokia/phone/SSC"
+
+/* libcsnet D-Bus definitions */
+#define NETWORK_BUS_NAME               "com.nokia.phone.net"
+#define NETWORK_INTERFACE              "Phone.Net"
+#define NETWORK_PATH                   "/com/nokia/phone/net"
+
+/* Mask bits for supported services */
+#define NETWORK_MASK_GPRS_SUPPORT      0x01
+#define NETWORK_MASK_CS_SERVICES       0x02
+#define NETWORK_MASK_EGPRS_SUPPORT     0x04
+#define NETWORK_MASK_HSDPA_AVAIL       0x08
+#define NETWORK_MASK_HSUPA_AVAIL       0x10
+
+/* network get cell info: cell type */
+#define NETWORK_UNKNOWN_CELL           0
+#define NETWORK_GSM_CELL               1
+#define NETWORK_WCDMA_CELL             2
+
+enum net_registration_status {
+       NETWORK_REG_STATUS_HOME = 0x00,
+       NETWORK_REG_STATUS_ROAM,
+       NETWORK_REG_STATUS_ROAM_BLINK,
+       NETWORK_REG_STATUS_NOSERV,
+       NETWORK_REG_STATUS_NOSERV_SEARCHING,
+       NETWORK_REG_STATUS_NOSERV_NOTSEARCHING,
+       NETWORK_REG_STATUS_NOSERV_NOSIM,
+       NETWORK_REG_STATUS_POWER_OFF = 0x08,
+       NETWORK_REG_STATUS_NSPS,
+       NETWORK_REG_STATUS_NSPS_NO_COVERAGE,
+       NETWORK_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW
+};
+
+enum network_types {
+       NETWORK_GSM_HOME_PLMN = 0,
+       NETWORK_GSM_PREFERRED_PLMN,
+       NETWORK_GSM_FORBIDDEN_PLMN,
+       NETWORK_GSM_OTHER_PLMN,
+       NETWORK_GSM_NO_PLMN_AVAIL
+};
+
+enum network_alpha_tag_name_type {
+       NETWORK_HARDCODED_LATIN_OPER_NAME = 0,
+       NETWORK_HARDCODED_USC2_OPER_NAME,
+       NETWORK_NITZ_SHORT_OPER_NAME,
+       NETWORK_NITZ_FULL_OPER_NAME,
+};
+
+#define TELEPHONY_MAEMO_PATH           "/com/nokia/MaemoTelephony"
+#define TELEPHONY_MAEMO_INTERFACE      "com.nokia.MaemoTelephony"
+
+#define CALLERID_BASE          "/var/lib/bluetooth/maemo-callerid-"
+#define ALLOWED_FLAG_FILE      "/var/lib/bluetooth/maemo-callerid-allowed"
+#define RESTRICTED_FLAG_FILE   "/var/lib/bluetooth/maemo-callerid-restricted"
+#define NONE_FLAG_FILE         "/var/lib/bluetooth/maemo-callerid-none"
+
+static uint32_t callerid = 0;
+
+/* CSD CALL plugin D-Bus definitions */
+#define CSD_CALL_BUS_NAME      "com.nokia.csd.Call"
+#define CSD_CALL_INTERFACE     "com.nokia.csd.Call"
+#define CSD_CALL_INSTANCE      "com.nokia.csd.Call.Instance"
+#define CSD_CALL_CONFERENCE    "com.nokia.csd.Call.Conference"
+#define CSD_CALL_PATH          "/com/nokia/csd/call"
+#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference"
+
+/* Call status values as exported by the CSD CALL plugin */
+#define CSD_CALL_STATUS_IDLE                   0
+#define CSD_CALL_STATUS_CREATE                 1
+#define CSD_CALL_STATUS_COMING                 2
+#define CSD_CALL_STATUS_PROCEEDING             3
+#define CSD_CALL_STATUS_MO_ALERTING            4
+#define CSD_CALL_STATUS_MT_ALERTING            5
+#define CSD_CALL_STATUS_WAITING                        6
+#define CSD_CALL_STATUS_ANSWERED               7
+#define CSD_CALL_STATUS_ACTIVE                 8
+#define CSD_CALL_STATUS_MO_RELEASE             9
+#define CSD_CALL_STATUS_MT_RELEASE             10
+#define CSD_CALL_STATUS_HOLD_INITIATED         11
+#define CSD_CALL_STATUS_HOLD                   12
+#define CSD_CALL_STATUS_RETRIEVE_INITIATED     13
+#define CSD_CALL_STATUS_RECONNECT_PENDING      14
+#define CSD_CALL_STATUS_TERMINATED             15
+#define CSD_CALL_STATUS_SWAP_INITIATED         16
+
+#define CALL_FLAG_NONE                         0
+#define CALL_FLAG_PRESENTATION_ALLOWED         0x01
+#define CALL_FLAG_PRESENTATION_RESTRICTED      0x02
+
+/* SIM Phonebook D-Bus definitions */
+#define SIM_PHONEBOOK_BUS_NAME                 "com.nokia.phone.SIM"
+#define SIM_PHONEBOOK_INTERFACE                        "Phone.Sim.Phonebook"
+#define SIM_PHONEBOOK_PATH                     "/com/nokia/phone/SIM/phonebook"
+
+#define PHONEBOOK_INDEX_FIRST_ENTRY            0xFFFF
+#define PHONEBOOK_INDEX_NEXT_FREE_LOCATION     0xFFFE
+
+enum sim_phonebook_type {
+       SIM_PHONEBOOK_TYPE_ADN = 0x0,
+       SIM_PHONEBOOK_TYPE_SDN,
+       SIM_PHONEBOOK_TYPE_FDN,
+       SIM_PHONEBOOK_TYPE_VMBX,
+       SIM_PHONEBOOK_TYPE_MBDN,
+       SIM_PHONEBOOK_TYPE_EN,
+       SIM_PHONEBOOK_TYPE_MSISDN
+};
+
+enum sim_phonebook_location_type {
+       SIM_PHONEBOOK_LOCATION_EXACT = 0x0,
+       SIM_PHONEBOOK_LOCATION_NEXT
+};
+
+struct csd_call {
+       char *object_path;
+       int status;
+       gboolean originating;
+       gboolean emergency;
+       gboolean on_hold;
+       gboolean conference;
+       char *number;
+       gboolean setup;
+};
+
+static struct {
+       uint8_t status;
+       uint16_t lac;
+       uint32_t cell_id;
+       uint32_t operator_code;
+       uint32_t country_code;
+       uint8_t network_type;
+       uint8_t supported_services;
+       uint16_t signals_bar;
+       char *operator_name;
+} net = {
+       .status = NETWORK_REG_STATUS_NOSERV,
+       .lac = 0,
+       .cell_id = 0,
+       .operator_code = 0,
+       .country_code = 0,
+       .network_type = NETWORK_GSM_NO_PLMN_AVAIL,
+       .supported_services = 0,
+       .signals_bar = 0,
+       .operator_name = NULL,
+};
+
+static DBusConnection *connection = NULL;
+
+static GSList *calls = NULL;
+
+/* Reference count for determining the call indicator status */
+static GSList *active_calls = NULL;
+
+static char *msisdn = NULL;    /* Subscriber number */
+static char *vmbx = NULL;      /* Voice mailbox number */
+
+/* HAL battery namespace key values */
+static int battchg_cur = -1;   /* "battery.charge_level.current" */
+static int battchg_last = -1;  /* "battery.charge_level.last_full" */
+static int battchg_design = -1;        /* "battery.charge_level.design" */
+
+static gboolean get_calls_active = FALSE;
+
+static gboolean events_enabled = FALSE;
+
+/* Supported set of call hold operations */
+static const char *chld_str = "0,1,1x,2,2x,3,4";
+
+static char *last_dialed_number = NULL;
+
+/* Timer for tracking call creation requests */
+static guint create_request_timer = 0;
+
+static struct indicator maemo_indicators[] =
+{
+       { "battchg",    "0-5",  5,      TRUE },
+       { "signal",     "0-5",  0,      TRUE },
+       { "service",    "0,1",  0,      TRUE },
+       { "call",       "0,1",  0,      TRUE },
+       { "callsetup",  "0-3",  0,      TRUE },
+       { "callheld",   "0-2",  0,      FALSE },
+       { "roam",       "0,1",  0,      TRUE },
+       { NULL }
+};
+
+static char *call_status_str[] = {
+       "IDLE",
+       "CREATE",
+       "COMING",
+       "PROCEEDING",
+       "MO_ALERTING",
+       "MT_ALERTING",
+       "WAITING",
+       "ANSWERED",
+       "ACTIVE",
+       "MO_RELEASE",
+       "MT_RELEASE",
+       "HOLD_INITIATED",
+       "HOLD",
+       "RETRIEVE_INITIATED",
+       "RECONNECT_PENDING",
+       "TERMINATED",
+       "SWAP_INITIATED",
+       "???"
+};
+
+static struct csd_call *find_call(const char *path)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct csd_call *call = l->data;
+
+               if (g_str_equal(call->object_path, path))
+                       return call;
+       }
+
+       return NULL;
+}
+
+static struct csd_call *find_non_held_call(void)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct csd_call *call = l->data;
+
+               if (call->status == CSD_CALL_STATUS_IDLE)
+                       continue;
+
+               if (call->status != CSD_CALL_STATUS_HOLD)
+                       return call;
+       }
+
+       return NULL;
+}
+
+static struct csd_call *find_non_idle_call(void)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct csd_call *call = l->data;
+
+               if (call->status != CSD_CALL_STATUS_IDLE)
+                       return call;
+       }
+
+       return NULL;
+}
+
+static struct csd_call *find_call_with_status(int status)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct csd_call *call = l->data;
+
+               if (call->status == status)
+                       return call;
+       }
+
+       return NULL;
+}
+
+static int release_conference(void)
+{
+       DBusMessage *msg;
+
+       DBG("telephony-maemo: releasing conference call");
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+                                               CSD_CALL_CONFERENCE_PATH,
+                                               CSD_CALL_INSTANCE,
+                                               "Release");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int release_call(struct csd_call *call)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+                                               call->object_path,
+                                               CSD_CALL_INSTANCE,
+                                               "Release");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int answer_call(struct csd_call *call)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+                                               call->object_path,
+                                               CSD_CALL_INSTANCE,
+                                               "Answer");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int split_call(struct csd_call *call)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+                                               call->object_path,
+                                               CSD_CALL_INSTANCE,
+                                               "Split");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int unhold_call(struct csd_call *call)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                                               CSD_CALL_INTERFACE,
+                                               "Unhold");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int hold_call(struct csd_call *call)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                                               CSD_CALL_INTERFACE,
+                                               "Hold");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int swap_calls(void)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                                               CSD_CALL_INTERFACE,
+                                               "Swap");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int create_conference(void)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                                               CSD_CALL_INTERFACE,
+                                               "Conference");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int call_transfer(void)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                                               CSD_CALL_INTERFACE,
+                                               "Transfer");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int number_type(const char *number)
+{
+       if (number == NULL)
+               return NUMBER_TYPE_TELEPHONY;
+
+       if (number[0] == '+' || strncmp(number, "00", 2) == 0)
+               return NUMBER_TYPE_INTERNATIONAL;
+
+       return NUMBER_TYPE_TELEPHONY;
+}
+
+void telephony_device_connected(void *telephony_device)
+{
+       struct csd_call *coming;
+
+       DBG("telephony-maemo: device %p connected", telephony_device);
+
+       coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+       if (coming) {
+               if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
+                       telephony_call_waiting_ind(coming->number,
+                                               number_type(coming->number));
+               else
+                       telephony_incoming_call_ind(coming->number,
+                                               number_type(coming->number));
+       }
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+       DBG("telephony-maemo: device %p disconnected", telephony_device);
+       events_enabled = FALSE;
+}
+
+void telephony_event_reporting_req(void *telephony_device, int ind)
+{
+       events_enabled = ind == 1 ? TRUE : FALSE;
+
+       telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_response_and_hold_req(void *telephony_device, int rh)
+{
+       telephony_response_and_hold_rsp(telephony_device,
+                                               CME_ERROR_NOT_SUPPORTED);
+}
+
+void telephony_last_dialed_number_req(void *telephony_device)
+{
+       DBG("telephony-maemo: last dialed number request");
+
+       if (last_dialed_number)
+               telephony_dial_number_req(telephony_device,
+                                               last_dialed_number);
+       else
+               telephony_last_dialed_number_rsp(telephony_device,
+                                               CME_ERROR_NOT_ALLOWED);
+}
+
+void telephony_terminate_call_req(void *telephony_device)
+{
+       struct csd_call *call;
+       int err;
+
+       call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+       if (!call)
+               call = find_non_idle_call();
+
+       if (!call) {
+               error("No active call");
+               telephony_terminate_call_rsp(telephony_device,
+                                               CME_ERROR_NOT_ALLOWED);
+               return;
+       }
+
+       if (call->conference)
+               err = release_conference();
+       else
+               err = release_call(call);
+
+       if (err < 0)
+               telephony_terminate_call_rsp(telephony_device,
+                                               CME_ERROR_AG_FAILURE);
+       else
+               telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_answer_call_req(void *telephony_device)
+{
+       struct csd_call *call;
+
+       call = find_call_with_status(CSD_CALL_STATUS_COMING);
+       if (!call)
+               call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+
+       if (!call)
+               call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
+
+       if (!call)
+               call = find_call_with_status(CSD_CALL_STATUS_WAITING);
+
+       if (!call) {
+               telephony_answer_call_rsp(telephony_device,
+                                               CME_ERROR_NOT_ALLOWED);
+               return;
+       }
+
+       if (answer_call(call) < 0)
+               telephony_answer_call_rsp(telephony_device,
+                                               CME_ERROR_AG_FAILURE);
+       else
+               telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static int send_method_call(const char *dest, const char *path,
+                               const char *interface, const char *method,
+                               DBusPendingCallNotifyFunction cb,
+                               void *user_data, int type, ...)
+{
+       DBusMessage *msg;
+       DBusPendingCall *call;
+       va_list args;
+
+       msg = dbus_message_new_method_call(dest, path, interface, method);
+       if (!msg) {
+               error("Unable to allocate new D-Bus %s message", method);
+               return -ENOMEM;
+       }
+
+       va_start(args, type);
+
+       if (!dbus_message_append_args_valist(msg, type, args)) {
+               dbus_message_unref(msg);
+               va_end(args);
+               return -EIO;
+       }
+
+       va_end(args);
+
+       if (!cb) {
+               g_dbus_send_message(connection, msg);
+               return 0;
+       }
+
+       if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+               error("Sending %s failed", method);
+               dbus_message_unref(msg);
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(call, cb, user_data, NULL);
+       dbus_pending_call_unref(call);
+       dbus_message_unref(msg);
+
+       return 0;
+}
+
+static const char *memory_dial_lookup(int location)
+{
+       if (location == 1)
+               return vmbx;
+       else
+               return NULL;
+}
+
+void telephony_dial_number_req(void *telephony_device, const char *number)
+{
+       uint32_t flags = callerid;
+       int ret;
+
+       DBG("telephony-maemo: dial request to %s", number);
+
+       if (strncmp(number, "*31#", 4) == 0) {
+               number += 4;
+               flags = CALL_FLAG_PRESENTATION_ALLOWED;
+       } else if (strncmp(number, "#31#", 4) == 0) {
+               number += 4;
+               flags = CALL_FLAG_PRESENTATION_RESTRICTED;
+       } else if (number[0] == '>') {
+               const char *location = &number[1];
+
+               number = memory_dial_lookup(strtol(&number[1], NULL, 0));
+               if (!number) {
+                       error("No number at memory location %s", location);
+                       telephony_dial_number_rsp(telephony_device,
+                                               CME_ERROR_INVALID_INDEX);
+                       return;
+               }
+       }
+
+       ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                               CSD_CALL_INTERFACE, "CreateWith",
+                               NULL, NULL,
+                               DBUS_TYPE_STRING, &number,
+                               DBUS_TYPE_UINT32, &flags,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0) {
+               telephony_dial_number_rsp(telephony_device,
+                                               CME_ERROR_AG_FAILURE);
+               return;
+       }
+
+       telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
+{
+       int ret;
+       char buf[2] = { tone, '\0' }, *buf_ptr = buf;
+
+       DBG("telephony-maemo: transmit dtmf: %s", buf);
+
+       ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                               CSD_CALL_INTERFACE, "SendDTMF",
+                               NULL, NULL,
+                               DBUS_TYPE_STRING, &buf_ptr,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0) {
+               telephony_transmit_dtmf_rsp(telephony_device,
+                                               CME_ERROR_AG_FAILURE);
+               return;
+       }
+
+       telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_subscriber_number_req(void *telephony_device)
+{
+       DBG("telephony-maemo: subscriber number request");
+       if (msisdn)
+               telephony_subscriber_number_ind(msisdn,
+                                               number_type(msisdn),
+                                               SUBSCRIBER_SERVICE_VOICE);
+       telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static int csd_status_to_hfp(struct csd_call *call)
+{
+       switch (call->status) {
+       case CSD_CALL_STATUS_IDLE:
+       case CSD_CALL_STATUS_MO_RELEASE:
+       case CSD_CALL_STATUS_MT_RELEASE:
+       case CSD_CALL_STATUS_TERMINATED:
+               return -1;
+       case CSD_CALL_STATUS_CREATE:
+               return CALL_STATUS_DIALING;
+       case CSD_CALL_STATUS_WAITING:
+               return CALL_STATUS_WAITING;
+       case CSD_CALL_STATUS_PROCEEDING:
+               /* PROCEEDING can happen in outgoing/incoming */
+               if (call->originating)
+                       return CALL_STATUS_DIALING;
+               else
+                       return CALL_STATUS_INCOMING;
+       case CSD_CALL_STATUS_COMING:
+               return CALL_STATUS_INCOMING;
+       case CSD_CALL_STATUS_MO_ALERTING:
+               return CALL_STATUS_ALERTING;
+       case CSD_CALL_STATUS_MT_ALERTING:
+               return CALL_STATUS_INCOMING;
+       case CSD_CALL_STATUS_ANSWERED:
+       case CSD_CALL_STATUS_ACTIVE:
+       case CSD_CALL_STATUS_RECONNECT_PENDING:
+       case CSD_CALL_STATUS_SWAP_INITIATED:
+       case CSD_CALL_STATUS_HOLD_INITIATED:
+               return CALL_STATUS_ACTIVE;
+       case CSD_CALL_STATUS_RETRIEVE_INITIATED:
+       case CSD_CALL_STATUS_HOLD:
+               return CALL_STATUS_HELD;
+       default:
+               return -1;
+       }
+}
+
+void telephony_list_current_calls_req(void *telephony_device)
+{
+       GSList *l;
+       int i;
+
+       DBG("telephony-maemo: list current calls request");
+
+       for (l = calls, i = 1; l != NULL; l = l->next, i++) {
+               struct csd_call *call = l->data;
+               int status, direction, multiparty;
+
+               status = csd_status_to_hfp(call);
+               if (status < 0)
+                       continue;
+
+               direction = call->originating ?
+                               CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
+
+               multiparty = call->conference ?
+                               CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
+
+               telephony_list_current_call_ind(i, direction, status,
+                                               CALL_MODE_VOICE, multiparty,
+                                               call->number,
+                                               number_type(call->number));
+       }
+
+       telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_operator_selection_req(void *telephony_device)
+{
+       telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
+                               net.operator_name ? net.operator_name : "");
+       telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static void foreach_call_with_status(int status,
+                                       int (*func)(struct csd_call *call))
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct csd_call *call = l->data;
+
+               if (call->status == status)
+                       func(call);
+       }
+}
+
+void telephony_call_hold_req(void *telephony_device, const char *cmd)
+{
+       const char *idx;
+       struct csd_call *call;
+       int err = 0;
+
+       DBG("telephony-maemo: got call hold request %s", cmd);
+
+       if (strlen(cmd) > 1)
+               idx = &cmd[1];
+       else
+               idx = NULL;
+
+       if (idx)
+               call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
+       else
+               call = NULL;
+
+       switch (cmd[0]) {
+       case '0':
+               foreach_call_with_status(CSD_CALL_STATUS_HOLD, release_call);
+               foreach_call_with_status(CSD_CALL_STATUS_WAITING,
+                                                               release_call);
+               break;
+       case '1':
+               if (idx) {
+                       if (call)
+                               err = release_call(call);
+                       break;
+               }
+               foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
+               call = find_call_with_status(CSD_CALL_STATUS_WAITING);
+               if (call)
+                       err = answer_call(call);
+               break;
+       case '2':
+               if (idx) {
+                       if (call)
+                               err = split_call(call);
+               } else {
+                       struct csd_call *held, *wait;
+
+                       call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+                       held = find_call_with_status(CSD_CALL_STATUS_HOLD);
+                       wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
+
+                       if (wait)
+                               err = answer_call(wait);
+                       else if (call && held)
+                               err = swap_calls();
+                       else {
+                               if (call)
+                                       err = hold_call(call);
+                               if (held)
+                                       err = unhold_call(held);
+                       }
+               }
+               break;
+       case '3':
+               if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
+                               find_call_with_status(CSD_CALL_STATUS_WAITING))
+                       err = create_conference();
+               break;
+       case '4':
+               err = call_transfer();
+               break;
+       default:
+               DBG("Unknown call hold request");
+               break;
+       }
+
+       if (err)
+               telephony_call_hold_rsp(telephony_device,
+                                       CME_ERROR_AG_FAILURE);
+       else
+               telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
+{
+       DBG("telephony-maemo: got %s NR and EC request",
+                       enable ? "enable" : "disable");
+       telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_key_press_req(void *telephony_device, const char *keys)
+{
+       struct csd_call *active, *waiting;
+       int err;
+
+       DBG("telephony-maemo: got key press request for %s", keys);
+
+       waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
+       if (!waiting)
+               waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+       if (!waiting)
+               waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
+
+       active = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+
+       if (waiting)
+               err = answer_call(waiting);
+       else if (active)
+               err = release_call(active);
+       else
+               err = 0;
+
+       if (err < 0)
+               telephony_key_press_rsp(telephony_device,
+                                                       CME_ERROR_AG_FAILURE);
+       else
+               telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
+{
+       DBG("telephony-maemo: got %s voice dial request",
+                       enable ? "enable" : "disable");
+
+       telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
+}
+
+static void handle_incoming_call(DBusMessage *msg)
+{
+       const char *number, *call_path;
+       struct csd_call *call;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &call_path,
+                                       DBUS_TYPE_STRING, &number,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in Call.Coming() signal");
+               return;
+       }
+
+       call = find_call(call_path);
+       if (!call) {
+               error("Didn't find any matching call object for %s",
+                               call_path);
+               return;
+       }
+
+       DBG("Incoming call to %s from number %s", call_path, number);
+
+       g_free(call->number);
+       call->number = g_strdup(number);
+
+       telephony_update_indicator(maemo_indicators, "callsetup",
+                                       EV_CALLSETUP_INCOMING);
+
+       if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
+               telephony_call_waiting_ind(call->number,
+                                               number_type(call->number));
+       else
+               telephony_incoming_call_ind(call->number,
+                                               number_type(call->number));
+}
+
+static void handle_outgoing_call(DBusMessage *msg)
+{
+       const char *number, *call_path;
+       struct csd_call *call;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &call_path,
+                                       DBUS_TYPE_STRING, &number,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in Call.Created() signal");
+               return;
+       }
+
+       call = find_call(call_path);
+       if (!call) {
+               error("Didn't find any matching call object for %s",
+                               call_path);
+               return;
+       }
+
+       DBG("Outgoing call from %s to number %s", call_path, number);
+
+       g_free(call->number);
+       call->number = g_strdup(number);
+
+       g_free(last_dialed_number);
+       last_dialed_number = g_strdup(number);
+
+       if (create_request_timer) {
+               g_source_remove(create_request_timer);
+               create_request_timer = 0;
+       }
+}
+
+static gboolean create_timeout(gpointer user_data)
+{
+       telephony_update_indicator(maemo_indicators, "callsetup",
+                                       EV_CALLSETUP_INACTIVE);
+       create_request_timer = 0;
+       return FALSE;
+}
+
+static void handle_create_requested(DBusMessage *msg)
+{
+       DBG("Call.CreateRequested()");
+
+       if (create_request_timer)
+               g_source_remove(create_request_timer);
+
+       create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL);
+
+       telephony_update_indicator(maemo_indicators, "callsetup",
+                                       EV_CALLSETUP_OUTGOING);
+}
+
+static void handle_call_status(DBusMessage *msg, const char *call_path)
+{
+       struct csd_call *call;
+       dbus_uint32_t status, cause_type, cause;
+       int callheld = telephony_get_indicator(maemo_indicators, "callheld");
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_UINT32, &status,
+                                       DBUS_TYPE_UINT32, &cause_type,
+                                       DBUS_TYPE_UINT32, &cause,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in Instance.CallStatus() signal");
+               return;
+       }
+
+       call = find_call(call_path);
+       if (!call) {
+               error("Didn't find any matching call object for %s",
+                               call_path);
+               return;
+       }
+
+       if (status > 16) {
+               error("Invalid call status %u", status);
+               return;
+       }
+
+       DBG("Call %s changed from %s to %s", call_path,
+               call_status_str[call->status], call_status_str[status]);
+
+       if (call->status == (int) status) {
+               DBG("Ignoring CSD Call state change to existing state");
+               return;
+       }
+
+       call->status = (int) status;
+
+       switch (status) {
+       case CSD_CALL_STATUS_IDLE:
+               if (call->setup) {
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callsetup",
+                                                       EV_CALLSETUP_INACTIVE);
+                       if (!call->originating)
+                               telephony_calling_stopped_ind();
+               }
+
+               g_free(call->number);
+               call->number = NULL;
+               call->originating = FALSE;
+               call->emergency = FALSE;
+               call->on_hold = FALSE;
+               call->conference = FALSE;
+               call->setup = FALSE;
+               break;
+       case CSD_CALL_STATUS_CREATE:
+               call->originating = TRUE;
+               call->setup = TRUE;
+               break;
+       case CSD_CALL_STATUS_COMING:
+               call->originating = FALSE;
+               call->setup = TRUE;
+               break;
+       case CSD_CALL_STATUS_PROCEEDING:
+               break;
+       case CSD_CALL_STATUS_MO_ALERTING:
+               telephony_update_indicator(maemo_indicators, "callsetup",
+                                               EV_CALLSETUP_ALERTING);
+               break;
+       case CSD_CALL_STATUS_MT_ALERTING:
+               break;
+       case CSD_CALL_STATUS_WAITING:
+               break;
+       case CSD_CALL_STATUS_ANSWERED:
+               break;
+       case CSD_CALL_STATUS_ACTIVE:
+               if (call->on_hold) {
+                       call->on_hold = FALSE;
+                       if (find_call_with_status(CSD_CALL_STATUS_HOLD))
+                               telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_MULTIPLE);
+                       else
+                               telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_NONE);
+               } else {
+                       if (!g_slist_find(active_calls, call))
+                               active_calls = g_slist_prepend(active_calls, call);
+                       if (g_slist_length(active_calls) == 1)
+                               telephony_update_indicator(maemo_indicators,
+                                                               "call",
+                                                               EV_CALL_ACTIVE);
+                       /* Upgrade callheld status if necessary */
+                       if (callheld == EV_CALLHELD_ON_HOLD)
+                               telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_MULTIPLE);
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callsetup",
+                                                       EV_CALLSETUP_INACTIVE);
+                       if (!call->originating)
+                               telephony_calling_stopped_ind();
+                       call->setup = FALSE;
+               }
+               break;
+       case CSD_CALL_STATUS_MO_RELEASE:
+       case CSD_CALL_STATUS_MT_RELEASE:
+               active_calls = g_slist_remove(active_calls, call);
+               if (g_slist_length(active_calls) == 0)
+                       telephony_update_indicator(maemo_indicators, "call",
+                                                       EV_CALL_INACTIVE);
+               break;
+       case CSD_CALL_STATUS_HOLD_INITIATED:
+               break;
+       case CSD_CALL_STATUS_HOLD:
+               call->on_hold = TRUE;
+               if (find_non_held_call())
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_MULTIPLE);
+               else
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_ON_HOLD);
+               break;
+       case CSD_CALL_STATUS_RETRIEVE_INITIATED:
+               break;
+       case CSD_CALL_STATUS_RECONNECT_PENDING:
+               break;
+       case CSD_CALL_STATUS_TERMINATED:
+               if (call->on_hold &&
+                               !find_call_with_status(CSD_CALL_STATUS_HOLD))
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_NONE);
+               else if (callheld == EV_CALLHELD_MULTIPLE &&
+                               find_call_with_status(CSD_CALL_STATUS_HOLD))
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_ON_HOLD);
+               break;
+       case CSD_CALL_STATUS_SWAP_INITIATED:
+               break;
+       default:
+               error("Unknown call status %u", status);
+               break;
+       }
+}
+
+static void handle_conference(DBusMessage *msg, gboolean joined)
+{
+       const char *path;
+       struct csd_call *call;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &path,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in Conference.%s",
+                                       dbus_message_get_member(msg));
+               return;
+       }
+
+       call = find_call(path);
+       if (!call) {
+               error("Conference signal for unknown call %s", path);
+               return;
+       }
+
+       DBG("Call %s %s the conference", path, joined ? "joined" : "left");
+
+       call->conference = joined;
+}
+
+static void get_operator_name_reply(DBusPendingCall *pending_call,
+                                       void *user_data)
+{
+       DBusMessage *reply;
+       DBusError err;
+       const char *name;
+       dbus_int32_t net_err;
+
+       reply = dbus_pending_call_steal_reply(pending_call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("get_operator_name failed: %s, %s",
+                       err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_error_init(&err);
+       if (!dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_STRING, &name,
+                                       DBUS_TYPE_INT32, &net_err,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected get_operator_name reply parameters: %s, %s",
+                       err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (net_err != 0) {
+               error("get_operator_name failed with code %d", net_err);
+               goto done;
+       }
+
+       if (strlen(name) == 0)
+               goto done;
+
+       g_free(net.operator_name);
+       net.operator_name = g_strdup(name);
+
+       DBG("telephony-maemo: operator name updated: %s", name);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void resolve_operator_name(uint32_t operator, uint32_t country)
+{
+       uint8_t name_type = NETWORK_HARDCODED_LATIN_OPER_NAME;
+
+       send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
+                               NETWORK_INTERFACE, "get_operator_name",
+                               get_operator_name_reply, NULL,
+                               DBUS_TYPE_BYTE, &name_type,
+                               DBUS_TYPE_UINT32, &operator,
+                               DBUS_TYPE_UINT32, &country,
+                               DBUS_TYPE_INVALID);
+}
+
+static void update_registration_status(uint8_t status, uint16_t lac,
+                                       uint32_t cell_id,
+                                       uint32_t operator_code,
+                                       uint32_t country_code,
+                                       uint8_t network_type,
+                                       uint8_t supported_services)
+{
+       if (net.status != status) {
+               switch (status) {
+               case NETWORK_REG_STATUS_HOME:
+                       telephony_update_indicator(maemo_indicators, "roam",
+                                                       EV_ROAM_INACTIVE);
+                       if (net.status >= NETWORK_REG_STATUS_NOSERV)
+                               telephony_update_indicator(maemo_indicators,
+                                                       "service",
+                                                       EV_SERVICE_PRESENT);
+                       break;
+               case NETWORK_REG_STATUS_ROAM:
+               case NETWORK_REG_STATUS_ROAM_BLINK:
+                       telephony_update_indicator(maemo_indicators, "roam",
+                                                       EV_ROAM_ACTIVE);
+                       if (net.status >= NETWORK_REG_STATUS_NOSERV)
+                               telephony_update_indicator(maemo_indicators,
+                                                       "service",
+                                                       EV_SERVICE_PRESENT);
+                       break;
+               case NETWORK_REG_STATUS_NOSERV:
+               case NETWORK_REG_STATUS_NOSERV_SEARCHING:
+               case NETWORK_REG_STATUS_NOSERV_NOTSEARCHING:
+               case NETWORK_REG_STATUS_NOSERV_NOSIM:
+               case NETWORK_REG_STATUS_POWER_OFF:
+               case NETWORK_REG_STATUS_NSPS:
+               case NETWORK_REG_STATUS_NSPS_NO_COVERAGE:
+               case NETWORK_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW:
+                       if (net.status < NETWORK_REG_STATUS_NOSERV)
+                               telephony_update_indicator(maemo_indicators,
+                                                       "service",
+                                                       EV_SERVICE_NONE);
+                       break;
+               }
+
+               net.status = status;
+       }
+
+       net.lac = lac;
+       net.cell_id = cell_id;
+
+       if (net.operator_code != operator_code ||
+                       net.country_code != country_code) {
+               g_free(net.operator_name);
+               net.operator_name = NULL;
+               resolve_operator_name(operator_code, country_code);
+               net.operator_code = operator_code;
+               net.country_code = country_code;
+       }
+
+       net.network_type = network_type;
+       net.supported_services = supported_services;
+}
+
+static void handle_registration_status_change(DBusMessage *msg)
+{
+       uint8_t status;
+       dbus_uint16_t lac, network_type, supported_services;
+       dbus_uint32_t cell_id, operator_code, country_code;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_BYTE, &status,
+                                       DBUS_TYPE_UINT16, &lac,
+                                       DBUS_TYPE_UINT32, &cell_id,
+                                       DBUS_TYPE_UINT32, &operator_code,
+                                       DBUS_TYPE_UINT32, &country_code,
+                                       DBUS_TYPE_BYTE, &network_type,
+                                       DBUS_TYPE_BYTE, &supported_services,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in registration_status_change");
+               return;
+       }
+
+       update_registration_status(status, lac, cell_id, operator_code,
+                                       country_code, network_type,
+                                       supported_services);
+}
+
+static void update_signal_strength(uint8_t signals_bar)
+{
+       int signal;
+
+       if (signals_bar > 100) {
+               DBG("signals_bar greater than expected: %u", signals_bar);
+               signals_bar = 100;
+       }
+
+       if (net.signals_bar == signals_bar)
+               return;
+
+       /* A simple conversion from 0-100 to 0-5 (used by HFP) */
+       signal = (signals_bar + 20) / 21;
+
+       telephony_update_indicator(maemo_indicators, "signal", signal);
+
+       net.signals_bar = signals_bar;
+
+       DBG("Signal strength updated: %u/100, %d/5", signals_bar, signal);
+}
+
+static void handle_signal_strength_change(DBusMessage *msg)
+{
+       uint8_t signals_bar, rssi_in_dbm;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_BYTE, &signals_bar,
+                                       DBUS_TYPE_BYTE, &rssi_in_dbm,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in signal_strength_change");
+               return;
+       }
+
+       update_signal_strength(signals_bar);
+}
+
+static gboolean iter_get_basic_args(DBusMessageIter *iter,
+                                       int first_arg_type, ...)
+{
+       int type;
+       va_list ap;
+
+       va_start(ap, first_arg_type);
+
+       for (type = first_arg_type; type != DBUS_TYPE_INVALID;
+                       type = va_arg(ap, int)) {
+               void *value = va_arg(ap, void *);
+               int real_type = dbus_message_iter_get_arg_type(iter);
+
+               if (real_type != type) {
+                       error("iter_get_basic_args: expected %c but got %c",
+                                       (char) type, (char) real_type);
+                       break;
+               }
+
+               dbus_message_iter_get_basic(iter, value);
+               dbus_message_iter_next(iter);
+       }
+
+       va_end(ap);
+
+       return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
+}
+
+static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       dbus_int32_t level;
+       int *value = user_data;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("hald replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_error_init(&err);
+       if (dbus_message_get_args(reply, &err,
+                               DBUS_TYPE_INT32, &level,
+                               DBUS_TYPE_INVALID) == FALSE) {
+               error("Unable to parse GetPropertyInteger reply: %s, %s",
+                                                       err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       *value = (int) level;
+
+       if (value == &battchg_last)
+               DBG("telephony-maemo: battery.charge_level.last_full is %d",
+                               *value);
+       else if (value == &battchg_design)
+               DBG("telephony-maemo: battery.charge_level.design is %d",
+                               *value);
+       else
+               DBG("telephony-maemo: battery.charge_level.current is %d",
+                               *value);
+
+       if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
+               int new, max;
+
+               if (battchg_last > 0)
+                       max = battchg_last;
+               else
+                       max = battchg_design;
+
+               new = battchg_cur * 5 / max;
+
+               telephony_update_indicator(maemo_indicators, "battchg", new);
+       }
+done:
+       dbus_message_unref(reply);
+}
+
+static void hal_get_integer(const char *path, const char *key, void *user_data)
+{
+       send_method_call("org.freedesktop.Hal", path,
+                               "org.freedesktop.Hal.Device",
+                               "GetPropertyInteger",
+                               hal_battery_level_reply, user_data,
+                               DBUS_TYPE_STRING, &key,
+                               DBUS_TYPE_INVALID);
+}
+
+static void handle_hal_property_modified(DBusMessage *msg)
+{
+       DBusMessageIter iter, array;
+       dbus_int32_t num_changes;
+       const char *path;
+
+       path = dbus_message_get_path(msg);
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+               error("Unexpected signature in hal PropertyModified signal");
+               return;
+       }
+
+       dbus_message_iter_get_basic(&iter, &num_changes);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature in hal PropertyModified signal");
+               return;
+       }
+
+       dbus_message_iter_recurse(&iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+               DBusMessageIter prop;
+               const char *name;
+               dbus_bool_t added, removed;
+
+               dbus_message_iter_recurse(&array, &prop);
+
+               if (!iter_get_basic_args(&prop,
+                                       DBUS_TYPE_STRING, &name,
+                                       DBUS_TYPE_BOOLEAN, &added,
+                                       DBUS_TYPE_BOOLEAN, &removed,
+                                       DBUS_TYPE_INVALID)) {
+                       error("Invalid hal PropertyModified parameters");
+                       break;
+               }
+
+               if (g_str_equal(name, "battery.charge_level.last_full"))
+                       hal_get_integer(path, name, &battchg_last);
+               else if (g_str_equal(name, "battery.charge_level.current"))
+                       hal_get_integer(path, name, &battchg_cur);
+               else if (g_str_equal(name, "battery.charge_level.design"))
+                       hal_get_integer(path, name, &battchg_design);
+
+               dbus_message_iter_next(&array);
+       }
+}
+
+static void csd_call_free(struct csd_call *call)
+{
+       if (!call)
+               return;
+
+       g_free(call->object_path);
+       g_free(call->number);
+
+       g_free(call);
+}
+
+static void parse_call_list(DBusMessageIter *iter)
+{
+       do {
+               DBusMessageIter call_iter;
+               struct csd_call *call;
+               const char *object_path, *number;
+               dbus_uint32_t status;
+               dbus_bool_t originating, terminating, emerg, on_hold, conf;
+
+               if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
+                       error("Unexpected signature in GetCallInfoAll reply");
+                       break;
+               }
+
+               dbus_message_iter_recurse(iter, &call_iter);
+
+               if (!iter_get_basic_args(&call_iter,
+                                       DBUS_TYPE_OBJECT_PATH, &object_path,
+                                       DBUS_TYPE_UINT32, &status,
+                                       DBUS_TYPE_BOOLEAN, &originating,
+                                       DBUS_TYPE_BOOLEAN, &terminating,
+                                       DBUS_TYPE_BOOLEAN, &emerg,
+                                       DBUS_TYPE_BOOLEAN, &on_hold,
+                                       DBUS_TYPE_BOOLEAN, &conf,
+                                       DBUS_TYPE_STRING, &number,
+                                       DBUS_TYPE_INVALID)) {
+                       error("Parsing call D-Bus parameters failed");
+                       break;
+               }
+
+               call = find_call(object_path);
+               if (!call) {
+                       call = g_new0(struct csd_call, 1);
+                       call->object_path = g_strdup(object_path);
+                       call->status = (int) status;
+                       calls = g_slist_append(calls, call);
+                       DBG("telephony-maemo: new csd call instance at %s",
+                                                               object_path);
+               }
+
+               if (call->status == CSD_CALL_STATUS_IDLE)
+                       continue;
+
+               /* CSD gives incorrect call_hold property sometimes */
+               if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) ||
+                               (call->status == CSD_CALL_STATUS_HOLD &&
+                                                               !on_hold)) {
+                       error("Conflicting call status and on_hold property!");
+                       on_hold = call->status == CSD_CALL_STATUS_HOLD;
+               }
+
+               call->originating = originating;
+               call->on_hold = on_hold;
+               call->conference = conf;
+               g_free(call->number);
+               call->number = g_strdup(number);
+
+       } while (dbus_message_iter_next(iter));
+}
+
+static void signal_strength_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       uint8_t signals_bar, rssi_in_dbm;
+       dbus_int32_t net_err;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("Unable to get signal strength: %s, %s",
+                       err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_error_init(&err);
+       if (!dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_BYTE, &signals_bar,
+                                       DBUS_TYPE_BYTE, &rssi_in_dbm,
+                                       DBUS_TYPE_INT32, &net_err,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unable to parse signal_strength reply: %s, %s",
+                                                       err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (net_err != 0) {
+               error("get_signal_strength failed with code %d", net_err);
+               goto done;
+       }
+
+       update_signal_strength(signals_bar);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static int get_signal_strength(void)
+{
+       return send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
+                               NETWORK_INTERFACE, "get_signal_strength",
+                               signal_strength_reply, NULL,
+                               DBUS_TYPE_INVALID);
+}
+
+static void registration_status_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       uint8_t status;
+       dbus_uint16_t lac, network_type, supported_services;
+       dbus_uint32_t cell_id, operator_code, country_code;
+       dbus_int32_t net_err;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("Unable to get registration status: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_error_init(&err);
+       if (!dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_BYTE, &status,
+                                       DBUS_TYPE_UINT16, &lac,
+                                       DBUS_TYPE_UINT32, &cell_id,
+                                       DBUS_TYPE_UINT32, &operator_code,
+                                       DBUS_TYPE_UINT32, &country_code,
+                                       DBUS_TYPE_BYTE, &network_type,
+                                       DBUS_TYPE_BYTE, &supported_services,
+                                       DBUS_TYPE_INT32, &net_err,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unable to parse registration_status_change reply:"
+                                       " %s, %s", err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (net_err != 0) {
+               error("get_registration_status failed with code %d", net_err);
+               goto done;
+       }
+
+       update_registration_status(status, lac, cell_id, operator_code,
+                                       country_code, network_type,
+                                       supported_services);
+
+       get_signal_strength();
+
+done:
+       dbus_message_unref(reply);
+}
+
+static int get_registration_status(void)
+{
+       return send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
+                               NETWORK_INTERFACE, "get_registration_status",
+                               registration_status_reply, NULL,
+                               DBUS_TYPE_INVALID);
+}
+
+static void call_info_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       DBusMessageIter iter, sub;
+
+       get_calls_active = FALSE;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("csd replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature in GetCallInfoAll return");
+               goto done;
+       }
+
+       dbus_message_iter_recurse(&iter, &sub);
+
+       parse_call_list(&sub);
+
+       get_registration_status();
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       DBusMessageIter iter, sub;
+       const char *path;
+       char match_string[256];
+       int type;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("hald replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature in FindDeviceByCapability return");
+               goto done;
+       }
+
+       dbus_message_iter_recurse(&iter, &sub);
+
+       type = dbus_message_iter_get_arg_type(&sub);
+
+       if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
+               error("No hal device with battery capability found");
+               goto done;
+       }
+
+       dbus_message_iter_get_basic(&sub, &path);
+
+       DBG("telephony-maemo: found battery device at %s", path);
+
+       snprintf(match_string, sizeof(match_string),
+                       "type='signal',"
+                       "path='%s',"
+                       "interface='org.freedesktop.Hal.Device',"
+                       "member='PropertyModified'", path);
+       dbus_bus_add_match(connection, match_string, NULL);
+
+       hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
+       hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
+       hal_get_integer(path, "battery.charge_level.design", &battchg_design);
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void phonebook_read_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError derr;
+       DBusMessage *reply;
+       const char *name, *number;
+       char **number_type = user_data;
+       dbus_int32_t current_location, err;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("SIM.Phonebook replied with an error: %s, %s",
+                               derr.name, derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       dbus_error_init(&derr);
+       if (dbus_message_get_args(reply, &derr,
+                               DBUS_TYPE_STRING, &name,
+                               DBUS_TYPE_STRING, &number,
+                               DBUS_TYPE_INT32, &current_location,
+                               DBUS_TYPE_INT32, &err,
+                               DBUS_TYPE_INVALID) == FALSE) {
+               error("Unable to parse SIM.Phonebook.read arguments: %s, %s",
+                               derr.name, derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       if (err != 0) {
+               error("SIM.Phonebook.read failed with error %d", err);
+               if (number_type == &vmbx)
+                       vmbx = g_strdup(getenv("VMBX_NUMBER"));
+               goto done;
+       }
+
+       if (number_type == &msisdn) {
+               g_free(msisdn);
+               msisdn = g_strdup(number);
+               DBG("Got MSISDN %s (%s)", number, name);
+       } else {
+               g_free(vmbx);
+               vmbx = g_strdup(number);
+               DBG("Got voice mailbox number %s (%s)", number, name);
+       }
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void csd_init(void)
+{
+       dbus_uint32_t location;
+       uint8_t pb_type, location_type;
+       int ret;
+
+       ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                               CSD_CALL_INTERFACE, "GetCallInfoAll",
+                               call_info_reply, NULL, DBUS_TYPE_INVALID);
+       if (ret < 0) {
+               error("Unable to sent GetCallInfoAll method call");
+               return;
+       }
+
+       get_calls_active = TRUE;
+
+       pb_type = SIM_PHONEBOOK_TYPE_MSISDN;
+       location = PHONEBOOK_INDEX_FIRST_ENTRY;
+       location_type = SIM_PHONEBOOK_LOCATION_NEXT;
+
+       ret = send_method_call(SIM_PHONEBOOK_BUS_NAME, SIM_PHONEBOOK_PATH,
+                               SIM_PHONEBOOK_INTERFACE, "read",
+                               phonebook_read_reply, &msisdn,
+                               DBUS_TYPE_BYTE, &pb_type,
+                               DBUS_TYPE_INT32, &location,
+                               DBUS_TYPE_BYTE, &location_type,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0) {
+               error("Unable to send " SIM_PHONEBOOK_INTERFACE ".read()");
+               return;
+       }
+
+       pb_type = SIM_PHONEBOOK_TYPE_MBDN;
+       location = PHONEBOOK_INDEX_FIRST_ENTRY;
+       location_type = SIM_PHONEBOOK_LOCATION_NEXT;
+
+       ret = send_method_call(SIM_PHONEBOOK_BUS_NAME, SIM_PHONEBOOK_PATH,
+                               SIM_PHONEBOOK_INTERFACE, "read",
+                               phonebook_read_reply, &vmbx,
+                               DBUS_TYPE_BYTE, &pb_type,
+                               DBUS_TYPE_INT32, &location,
+                               DBUS_TYPE_BYTE, &location_type,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0) {
+               error("Unable to send " SIM_PHONEBOOK_INTERFACE ".read()");
+               return;
+       }
+}
+
+static uint32_t get_callflag(const char *callerid_setting)
+{
+       if (callerid_setting != NULL) {
+               if (g_str_equal(callerid_setting, "allowed"))
+                       return CALL_FLAG_PRESENTATION_ALLOWED;
+               else if (g_str_equal(callerid_setting, "restricted"))
+                       return CALL_FLAG_PRESENTATION_RESTRICTED;
+               else
+                       return CALL_FLAG_NONE;
+       } else
+               return CALL_FLAG_NONE;
+}
+
+static void generate_flag_file(const char *filename)
+{
+       int fd;
+
+       if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS) ||
+                       g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS) ||
+                       g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
+               return;
+
+       fd = open(filename, O_WRONLY | O_CREAT, 0);
+       if (fd >= 0)
+               close(fd);
+}
+
+static void save_callerid_to_file(const char *callerid_setting)
+{
+       char callerid_file[FILENAME_MAX];
+
+       snprintf(callerid_file, sizeof(callerid_file), "%s%s",
+                                       CALLERID_BASE, callerid_setting);
+
+       if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS))
+               rename(ALLOWED_FLAG_FILE, callerid_file);
+       else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS))
+               rename(RESTRICTED_FLAG_FILE, callerid_file);
+       else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
+               rename(NONE_FLAG_FILE, callerid_file);
+       else
+               generate_flag_file(callerid_file);
+}
+
+static uint32_t callerid_from_file(void)
+{
+       if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS))
+               return CALL_FLAG_PRESENTATION_ALLOWED;
+       else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS))
+               return CALL_FLAG_PRESENTATION_RESTRICTED;
+       else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
+               return CALL_FLAG_NONE;
+       else
+               return CALL_FLAG_NONE;
+}
+
+static DBusMessage *set_callerid(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       const char *callerid_setting;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,
+                                               &callerid_setting,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       if (g_str_equal(callerid_setting, "allowed") ||
+                       g_str_equal(callerid_setting, "restricted") ||
+                       g_str_equal(callerid_setting, "none")) {
+               save_callerid_to_file(callerid_setting);
+               callerid = get_callflag(callerid_setting);
+               DBG("telephony-maemo setting callerid flag: %s",
+                                                       callerid_setting);
+               return dbus_message_new_method_return(msg);
+       }
+
+       error("telephony-maemo: invalid argument %s for method call"
+                                       " SetCallerId", callerid_setting);
+               return btd_error_invalid_args(msg);
+}
+
+static const GDBusMethodTable telephony_maemo_methods[] = {
+       { GDBUS_ASYNC_METHOD("SetCallerId",
+                               GDBUS_ARGS({ "id", "s" }), NULL,
+                               set_callerid) },
+       { }
+};
+
+static void handle_modem_state(DBusMessage *msg)
+{
+       const char *state;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state,
+                                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected modem state parameters");
+               return;
+       }
+
+       DBG("SSC modem state: %s", state);
+
+       if (calls != NULL || get_calls_active)
+               return;
+
+       if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online"))
+               csd_init();
+}
+
+static void modem_state_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError err;
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("get_modem_status: %s, %s", err.name, err.message);
+               dbus_error_free(&err);
+       } else
+               handle_modem_state(reply);
+
+       dbus_message_unref(reply);
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       const char *path = dbus_message_get_path(msg);
+
+       if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
+               handle_incoming_call(msg);
+       else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
+               handle_outgoing_call(msg);
+       else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
+                                                       "CreateRequested"))
+               handle_create_requested(msg);
+       else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
+               handle_call_status(msg, path);
+       else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
+               handle_conference(msg, TRUE);
+       else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
+               handle_conference(msg, FALSE);
+       else if (dbus_message_is_signal(msg, NETWORK_INTERFACE,
+                                       "registration_status_change"))
+               handle_registration_status_change(msg);
+       else if (dbus_message_is_signal(msg, NETWORK_INTERFACE,
+                                       "signal_strength_change"))
+               handle_signal_strength_change(msg);
+       else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
+                                       "PropertyModified"))
+               handle_hal_property_modified(msg);
+       else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE,
+                                               "modem_state_changed_ind"))
+               handle_modem_state(msg);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int telephony_init(void)
+{
+       const char *battery_cap = "battery";
+       uint32_t features = AG_FEATURE_EC_ANDOR_NR |
+                               AG_FEATURE_INBAND_RINGTONE |
+                               AG_FEATURE_REJECT_A_CALL |
+                               AG_FEATURE_ENHANCED_CALL_STATUS |
+                               AG_FEATURE_ENHANCED_CALL_CONTROL |
+                               AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
+                               AG_FEATURE_THREE_WAY_CALLING;
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+       if (!dbus_connection_add_filter(connection, signal_filter,
+                                               NULL, NULL))
+               error("Can't add signal filter");
+
+       dbus_bus_add_match(connection,
+                       "type=signal,interface=" CSD_CALL_INTERFACE, NULL);
+       dbus_bus_add_match(connection,
+                       "type=signal,interface=" CSD_CALL_INSTANCE, NULL);
+       dbus_bus_add_match(connection,
+                       "type=signal,interface=" CSD_CALL_CONFERENCE, NULL);
+       dbus_bus_add_match(connection,
+                       "type=signal,interface=" NETWORK_INTERFACE, NULL);
+       dbus_bus_add_match(connection,
+                               "type=signal,interface=" SSC_DBUS_IFACE
+                               ",member=modem_state_changed_ind", NULL);
+
+       if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE,
+                                       "get_modem_state", modem_state_reply,
+                                       NULL, DBUS_TYPE_INVALID) < 0)
+               error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()");
+
+       generate_flag_file(NONE_FLAG_FILE);
+       callerid = callerid_from_file();
+
+       if (!g_dbus_register_interface(connection, TELEPHONY_MAEMO_PATH,
+                       TELEPHONY_MAEMO_INTERFACE, telephony_maemo_methods,
+                       NULL, NULL, NULL, NULL)) {
+               error("telephony-maemo interface %s init failed on path %s",
+                       TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH);
+       }
+
+       DBG("telephony-maemo registering %s interface on path %s",
+                       TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH);
+
+       telephony_ready_ind(features, maemo_indicators, BTRH_NOT_SUPPORTED,
+                                                               chld_str);
+       if (send_method_call("org.freedesktop.Hal",
+                               "/org/freedesktop/Hal/Manager",
+                               "org.freedesktop.Hal.Manager",
+                               "FindDeviceByCapability",
+                               hal_find_device_reply, NULL,
+                               DBUS_TYPE_STRING, &battery_cap,
+                               DBUS_TYPE_INVALID) < 0)
+               error("Unable to send HAL method call");
+
+       return 0;
+}
+
+void telephony_exit(void)
+{
+       g_slist_foreach(calls, (GFunc) csd_call_free, NULL);
+       g_slist_free(calls);
+       calls = NULL;
+
+       dbus_connection_remove_filter(connection, signal_filter, NULL);
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+
+       telephony_deinit();
+}
diff --git a/audio/telephony-maemo6.c b/audio/telephony-maemo6.c
new file mode 100644 (file)
index 0000000..0727ffe
--- /dev/null
@@ -0,0 +1,2200 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include <bluetooth/sdp.h>
+
+#include "log.h"
+#include "telephony.h"
+#include "error.h"
+
+/* SSC D-Bus definitions */
+#define SSC_DBUS_NAME  "com.nokia.phone.SSC"
+#define SSC_DBUS_IFACE "com.nokia.phone.SSC"
+#define SSC_DBUS_PATH  "/com/nokia/phone/SSC"
+
+/* libcsnet D-Bus definitions */
+#define CSD_CSNET_BUS_NAME     "com.nokia.csd.CSNet"
+#define CSD_CSNET_PATH         "/com/nokia/csd/csnet"
+#define CSD_CSNET_IFACE                "com.nokia.csd.CSNet"
+#define CSD_CSNET_REGISTRATION "com.nokia.csd.CSNet.NetworkRegistration"
+#define CSD_CSNET_OPERATOR     "com.nokia.csd.CSNet.NetworkOperator"
+#define CSD_CSNET_SIGNAL       "com.nokia.csd.CSNet.SignalStrength"
+
+enum net_registration_status {
+       NETWORK_REG_STATUS_HOME,
+       NETWORK_REG_STATUS_ROAMING,
+       NETWORK_REG_STATUS_OFFLINE,
+       NETWORK_REG_STATUS_SEARCHING,
+       NETWORK_REG_STATUS_NO_SIM,
+       NETWORK_REG_STATUS_POWEROFF,
+       NETWORK_REG_STATUS_POWERSAFE,
+       NETWORK_REG_STATUS_NO_COVERAGE,
+       NETWORK_REG_STATUS_REJECTED,
+       NETWORK_REG_STATUS_UNKOWN
+};
+
+/* CSD CALL plugin D-Bus definitions */
+#define CSD_CALL_BUS_NAME      "com.nokia.csd.Call"
+#define CSD_CALL_INTERFACE     "com.nokia.csd.Call"
+#define CSD_CALL_INSTANCE      "com.nokia.csd.Call.Instance"
+#define CSD_CALL_CONFERENCE    "com.nokia.csd.Call.Conference"
+#define CSD_CALL_PATH          "/com/nokia/csd/call"
+#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference"
+
+/* Call status values as exported by the CSD CALL plugin */
+#define CSD_CALL_STATUS_IDLE                   0
+#define CSD_CALL_STATUS_CREATE                 1
+#define CSD_CALL_STATUS_COMING                 2
+#define CSD_CALL_STATUS_PROCEEDING             3
+#define CSD_CALL_STATUS_MO_ALERTING            4
+#define CSD_CALL_STATUS_MT_ALERTING            5
+#define CSD_CALL_STATUS_WAITING                        6
+#define CSD_CALL_STATUS_ANSWERED               7
+#define CSD_CALL_STATUS_ACTIVE                 8
+#define CSD_CALL_STATUS_MO_RELEASE             9
+#define CSD_CALL_STATUS_MT_RELEASE             10
+#define CSD_CALL_STATUS_HOLD_INITIATED         11
+#define CSD_CALL_STATUS_HOLD                   12
+#define CSD_CALL_STATUS_RETRIEVE_INITIATED     13
+#define CSD_CALL_STATUS_RECONNECT_PENDING      14
+#define CSD_CALL_STATUS_TERMINATED             15
+#define CSD_CALL_STATUS_SWAP_INITIATED         16
+
+#define CALL_FLAG_NONE                         0
+#define CALL_FLAG_PRESENTATION_ALLOWED         0x01
+#define CALL_FLAG_PRESENTATION_RESTRICTED      0x02
+
+/* SIM Phonebook D-Bus definitions */
+#define CSD_SIMPB_BUS_NAME                     "com.nokia.csd.SIM"
+#define CSD_SIMPB_INTERFACE                    "com.nokia.csd.SIM.Phonebook"
+#define CSD_SIMPB_PATH                         "/com/nokia/csd/sim/phonebook"
+
+#define CSD_SIMPB_TYPE_ADN                     "ADN"
+#define CSD_SIMPB_TYPE_FDN                     "FDN"
+#define CSD_SIMPB_TYPE_SDN                     "SDN"
+#define CSD_SIMPB_TYPE_VMBX                    "VMBX"
+#define CSD_SIMPB_TYPE_MBDN                    "MBDN"
+#define CSD_SIMPB_TYPE_EN                      "EN"
+#define CSD_SIMPB_TYPE_MSISDN                  "MSISDN"
+
+/* OHM plugin D-Bus definitions */
+#define OHM_BUS_NAME           "com.nokia.NonGraphicFeedback1"
+#define OHM_INTERFACE          "com.nokia.NonGraphicFeedback1"
+#define OHM_PATH               "/com/nokia/NonGraphicFeedback1"
+
+/* tone-genenerator D-Bus definitions */
+#define TONEGEN_BUS_NAME       "com.Nokia.Telephony.Tones"
+#define TONEGEN_INTERFACE      "com.Nokia.Telephony.Tones"
+#define TONEGEN_PATH           "/com/Nokia/Telephony/Tones"
+
+/* tone-generator DTMF definitions */
+#define DTMF_ASTERISK   10
+#define DTMF_HASHMARK   11
+#define DTMF_A          12
+#define DTMF_B          13
+#define DTMF_C          14
+#define DTMF_D          15
+
+#define FEEDBACK_TONE_DURATION                 200
+
+struct csd_call {
+       char *object_path;
+       int status;
+       gboolean originating;
+       gboolean emergency;
+       gboolean on_hold;
+       gboolean conference;
+       char *number;
+       gboolean setup;
+};
+
+static struct {
+       char *operator_name;
+       uint8_t status;
+       int32_t signal_bars;
+} net = {
+       .operator_name = NULL,
+       .status = NETWORK_REG_STATUS_UNKOWN,
+       /* Init as 0 meaning inactive mode. In modem power off state
+        * can be be -1, but we treat all values as 0s regardless
+        * inactive or power off. */
+       .signal_bars = 0,
+};
+
+struct pending_req {
+       DBusPendingCall *call;
+       void *user_data;
+};
+
+static int get_property(const char *iface, const char *prop);
+
+static DBusConnection *connection = NULL;
+
+static GSList *calls = NULL;
+static GSList *watches = NULL;
+static GSList *pending = NULL;
+
+/* Reference count for determining the call indicator status */
+static GSList *active_calls = NULL;
+
+/* Queue of DTMF tones to play */
+static GSList *tones = NULL;
+static guint create_tones_timer = 0;
+
+static char *msisdn = NULL;    /* Subscriber number */
+static char *vmbx = NULL;      /* Voice mailbox number */
+
+/* HAL battery namespace key values */
+static int battchg_cur = -1;   /* "battery.charge_level.current" */
+static int battchg_last = -1;  /* "battery.charge_level.last_full" */
+static int battchg_design = -1;        /* "battery.charge_level.design" */
+
+static gboolean get_calls_active = FALSE;
+
+static gboolean events_enabled = FALSE;
+
+/* Supported set of call hold operations */
+static const char *chld_str = "0,1,1x,2,2x,3,4";
+
+/* Timer for tracking call creation requests */
+static guint create_request_timer = 0;
+
+static struct indicator maemo_indicators[] =
+{
+       { "battchg",    "0-5",  5,      TRUE },
+       /* signal strength in terms of bars */
+       { "signal",     "0-5",  0,      TRUE },
+       { "service",    "0,1",  0,      TRUE },
+       { "call",       "0,1",  0,      TRUE },
+       { "callsetup",  "0-3",  0,      TRUE },
+       { "callheld",   "0-2",  0,      FALSE },
+       { "roam",       "0,1",  0,      TRUE },
+       { NULL }
+};
+
+static char *call_status_str[] = {
+       "IDLE",
+       "CREATE",
+       "COMING",
+       "PROCEEDING",
+       "MO_ALERTING",
+       "MT_ALERTING",
+       "WAITING",
+       "ANSWERED",
+       "ACTIVE",
+       "MO_RELEASE",
+       "MT_RELEASE",
+       "HOLD_INITIATED",
+       "HOLD",
+       "RETRIEVE_INITIATED",
+       "RECONNECT_PENDING",
+       "TERMINATED",
+       "SWAP_INITIATED",
+       "???"
+};
+
+static int send_method_call(const char *dest, const char *path,
+                               const char *interface, const char *method,
+                               DBusPendingCallNotifyFunction cb,
+                               void *user_data, int type, ...)
+{
+       DBusMessage *msg;
+       DBusPendingCall *call;
+       va_list args;
+       struct pending_req *req;
+
+       msg = dbus_message_new_method_call(dest, path, interface, method);
+       if (!msg) {
+               error("Unable to allocate new D-Bus %s message", method);
+               return -ENOMEM;
+       }
+
+       va_start(args, type);
+
+       if (!dbus_message_append_args_valist(msg, type, args)) {
+               dbus_message_unref(msg);
+               va_end(args);
+               return -EIO;
+       }
+
+       va_end(args);
+
+       if (!cb) {
+               g_dbus_send_message(connection, msg);
+               return 0;
+       }
+
+       if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+               error("Sending %s failed", method);
+               dbus_message_unref(msg);
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(call, cb, user_data, NULL);
+
+       req = g_new0(struct pending_req, 1);
+       req->call = call;
+       req->user_data = user_data;
+
+       pending = g_slist_prepend(pending, req);
+       dbus_message_unref(msg);
+
+       return 0;
+}
+
+static struct csd_call *find_call(const char *path)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct csd_call *call = l->data;
+
+               if (g_str_equal(call->object_path, path))
+                       return call;
+       }
+
+       return NULL;
+}
+
+static struct csd_call *find_non_held_call(void)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct csd_call *call = l->data;
+
+               if (call->status == CSD_CALL_STATUS_IDLE)
+                       continue;
+
+               if (call->status != CSD_CALL_STATUS_HOLD)
+                       return call;
+       }
+
+       return NULL;
+}
+
+static struct csd_call *find_non_idle_call(void)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct csd_call *call = l->data;
+
+               if (call->status != CSD_CALL_STATUS_IDLE)
+                       return call;
+       }
+
+       return NULL;
+}
+
+static struct csd_call *find_call_with_status(int status)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct csd_call *call = l->data;
+
+               if (call->status == status)
+                       return call;
+       }
+
+       return NULL;
+}
+
+static int release_conference(void)
+{
+       DBusMessage *msg;
+
+       DBG("telephony-maemo6: releasing conference call");
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+                                               CSD_CALL_CONFERENCE_PATH,
+                                               CSD_CALL_INSTANCE,
+                                               "Release");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int release_call(struct csd_call *call)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+                                               call->object_path,
+                                               CSD_CALL_INSTANCE,
+                                               "Release");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int answer_call(struct csd_call *call)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+                                               call->object_path,
+                                               CSD_CALL_INSTANCE,
+                                               "Answer");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static struct pending_req *find_request(const DBusPendingCall *call)
+{
+       GSList *l;
+
+       for (l = pending; l; l = l->next) {
+               struct pending_req *req = l->data;
+
+               if (req->call == call)
+                       return req;
+       }
+
+       return NULL;
+}
+
+static void pending_req_finalize(void *data)
+{
+       struct pending_req *req = data;
+
+       if (!dbus_pending_call_get_completed(req->call))
+               dbus_pending_call_cancel(req->call);
+
+       dbus_pending_call_unref(req->call);
+       g_free(req);
+}
+
+static void remove_pending(DBusPendingCall *call)
+{
+       struct pending_req *req = find_request(call);
+
+       pending = g_slist_remove(pending, req);
+       pending_req_finalize(req);
+}
+
+static void stop_ringtone_reply(DBusPendingCall *call, void *user_data)
+{
+       struct csd_call *coming = user_data;
+
+       remove_pending(call);
+       answer_call(coming);
+}
+
+static int stop_ringtone_and_answer(struct csd_call *call)
+{
+       int ret;
+
+       ret = send_method_call(OHM_BUS_NAME, OHM_PATH,
+                               OHM_INTERFACE, "StopRingtone",
+                               stop_ringtone_reply, call,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0)
+               return answer_call(call);
+
+       return 0;
+}
+
+static int split_call(struct csd_call *call)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
+                                               call->object_path,
+                                               CSD_CALL_INSTANCE,
+                                               "Split");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int unhold_call(struct csd_call *call)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                                               CSD_CALL_INTERFACE,
+                                               "Unhold");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int hold_call(struct csd_call *call)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                                               CSD_CALL_INTERFACE,
+                                               "Hold");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int swap_calls(void)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                                               CSD_CALL_INTERFACE,
+                                               "Swap");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int create_conference(void)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                                               CSD_CALL_INTERFACE,
+                                               "Conference");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int call_transfer(void)
+{
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                                               CSD_CALL_INTERFACE,
+                                               "Transfer");
+       if (!msg) {
+               error("Unable to allocate new D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, msg);
+
+       return 0;
+}
+
+static int number_type(const char *number)
+{
+       if (number == NULL)
+               return NUMBER_TYPE_TELEPHONY;
+
+       if (number[0] == '+' || strncmp(number, "00", 2) == 0)
+               return NUMBER_TYPE_INTERNATIONAL;
+
+       return NUMBER_TYPE_TELEPHONY;
+}
+
+void telephony_device_connected(void *telephony_device)
+{
+       struct csd_call *coming;
+
+       DBG("telephony-maemo6: device %p connected", telephony_device);
+
+       coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+       if (coming) {
+               if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
+                       telephony_call_waiting_ind(coming->number,
+                                               number_type(coming->number));
+               else
+                       telephony_incoming_call_ind(coming->number,
+                                               number_type(coming->number));
+       }
+}
+
+static void remove_pending_by_data(gpointer data, gpointer user_data)
+{
+       struct pending_req *req = data;
+
+       if (req->user_data == user_data) {
+               pending = g_slist_remove(pending, req);
+               pending_req_finalize(req);
+       }
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+       DBG("telephony-maemo6: device %p disconnected", telephony_device);
+       events_enabled = FALSE;
+
+       g_slist_foreach(pending, remove_pending_by_data, telephony_device);
+}
+
+void telephony_event_reporting_req(void *telephony_device, int ind)
+{
+       events_enabled = ind == 1 ? TRUE : FALSE;
+
+       telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_response_and_hold_req(void *telephony_device, int rh)
+{
+       telephony_response_and_hold_rsp(telephony_device,
+                                               CME_ERROR_NOT_SUPPORTED);
+}
+
+void telephony_terminate_call_req(void *telephony_device)
+{
+       struct csd_call *call;
+       struct csd_call *alerting;
+       int err;
+
+       call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+       if (!call)
+               call = find_non_idle_call();
+
+       if (!call) {
+               error("No active call");
+               telephony_terminate_call_rsp(telephony_device,
+                                               CME_ERROR_NOT_ALLOWED);
+               return;
+       }
+
+       alerting = find_call_with_status(CSD_CALL_STATUS_MO_ALERTING);
+       if (call->on_hold && alerting)
+               err = release_call(alerting);
+       else if (call->conference)
+               err = release_conference();
+       else
+               err = release_call(call);
+
+       if (err < 0)
+               telephony_terminate_call_rsp(telephony_device,
+                                               CME_ERROR_AG_FAILURE);
+       else
+               telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_answer_call_req(void *telephony_device)
+{
+       struct csd_call *call;
+
+       call = find_call_with_status(CSD_CALL_STATUS_COMING);
+       if (!call)
+               call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+
+       if (!call)
+               call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
+
+       if (!call)
+               call = find_call_with_status(CSD_CALL_STATUS_WAITING);
+
+       if (!call) {
+               telephony_answer_call_rsp(telephony_device,
+                                               CME_ERROR_NOT_ALLOWED);
+               return;
+       }
+
+       if (stop_ringtone_and_answer(call) < 0)
+               telephony_answer_call_rsp(telephony_device,
+                                               CME_ERROR_AG_FAILURE);
+       else
+               telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static void create_call_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       void *telephony_device = user_data;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("csd replied with an error: %s, %s",
+                               err.name, err.message);
+               if (g_strcmp0(err.name,
+                               "com.nokia.csd.Call.Error.CSInactive") == 0)
+                       telephony_dial_number_rsp(telephony_device,
+                                               CME_ERROR_NO_NETWORK_SERVICE);
+               else
+                       telephony_dial_number_rsp(telephony_device,
+                                                       CME_ERROR_AG_FAILURE);
+               dbus_error_free(&err);
+       } else
+               telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
+
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+void telephony_last_dialed_number_req(void *telephony_device)
+{
+       int ret;
+
+       DBG("telephony-maemo6: last dialed number request");
+
+       ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                               CSD_CALL_INTERFACE, "CreateFromLast",
+                               create_call_reply, telephony_device,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0)
+               telephony_dial_number_rsp(telephony_device,
+                                               CME_ERROR_AG_FAILURE);
+}
+
+static const char *memory_dial_lookup(int location)
+{
+       if (location == 1)
+               return vmbx;
+       else
+               return NULL;
+}
+
+void telephony_dial_number_req(void *telephony_device, const char *number)
+{
+       int ret;
+
+       DBG("telephony-maemo6: dial request to %s", number);
+
+       if (strncmp(number, "*31#", 4) == 0)
+               number += 4;
+       else if (strncmp(number, "#31#", 4) == 0)
+               number += 4;
+       else if (number[0] == '>') {
+               const char *location = &number[1];
+
+               number = memory_dial_lookup(strtol(&number[1], NULL, 0));
+               if (!number) {
+                       error("No number at memory location %s", location);
+                       telephony_dial_number_rsp(telephony_device,
+                                               CME_ERROR_INVALID_INDEX);
+                       return;
+               }
+       }
+
+       ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                               CSD_CALL_INTERFACE, "Create",
+                               create_call_reply, telephony_device,
+                               DBUS_TYPE_STRING, &number,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0)
+               telephony_dial_number_rsp(telephony_device,
+                                               CME_ERROR_AG_FAILURE);
+}
+
+static void start_dtmf_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("csd replied with an error: %s, %s",
+                               err.name, err.message);
+
+               dbus_error_free(&err);
+       } else
+               send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                               CSD_CALL_INTERFACE, "StopDTMF",
+                               NULL, NULL,
+                               DBUS_TYPE_INVALID);
+
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+static void start_dtmf(void *telephony_device, char tone)
+{
+       int ret;
+
+       /*
+        * Stop tone immediately, modem will place it in queue and play
+        * required time.
+        */
+       ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                               CSD_CALL_INTERFACE, "StartDTMF",
+                               start_dtmf_reply, NULL,
+                               DBUS_TYPE_BYTE, &tone,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0) {
+               telephony_transmit_dtmf_rsp(telephony_device,
+                                               CME_ERROR_AG_FAILURE);
+               return;
+       }
+
+       telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static int tonegen_startevent(char tone)
+{
+       int ret;
+       dbus_uint32_t event_tone;
+       dbus_int32_t dbm0 = -15;
+       dbus_uint32_t duration = 150;
+
+       switch (tone) {
+       case '*':
+               event_tone = DTMF_ASTERISK;
+               break;
+       case '#':
+               event_tone = DTMF_HASHMARK;
+               break;
+       case 'A':
+               event_tone = DTMF_A;
+               break;
+       case 'B':
+               event_tone = DTMF_B;
+               break;
+       case 'C':
+               event_tone = DTMF_C;
+               break;
+       case 'D':
+               event_tone = DTMF_D;
+               break;
+       default:
+               ret = g_ascii_digit_value(tone);
+               if (ret < 0)
+                       return -EINVAL;
+               event_tone = ret;
+       }
+
+       ret = send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
+                               TONEGEN_INTERFACE, "StartEventTone",
+                               NULL, NULL,
+                               DBUS_TYPE_UINT32, &event_tone,
+                               DBUS_TYPE_INT32, &dbm0,
+                               DBUS_TYPE_UINT32, &duration,
+                               DBUS_TYPE_INVALID);
+       return ret;
+}
+
+static gboolean stop_feedback_tone(gpointer user_data)
+{
+       if (g_slist_length(tones) > 0) {
+               gpointer ptone;
+               int ret;
+
+               send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
+                               TONEGEN_INTERFACE, "StopTone",
+                               NULL, NULL,
+                               DBUS_TYPE_INVALID);
+
+               ptone = g_slist_nth_data(tones, 0);
+               tones = g_slist_remove(tones, ptone);
+
+               ret = tonegen_startevent(GPOINTER_TO_UINT(ptone));
+               if (ret < 0)
+                       goto done;
+
+               return TRUE;
+       }
+done:
+       return FALSE;
+}
+
+static void tones_timer_notify(gpointer data)
+{
+       send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
+                               TONEGEN_INTERFACE, "StopTone",
+                               NULL, NULL,
+                               DBUS_TYPE_INVALID);
+       g_slist_free(tones);
+       tones = NULL;
+
+       create_tones_timer = 0;
+}
+
+static void start_feedback_tone(char tone)
+{
+       if (!create_tones_timer) {
+               int ret;
+
+               ret = tonegen_startevent(tone);
+               if (ret < 0)
+                       return;
+
+               create_tones_timer = g_timeout_add_full(G_PRIORITY_DEFAULT,
+                                               FEEDBACK_TONE_DURATION,
+                                               stop_feedback_tone,
+                                               NULL,
+                                               tones_timer_notify);
+       } else {
+               glong dtmf_tone = tone;
+
+               DBG("add %c to queue", tone);
+               tones = g_slist_append(tones, GUINT_TO_POINTER(dtmf_tone));
+       }
+}
+
+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
+{
+       DBG("telephony-maemo6: transmit dtmf: %c", tone);
+
+       start_dtmf(telephony_device, tone);
+
+       if (!find_call_with_status(CSD_CALL_STATUS_ACTIVE))
+               error("No active call");
+       else
+               start_feedback_tone(tone);
+}
+
+void telephony_subscriber_number_req(void *telephony_device)
+{
+       DBG("telephony-maemo6: subscriber number request");
+       if (msisdn)
+               telephony_subscriber_number_ind(msisdn,
+                                               number_type(msisdn),
+                                               SUBSCRIBER_SERVICE_VOICE);
+       telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static int csd_status_to_hfp(struct csd_call *call)
+{
+       switch (call->status) {
+       case CSD_CALL_STATUS_IDLE:
+       case CSD_CALL_STATUS_MO_RELEASE:
+       case CSD_CALL_STATUS_MT_RELEASE:
+       case CSD_CALL_STATUS_TERMINATED:
+               return -1;
+       case CSD_CALL_STATUS_CREATE:
+               return CALL_STATUS_DIALING;
+       case CSD_CALL_STATUS_WAITING:
+               return CALL_STATUS_WAITING;
+       case CSD_CALL_STATUS_PROCEEDING:
+               /* PROCEEDING can happen in outgoing/incoming */
+               if (call->originating)
+                       return CALL_STATUS_DIALING;
+
+               /*
+                * PROCEEDING is followed by WAITING CSD status, therefore
+                * second incoming call status indication is set immediately
+                * to waiting.
+                */
+               if (g_slist_length(active_calls) > 0)
+                       return CALL_STATUS_WAITING;
+
+               return CALL_STATUS_INCOMING;
+       case CSD_CALL_STATUS_COMING:
+               if (g_slist_length(active_calls) > 0)
+                       return CALL_STATUS_WAITING;
+
+               return CALL_STATUS_INCOMING;
+       case CSD_CALL_STATUS_MO_ALERTING:
+               return CALL_STATUS_ALERTING;
+       case CSD_CALL_STATUS_MT_ALERTING:
+               return CALL_STATUS_INCOMING;
+       case CSD_CALL_STATUS_ANSWERED:
+       case CSD_CALL_STATUS_ACTIVE:
+       case CSD_CALL_STATUS_RECONNECT_PENDING:
+       case CSD_CALL_STATUS_SWAP_INITIATED:
+       case CSD_CALL_STATUS_HOLD_INITIATED:
+               return CALL_STATUS_ACTIVE;
+       case CSD_CALL_STATUS_RETRIEVE_INITIATED:
+       case CSD_CALL_STATUS_HOLD:
+               return CALL_STATUS_HELD;
+       default:
+               return -1;
+       }
+}
+
+void telephony_list_current_calls_req(void *telephony_device)
+{
+       GSList *l;
+       int i;
+
+       DBG("telephony-maemo6: list current calls request");
+
+       for (l = calls, i = 1; l != NULL; l = l->next, i++) {
+               struct csd_call *call = l->data;
+               int status, direction, multiparty;
+
+               status = csd_status_to_hfp(call);
+               if (status < 0)
+                       continue;
+
+               direction = call->originating ?
+                               CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
+
+               multiparty = call->conference ?
+                               CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
+
+               telephony_list_current_call_ind(i, direction, status,
+                                               CALL_MODE_VOICE, multiparty,
+                                               call->number,
+                                               number_type(call->number));
+       }
+
+       telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_operator_selection_req(void *telephony_device)
+{
+       telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
+                               net.operator_name ? net.operator_name : "");
+       telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static void foreach_call_with_status(int status,
+                                       int (*func)(struct csd_call *call))
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct csd_call *call = l->data;
+
+               if (call->status == status)
+                       func(call);
+       }
+}
+
+void telephony_call_hold_req(void *telephony_device, const char *cmd)
+{
+       const char *idx;
+       struct csd_call *call;
+       int err = 0;
+
+       DBG("telephony-maemo6: got call hold request %s", cmd);
+
+       if (strlen(cmd) > 1)
+               idx = &cmd[1];
+       else
+               idx = NULL;
+
+       if (idx)
+               call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
+       else
+               call = NULL;
+
+       switch (cmd[0]) {
+       case '0':
+               if (find_call_with_status(CSD_CALL_STATUS_WAITING))
+                       foreach_call_with_status(CSD_CALL_STATUS_WAITING,
+                                                               release_call);
+               else
+                       foreach_call_with_status(CSD_CALL_STATUS_HOLD,
+                                                               release_call);
+               break;
+       case '1':
+               if (idx) {
+                       if (call)
+                               err = release_call(call);
+                       break;
+               }
+               foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
+               call = find_call_with_status(CSD_CALL_STATUS_WAITING);
+               if (call)
+                       err = answer_call(call);
+               break;
+       case '2':
+               if (idx) {
+                       if (call)
+                               err = split_call(call);
+               } else {
+                       struct csd_call *held, *wait;
+
+                       call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+                       held = find_call_with_status(CSD_CALL_STATUS_HOLD);
+                       wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
+
+                       if (wait)
+                               err = answer_call(wait);
+                       else if (call && held)
+                               err = swap_calls();
+                       else {
+                               if (call)
+                                       err = hold_call(call);
+                               if (held)
+                                       err = unhold_call(held);
+                       }
+               }
+               break;
+       case '3':
+               if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
+                               find_call_with_status(CSD_CALL_STATUS_WAITING))
+                       err = create_conference();
+               break;
+       case '4':
+               err = call_transfer();
+               break;
+       default:
+               DBG("Unknown call hold request");
+               break;
+       }
+
+       if (err)
+               telephony_call_hold_rsp(telephony_device,
+                                       CME_ERROR_AG_FAILURE);
+       else
+               telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
+{
+       DBG("telephony-maemo6: got %s NR and EC request",
+                       enable ? "enable" : "disable");
+       telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_key_press_req(void *telephony_device, const char *keys)
+{
+       struct csd_call *active, *waiting;
+       int err;
+
+       DBG("telephony-maemo6: got key press request for %s", keys);
+
+       waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
+       if (!waiting)
+               waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
+       if (!waiting)
+               waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
+
+       active = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
+
+       if (waiting)
+               err = answer_call(waiting);
+       else if (active)
+               err = release_call(active);
+       else
+               err = 0;
+
+       if (err < 0)
+               telephony_key_press_rsp(telephony_device,
+                                                       CME_ERROR_AG_FAILURE);
+       else
+               telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
+{
+       DBG("telephony-maemo6: got %s voice dial request",
+                       enable ? "enable" : "disable");
+
+       telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
+}
+
+static void handle_incoming_call(DBusMessage *msg)
+{
+       const char *number, *call_path;
+       struct csd_call *call;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &call_path,
+                                       DBUS_TYPE_STRING, &number,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in Call.Coming() signal");
+               return;
+       }
+
+       call = find_call(call_path);
+       if (!call) {
+               error("Didn't find any matching call object for %s",
+                               call_path);
+               return;
+       }
+
+       DBG("Incoming call to %s from number %s", call_path, number);
+
+       g_free(call->number);
+       call->number = g_strdup(number);
+
+       if (find_call_with_status(CSD_CALL_STATUS_ACTIVE) ||
+                       find_call_with_status(CSD_CALL_STATUS_HOLD))
+               telephony_call_waiting_ind(call->number,
+                                               number_type(call->number));
+       else
+               telephony_incoming_call_ind(call->number,
+                                               number_type(call->number));
+
+       telephony_update_indicator(maemo_indicators, "callsetup",
+                                       EV_CALLSETUP_INCOMING);
+}
+
+static void handle_outgoing_call(DBusMessage *msg)
+{
+       const char *number, *call_path;
+       struct csd_call *call;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &call_path,
+                                       DBUS_TYPE_STRING, &number,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in Call.Created() signal");
+               return;
+       }
+
+       call = find_call(call_path);
+       if (!call) {
+               error("Didn't find any matching call object for %s",
+                               call_path);
+               return;
+       }
+
+       DBG("Outgoing call from %s to number %s", call_path, number);
+
+       g_free(call->number);
+       call->number = g_strdup(number);
+
+       if (create_request_timer) {
+               g_source_remove(create_request_timer);
+               create_request_timer = 0;
+       }
+}
+
+static gboolean create_timeout(gpointer user_data)
+{
+       telephony_update_indicator(maemo_indicators, "callsetup",
+                                       EV_CALLSETUP_INACTIVE);
+       create_request_timer = 0;
+       return FALSE;
+}
+
+static void handle_create_requested(DBusMessage *msg)
+{
+       DBG("Call.CreateRequested()");
+
+       if (create_request_timer)
+               g_source_remove(create_request_timer);
+
+       create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL);
+
+       telephony_update_indicator(maemo_indicators, "callsetup",
+                                       EV_CALLSETUP_OUTGOING);
+}
+
+static void call_set_status(struct csd_call *call, dbus_uint32_t status)
+{
+       dbus_uint32_t prev_status;
+       int callheld = telephony_get_indicator(maemo_indicators, "callheld");
+
+       prev_status = call->status;
+       DBG("Call %s changed from %s to %s", call->object_path,
+               call_status_str[prev_status], call_status_str[status]);
+
+       if (prev_status == status) {
+               DBG("Ignoring CSD Call state change to existing state");
+               return;
+       }
+
+       call->status = (int) status;
+
+       switch (status) {
+       case CSD_CALL_STATUS_IDLE:
+               if (call->setup) {
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callsetup",
+                                                       EV_CALLSETUP_INACTIVE);
+                       if (!call->originating)
+                               telephony_calling_stopped_ind();
+               }
+
+               g_free(call->number);
+               call->number = NULL;
+               call->originating = FALSE;
+               call->emergency = FALSE;
+               call->on_hold = FALSE;
+               call->conference = FALSE;
+               call->setup = FALSE;
+               break;
+       case CSD_CALL_STATUS_CREATE:
+               call->originating = TRUE;
+               call->setup = TRUE;
+               break;
+       case CSD_CALL_STATUS_COMING:
+               call->originating = FALSE;
+               call->setup = TRUE;
+               break;
+       case CSD_CALL_STATUS_PROCEEDING:
+               break;
+       case CSD_CALL_STATUS_MO_ALERTING:
+               telephony_update_indicator(maemo_indicators, "callsetup",
+                                               EV_CALLSETUP_ALERTING);
+               break;
+       case CSD_CALL_STATUS_MT_ALERTING:
+               /* Some headsets expect incoming call notification before they
+                * can send ATA command. When call changed status from waiting
+                * to alerting we need to send missing notification. Otherwise
+                * headsets like Nokia BH-108 or BackBeat 903 are unable to
+                * answer incoming call that was previously waiting. */
+               if (prev_status == CSD_CALL_STATUS_WAITING)
+                       telephony_incoming_call_ind(call->number,
+                                               number_type(call->number));
+               break;
+       case CSD_CALL_STATUS_WAITING:
+               break;
+       case CSD_CALL_STATUS_ANSWERED:
+               break;
+       case CSD_CALL_STATUS_ACTIVE:
+               if (call->on_hold) {
+                       call->on_hold = FALSE;
+                       if (find_call_with_status(CSD_CALL_STATUS_HOLD))
+                               telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_MULTIPLE);
+                       else
+                               telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_NONE);
+               } else {
+                       if (!g_slist_find(active_calls, call))
+                               active_calls = g_slist_prepend(active_calls, call);
+                       if (g_slist_length(active_calls) == 1)
+                               telephony_update_indicator(maemo_indicators,
+                                                               "call",
+                                                               EV_CALL_ACTIVE);
+                       /* Upgrade callheld status if necessary */
+                       if (callheld == EV_CALLHELD_ON_HOLD)
+                               telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_MULTIPLE);
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callsetup",
+                                                       EV_CALLSETUP_INACTIVE);
+                       if (!call->originating)
+                               telephony_calling_stopped_ind();
+                       call->setup = FALSE;
+               }
+               break;
+       case CSD_CALL_STATUS_MO_RELEASE:
+       case CSD_CALL_STATUS_MT_RELEASE:
+               active_calls = g_slist_remove(active_calls, call);
+               if (g_slist_length(active_calls) == 0)
+                       telephony_update_indicator(maemo_indicators, "call",
+                                                       EV_CALL_INACTIVE);
+
+               if (create_tones_timer)
+                       g_source_remove(create_tones_timer);
+               break;
+       case CSD_CALL_STATUS_HOLD_INITIATED:
+               break;
+       case CSD_CALL_STATUS_HOLD:
+               call->on_hold = TRUE;
+               if (find_non_held_call())
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_MULTIPLE);
+               else
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_ON_HOLD);
+               break;
+       case CSD_CALL_STATUS_RETRIEVE_INITIATED:
+               break;
+       case CSD_CALL_STATUS_RECONNECT_PENDING:
+               break;
+       case CSD_CALL_STATUS_TERMINATED:
+               if (call->on_hold &&
+                               !find_call_with_status(CSD_CALL_STATUS_HOLD)) {
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_NONE);
+                       return;
+               }
+
+               if (callheld == EV_CALLHELD_MULTIPLE &&
+                               find_call_with_status(CSD_CALL_STATUS_HOLD) &&
+                               !find_call_with_status(CSD_CALL_STATUS_ACTIVE))
+                       telephony_update_indicator(maemo_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_ON_HOLD);
+               break;
+       case CSD_CALL_STATUS_SWAP_INITIATED:
+               break;
+       default:
+               error("Unknown call status %u", status);
+               break;
+       }
+}
+
+static void handle_call_status(DBusMessage *msg, const char *call_path)
+{
+       struct csd_call *call;
+       dbus_uint32_t status, cause_type, cause;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_UINT32, &status,
+                                       DBUS_TYPE_UINT32, &cause_type,
+                                       DBUS_TYPE_UINT32, &cause,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in Instance.CallStatus() signal");
+               return;
+       }
+
+       call = find_call(call_path);
+       if (!call) {
+               error("Didn't find any matching call object for %s",
+                               call_path);
+               return;
+       }
+
+       if (status > 16) {
+               error("Invalid call status %u", status);
+               return;
+       }
+
+       call_set_status(call, status);
+}
+
+static void handle_conference(DBusMessage *msg, gboolean joined)
+{
+       const char *path;
+       struct csd_call *call;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &path,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in Conference.%s",
+                                       dbus_message_get_member(msg));
+               return;
+       }
+
+       call = find_call(path);
+       if (!call) {
+               error("Conference signal for unknown call %s", path);
+               return;
+       }
+
+       DBG("Call %s %s the conference", path, joined ? "joined" : "left");
+
+       call->conference = joined;
+}
+
+static uint8_t str2status(const char *state)
+{
+       if (g_strcmp0(state, "Home") == 0)
+               return NETWORK_REG_STATUS_HOME;
+       else if (g_strcmp0(state, "Roaming") == 0)
+               return NETWORK_REG_STATUS_ROAMING;
+       else if (g_strcmp0(state, "Offline") == 0)
+               return NETWORK_REG_STATUS_OFFLINE;
+       else if (g_strcmp0(state, "Searching") == 0)
+               return NETWORK_REG_STATUS_SEARCHING;
+       else if (g_strcmp0(state, "NoSim") == 0)
+               return NETWORK_REG_STATUS_NO_SIM;
+       else if (g_strcmp0(state, "Poweroff") == 0)
+               return NETWORK_REG_STATUS_POWEROFF;
+       else if (g_strcmp0(state, "Powersafe") == 0)
+               return NETWORK_REG_STATUS_POWERSAFE;
+       else if (g_strcmp0(state, "NoCoverage") == 0)
+               return NETWORK_REG_STATUS_NO_COVERAGE;
+       else if (g_strcmp0(state, "Reject") == 0)
+               return NETWORK_REG_STATUS_REJECTED;
+       else
+               return NETWORK_REG_STATUS_UNKOWN;
+}
+
+static void update_registration_status(const char *status)
+{
+       uint8_t new_status;
+
+       new_status = str2status(status);
+
+       if (net.status == new_status)
+               return;
+
+       switch (new_status) {
+       case NETWORK_REG_STATUS_HOME:
+               telephony_update_indicator(maemo_indicators, "roam",
+                                                       EV_ROAM_INACTIVE);
+               if (net.status > NETWORK_REG_STATUS_ROAMING)
+                       telephony_update_indicator(maemo_indicators,
+                                                       "service",
+                                                       EV_SERVICE_PRESENT);
+               break;
+       case NETWORK_REG_STATUS_ROAMING:
+               telephony_update_indicator(maemo_indicators, "roam",
+                                                       EV_ROAM_ACTIVE);
+               if (net.status > NETWORK_REG_STATUS_ROAMING)
+                       telephony_update_indicator(maemo_indicators,
+                                                       "service",
+                                                       EV_SERVICE_PRESENT);
+               break;
+       case NETWORK_REG_STATUS_OFFLINE:
+       case NETWORK_REG_STATUS_SEARCHING:
+       case NETWORK_REG_STATUS_NO_SIM:
+       case NETWORK_REG_STATUS_POWEROFF:
+       case NETWORK_REG_STATUS_POWERSAFE:
+       case NETWORK_REG_STATUS_NO_COVERAGE:
+       case NETWORK_REG_STATUS_REJECTED:
+       case NETWORK_REG_STATUS_UNKOWN:
+               if (net.status < NETWORK_REG_STATUS_OFFLINE)
+                       telephony_update_indicator(maemo_indicators,
+                                                       "service",
+                                                       EV_SERVICE_NONE);
+               break;
+       }
+
+       net.status = new_status;
+
+       DBG("telephony-maemo6: registration status changed: %s", status);
+}
+
+static void handle_registration_changed(DBusMessage *msg)
+{
+       const char *status;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_STRING, &status,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in RegistrationChanged");
+               return;
+       }
+
+       update_registration_status(status);
+}
+
+static void update_signal_strength(int32_t signal_bars)
+{
+       if (signal_bars < 0) {
+               DBG("signal strength smaller than expected: %d < 0",
+                                                               signal_bars);
+               signal_bars = 0;
+       } else if (signal_bars > 5) {
+               DBG("signal strength greater than expected: %d > 5",
+                                                               signal_bars);
+               signal_bars = 5;
+       }
+
+       if (net.signal_bars == signal_bars)
+               return;
+
+       telephony_update_indicator(maemo_indicators, "signal", signal_bars);
+
+       net.signal_bars = signal_bars;
+       DBG("telephony-maemo6: signal strength updated: %d/5", signal_bars);
+}
+
+static void handle_signal_bars_changed(DBusMessage *msg)
+{
+       int32_t signal_bars;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_INT32, &signal_bars,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in SignalBarsChanged");
+               return;
+       }
+
+       update_signal_strength(signal_bars);
+}
+
+static gboolean iter_get_basic_args(DBusMessageIter *iter,
+                                       int first_arg_type, ...)
+{
+       int type;
+       va_list ap;
+
+       va_start(ap, first_arg_type);
+
+       for (type = first_arg_type; type != DBUS_TYPE_INVALID;
+                       type = va_arg(ap, int)) {
+               void *value = va_arg(ap, void *);
+               int real_type = dbus_message_iter_get_arg_type(iter);
+
+               if (real_type != type) {
+                       error("iter_get_basic_args: expected %c but got %c",
+                                       (char) type, (char) real_type);
+                       break;
+               }
+
+               dbus_message_iter_get_basic(iter, value);
+               dbus_message_iter_next(iter);
+       }
+
+       va_end(ap);
+
+       return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
+}
+
+static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       dbus_int32_t level;
+       int *value = user_data;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("hald replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (!dbus_message_get_args(reply, NULL,
+                               DBUS_TYPE_INT32, &level,
+                               DBUS_TYPE_INVALID)) {
+               error("Unexpected args in hald reply");
+               goto done;
+       }
+
+       *value = (int) level;
+
+       if (value == &battchg_last)
+               DBG("telephony-maemo6: battery.charge_level.last_full is %d",
+                               *value);
+       else if (value == &battchg_design)
+               DBG("telephony-maemo6: battery.charge_level.design is %d",
+                               *value);
+       else
+               DBG("telephony-maemo6: battery.charge_level.current is %d",
+                               *value);
+
+       if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
+               int new, max;
+
+               if (battchg_last > 0)
+                       max = battchg_last;
+               else
+                       max = battchg_design;
+
+               new = battchg_cur * 5 / max;
+
+               telephony_update_indicator(maemo_indicators, "battchg", new);
+       }
+
+done:
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+static void hal_get_integer(const char *path, const char *key, void *user_data)
+{
+       send_method_call("org.freedesktop.Hal", path,
+                               "org.freedesktop.Hal.Device",
+                               "GetPropertyInteger",
+                               hal_battery_level_reply, user_data,
+                               DBUS_TYPE_STRING, &key,
+                               DBUS_TYPE_INVALID);
+}
+
+static void handle_hal_property_modified(DBusMessage *msg)
+{
+       DBusMessageIter iter, array;
+       dbus_int32_t num_changes;
+       const char *path;
+
+       path = dbus_message_get_path(msg);
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+               error("Unexpected signature in hal PropertyModified signal");
+               return;
+       }
+
+       dbus_message_iter_get_basic(&iter, &num_changes);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature in hal PropertyModified signal");
+               return;
+       }
+
+       dbus_message_iter_recurse(&iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+               DBusMessageIter prop;
+               const char *name;
+               dbus_bool_t added, removed;
+
+               dbus_message_iter_recurse(&array, &prop);
+
+               if (!iter_get_basic_args(&prop,
+                                       DBUS_TYPE_STRING, &name,
+                                       DBUS_TYPE_BOOLEAN, &added,
+                                       DBUS_TYPE_BOOLEAN, &removed,
+                                       DBUS_TYPE_INVALID)) {
+                       error("Invalid hal PropertyModified parameters");
+                       break;
+               }
+
+               if (g_str_equal(name, "battery.charge_level.last_full"))
+                       hal_get_integer(path, name, &battchg_last);
+               else if (g_str_equal(name, "battery.charge_level.current"))
+                       hal_get_integer(path, name, &battchg_cur);
+               else if (g_str_equal(name, "battery.charge_level.design"))
+                       hal_get_integer(path, name, &battchg_design);
+
+               dbus_message_iter_next(&array);
+       }
+}
+
+static void csd_call_free(void *data)
+{
+       struct csd_call *call = data;
+
+       if (!call)
+               return;
+
+       g_free(call->object_path);
+       g_free(call->number);
+
+       g_slist_foreach(pending, remove_pending_by_data, call);
+
+       g_free(call);
+}
+
+static void parse_call_list(DBusMessageIter *iter)
+{
+       do {
+               DBusMessageIter call_iter;
+               struct csd_call *call;
+               const char *object_path, *number;
+               dbus_uint32_t status;
+               dbus_bool_t originating, terminating, emerg, on_hold, conf;
+
+               if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
+                       error("Unexpected signature in GetCallInfoAll reply");
+                       break;
+               }
+
+               dbus_message_iter_recurse(iter, &call_iter);
+
+               if (!iter_get_basic_args(&call_iter,
+                                       DBUS_TYPE_OBJECT_PATH, &object_path,
+                                       DBUS_TYPE_UINT32, &status,
+                                       DBUS_TYPE_BOOLEAN, &originating,
+                                       DBUS_TYPE_BOOLEAN, &terminating,
+                                       DBUS_TYPE_BOOLEAN, &emerg,
+                                       DBUS_TYPE_BOOLEAN, &on_hold,
+                                       DBUS_TYPE_BOOLEAN, &conf,
+                                       DBUS_TYPE_STRING, &number,
+                                       DBUS_TYPE_INVALID)) {
+                       error("Parsing call D-Bus parameters failed");
+                       break;
+               }
+
+               call = find_call(object_path);
+               if (!call) {
+                       call = g_new0(struct csd_call, 1);
+                       call->object_path = g_strdup(object_path);
+                       calls = g_slist_append(calls, call);
+                       DBG("telephony-maemo6: new csd call instance at %s",
+                                                               object_path);
+               }
+
+               if (status == CSD_CALL_STATUS_IDLE)
+                       continue;
+
+               /* CSD gives incorrect call_hold property sometimes */
+               if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) ||
+                               (call->status == CSD_CALL_STATUS_HOLD &&
+                                                               !on_hold)) {
+                       error("Conflicting call status and on_hold property!");
+                       on_hold = call->status == CSD_CALL_STATUS_HOLD;
+               }
+
+               call->originating = originating;
+               call->on_hold = on_hold;
+               call->conference = conf;
+               g_free(call->number);
+               call->number = g_strdup(number);
+
+               /* Update indicators */
+               call_set_status(call, status);
+
+       } while (dbus_message_iter_next(iter));
+}
+
+static void update_operator_name(const char *name)
+{
+       if (name == NULL)
+               return;
+
+       g_free(net.operator_name);
+       net.operator_name = g_strndup(name, 16);
+       DBG("telephony-maemo6: operator name updated: %s", name);
+}
+
+static void get_property_reply(DBusPendingCall *call, void *user_data)
+{
+       char *prop = user_data;
+       DBusError err;
+       DBusMessage *reply;
+       DBusMessageIter iter, sub;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("csd replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
+               error("Unexpected signature in Get return");
+               goto done;
+       }
+
+       dbus_message_iter_recurse(&iter, &sub);
+
+       if (g_strcmp0(prop, "RegistrationStatus") == 0) {
+               const char *status;
+
+               dbus_message_iter_get_basic(&sub, &status);
+               update_registration_status(status);
+
+               get_property(CSD_CSNET_OPERATOR, "OperatorName");
+               get_property(CSD_CSNET_SIGNAL, "SignalBars");
+       } else if (g_strcmp0(prop, "OperatorName") == 0) {
+               const char *name;
+
+               dbus_message_iter_get_basic(&sub, &name);
+               update_operator_name(name);
+       } else if (g_strcmp0(prop, "SignalBars") == 0) {
+               int32_t signal_bars;
+
+               dbus_message_iter_get_basic(&sub, &signal_bars);
+               update_signal_strength(signal_bars);
+       }
+
+done:
+       g_free(prop);
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+static int get_property(const char *iface, const char *prop)
+{
+       return send_method_call(CSD_CSNET_BUS_NAME, CSD_CSNET_PATH,
+                               DBUS_INTERFACE_PROPERTIES, "Get",
+                               get_property_reply, g_strdup(prop),
+                               DBUS_TYPE_STRING, &iface,
+                               DBUS_TYPE_STRING, &prop,
+                               DBUS_TYPE_INVALID);
+}
+
+static void handle_operator_name_changed(DBusMessage *msg)
+{
+       const char *name;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_STRING, &name,
+                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected parameters in OperatorNameChanged");
+               return;
+       }
+
+       update_operator_name(name);
+}
+
+static void call_info_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       DBusMessageIter iter, sub;
+
+       get_calls_active = FALSE;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("csd replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature in GetCallInfoAll return");
+               goto done;
+       }
+
+       dbus_message_iter_recurse(&iter, &sub);
+
+       parse_call_list(&sub);
+
+       get_property(CSD_CSNET_REGISTRATION, "RegistrationStatus");
+
+done:
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+
+static void phonebook_read_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError derr;
+       DBusMessage *reply;
+       const char *name, *number, *secondname, *additionalnumber, *email;
+       int index;
+       char **number_type = user_data;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("%s.ReadFirst replied with an error: %s, %s",
+                               CSD_SIMPB_INTERFACE, derr.name, derr.message);
+               dbus_error_free(&derr);
+               if (number_type == &vmbx)
+                       vmbx = g_strdup(getenv("VMBX_NUMBER"));
+               goto done;
+       }
+
+       dbus_error_init(&derr);
+       if (dbus_message_get_args(reply, NULL,
+                               DBUS_TYPE_INT32, &index,
+                               DBUS_TYPE_STRING, &name,
+                               DBUS_TYPE_STRING, &number,
+                               DBUS_TYPE_STRING, &secondname,
+                               DBUS_TYPE_STRING, &additionalnumber,
+                               DBUS_TYPE_STRING, &email,
+                               DBUS_TYPE_INVALID) == FALSE) {
+               error("Unable to parse %s.ReadFirst arguments: %s, %s",
+                               CSD_SIMPB_INTERFACE, derr.name, derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       if (number_type == &msisdn) {
+               g_free(msisdn);
+               msisdn = g_strdup(number);
+               DBG("Got MSISDN %s (%s)", number, name);
+       } else {
+               g_free(vmbx);
+               vmbx = g_strdup(number);
+               DBG("Got voice mailbox number %s (%s)", number, name);
+       }
+
+done:
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+static void csd_init(void)
+{
+       const char *pb_type;
+       int ret;
+
+       ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+                               CSD_CALL_INTERFACE, "GetCallInfoAll",
+                               call_info_reply, NULL, DBUS_TYPE_INVALID);
+       if (ret < 0) {
+               error("Unable to sent GetCallInfoAll method call");
+               return;
+       }
+
+       get_calls_active = TRUE;
+
+       pb_type = CSD_SIMPB_TYPE_MSISDN;
+
+       ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
+                               CSD_SIMPB_INTERFACE, "ReadFirst",
+                               phonebook_read_reply, &msisdn,
+                               DBUS_TYPE_STRING, &pb_type,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0) {
+               error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
+               return;
+       }
+
+       /* Voicemail should be in MBDN index 0 */
+       pb_type = CSD_SIMPB_TYPE_MBDN;
+
+       ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
+                               CSD_SIMPB_INTERFACE, "ReadFirst",
+                               phonebook_read_reply, &vmbx,
+                               DBUS_TYPE_STRING, &pb_type,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0) {
+               error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
+               return;
+       }
+}
+
+static void handle_modem_state(DBusMessage *msg)
+{
+       const char *state;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state,
+                                                       DBUS_TYPE_INVALID)) {
+               error("Unexpected modem state parameters");
+               return;
+       }
+
+       DBG("SSC modem state: %s", state);
+
+       if (calls != NULL || get_calls_active)
+               return;
+
+       if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online"))
+               csd_init();
+}
+
+static void modem_state_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError err;
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("get_modem_state: %s, %s", err.name, err.message);
+               dbus_error_free(&err);
+       } else
+               handle_modem_state(reply);
+
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+static gboolean signal_filter(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *path = dbus_message_get_path(msg);
+
+       if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
+               handle_incoming_call(msg);
+       else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
+               handle_outgoing_call(msg);
+       else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
+                                                       "CreateRequested"))
+               handle_create_requested(msg);
+       else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
+               handle_call_status(msg, path);
+       else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
+               handle_conference(msg, TRUE);
+       else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
+               handle_conference(msg, FALSE);
+       else if (dbus_message_is_signal(msg, CSD_CSNET_REGISTRATION,
+                               "RegistrationChanged"))
+               handle_registration_changed(msg);
+       else if (dbus_message_is_signal(msg, CSD_CSNET_OPERATOR,
+                               "OperatorNameChanged"))
+               handle_operator_name_changed(msg);
+       else if (dbus_message_is_signal(msg, CSD_CSNET_SIGNAL,
+                               "SignalBarsChanged"))
+               handle_signal_bars_changed(msg);
+       else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
+                                       "PropertyModified"))
+               handle_hal_property_modified(msg);
+       else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE,
+                                               "modem_state_changed_ind"))
+               handle_modem_state(msg);
+
+       return TRUE;
+}
+
+static void add_watch(const char *sender, const char *path,
+                               const char *interface, const char *member)
+{
+       guint watch;
+
+       watch = g_dbus_add_signal_watch(connection, sender, path, interface,
+                                       member, signal_filter, NULL, NULL);
+
+       watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
+}
+
+static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       DBusMessageIter iter, sub;
+       const char *path;
+       int type;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("hald replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature in FindDeviceByCapability return");
+               goto done;
+       }
+
+       dbus_message_iter_recurse(&iter, &sub);
+
+       type = dbus_message_iter_get_arg_type(&sub);
+
+       if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
+               error("No hal device with battery capability found");
+               goto done;
+       }
+
+       dbus_message_iter_get_basic(&sub, &path);
+
+       DBG("telephony-maemo6: found battery device at %s", path);
+
+       add_watch(NULL, path, "org.freedesktop.Hal.Device",
+                                                       "PropertyModified");
+
+       hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
+       hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
+       hal_get_integer(path, "battery.charge_level.design", &battchg_design);
+
+done:
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+int telephony_init(void)
+{
+       const char *battery_cap = "battery";
+       uint32_t features = AG_FEATURE_EC_ANDOR_NR |
+                               AG_FEATURE_INBAND_RINGTONE |
+                               AG_FEATURE_REJECT_A_CALL |
+                               AG_FEATURE_ENHANCED_CALL_STATUS |
+                               AG_FEATURE_ENHANCED_CALL_CONTROL |
+                               AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
+                               AG_FEATURE_THREE_WAY_CALLING;
+       int i;
+
+       DBG("");
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+       add_watch(NULL, NULL, CSD_CALL_INTERFACE, NULL);
+       add_watch(NULL, NULL, CSD_CALL_INSTANCE, NULL);
+       add_watch(NULL, NULL, CSD_CALL_CONFERENCE, NULL);
+       add_watch(NULL, NULL, CSD_CSNET_REGISTRATION, "RegistrationChanged");
+       add_watch(NULL, NULL, CSD_CSNET_OPERATOR, "OperatorNameChanged");
+       add_watch(NULL, NULL, CSD_CSNET_SIGNAL, "SignalBarsChanged");
+       add_watch(NULL, NULL, SSC_DBUS_IFACE, "modem_state_changed_ind");
+
+       if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE,
+                                       "get_modem_state", modem_state_reply,
+                                       NULL, DBUS_TYPE_INVALID) < 0)
+               error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()");
+
+       /* Reset indicators */
+       for (i = 0; maemo_indicators[i].desc != NULL; i++) {
+               if (g_str_equal(maemo_indicators[i].desc, "battchg"))
+                       maemo_indicators[i].val = 5;
+               else
+                       maemo_indicators[i].val = 0;
+       }
+
+       telephony_ready_ind(features, maemo_indicators, BTRH_NOT_SUPPORTED,
+                                                               chld_str);
+       if (send_method_call("org.freedesktop.Hal",
+                               "/org/freedesktop/Hal/Manager",
+                               "org.freedesktop.Hal.Manager",
+                               "FindDeviceByCapability",
+                               hal_find_device_reply, NULL,
+                               DBUS_TYPE_STRING, &battery_cap,
+                               DBUS_TYPE_INVALID) < 0)
+               error("Unable to send HAL method call");
+
+       return 0;
+}
+
+static void remove_watch(gpointer data)
+{
+       g_dbus_remove_watch(connection, GPOINTER_TO_UINT(data));
+}
+
+void telephony_exit(void)
+{
+       DBG("");
+
+       g_free(net.operator_name);
+       net.operator_name = NULL;
+
+       net.status = NETWORK_REG_STATUS_UNKOWN;
+       net.signal_bars = 0;
+
+       g_slist_free(active_calls);
+       active_calls = NULL;
+
+       g_slist_free_full(calls, csd_call_free);
+       calls = NULL;
+
+       g_slist_free_full(pending, pending_req_finalize);
+       pending = NULL;
+
+       g_slist_free_full(watches, remove_watch);
+       watches = NULL;
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+
+       telephony_deinit();
+}
diff --git a/audio/telephony-ofono.c b/audio/telephony-ofono.c
new file mode 100644 (file)
index 0000000..961fedd
--- /dev/null
@@ -0,0 +1,1637 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Intel Corporation
+ *  Copyright (C) 2006-2009  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include <bluetooth/sdp.h>
+
+#include "log.h"
+#include "telephony.h"
+
+enum net_registration_status {
+       NETWORK_REG_STATUS_HOME = 0x00,
+       NETWORK_REG_STATUS_ROAM,
+       NETWORK_REG_STATUS_NOSERV
+};
+
+struct voice_call {
+       char *obj_path;
+       int status;
+       gboolean originating;
+       gboolean conference;
+       char *number;
+       guint watch;
+};
+
+static DBusConnection *connection = NULL;
+static char *modem_obj_path = NULL;
+static char *last_dialed_number = NULL;
+static GSList *calls = NULL;
+static GSList *watches = NULL;
+static GSList *pending = NULL;
+
+#define OFONO_BUS_NAME "org.ofono"
+#define OFONO_PATH "/"
+#define OFONO_MODEM_INTERFACE "org.ofono.Modem"
+#define OFONO_MANAGER_INTERFACE "org.ofono.Manager"
+#define OFONO_NETWORKREG_INTERFACE "org.ofono.NetworkRegistration"
+#define OFONO_VCMANAGER_INTERFACE "org.ofono.VoiceCallManager"
+#define OFONO_VC_INTERFACE "org.ofono.VoiceCall"
+
+/* HAL battery namespace key values */
+static int battchg_cur = -1;    /* "battery.charge_level.current" */
+static int battchg_last = -1;   /* "battery.charge_level.last_full" */
+static int battchg_design = -1; /* "battery.charge_level.design" */
+
+static struct {
+       uint8_t status;
+       uint32_t signals_bar;
+       char *operator_name;
+} net = {
+       .status = NETWORK_REG_STATUS_NOSERV,
+       .signals_bar = 0,
+       .operator_name = NULL,
+};
+
+static const char *chld_str = "0,1,1x,2,2x,3,4";
+static char *subscriber_number = NULL;
+
+static gboolean events_enabled = FALSE;
+
+static struct indicator ofono_indicators[] =
+{
+       { "battchg",    "0-5",  5,      TRUE },
+       { "signal",     "0-5",  5,      TRUE },
+       { "service",    "0,1",  1,      TRUE },
+       { "call",       "0,1",  0,      TRUE },
+       { "callsetup",  "0-3",  0,      TRUE },
+       { "callheld",   "0-2",  0,      FALSE },
+       { "roam",       "0,1",  0,      TRUE },
+       { NULL }
+};
+
+static struct voice_call *find_vc(const char *path)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct voice_call *vc = l->data;
+
+               if (g_str_equal(vc->obj_path, path))
+                       return vc;
+       }
+
+       return NULL;
+}
+
+static struct voice_call *find_vc_with_status(int status)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct voice_call *vc = l->data;
+
+               if (vc->status == status)
+                       return vc;
+       }
+
+       return NULL;
+}
+
+static struct voice_call *find_vc_without_status(int status)
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct voice_call *call = l->data;
+
+               if (call->status != status)
+                       return call;
+       }
+
+       return NULL;
+}
+
+static int number_type(const char *number)
+{
+       if (number == NULL)
+               return NUMBER_TYPE_TELEPHONY;
+
+       if (number[0] == '+' || strncmp(number, "00", 2) == 0)
+               return NUMBER_TYPE_INTERNATIONAL;
+
+       return NUMBER_TYPE_TELEPHONY;
+}
+
+void telephony_device_connected(void *telephony_device)
+{
+       struct voice_call *coming;
+
+       DBG("telephony-ofono: device %p connected", telephony_device);
+
+       coming = find_vc_with_status(CALL_STATUS_ALERTING);
+       if (coming) {
+               if (find_vc_with_status(CALL_STATUS_ACTIVE))
+                       telephony_call_waiting_ind(coming->number,
+                                               number_type(coming->number));
+               else
+                       telephony_incoming_call_ind(coming->number,
+                                               number_type(coming->number));
+       }
+}
+
+void telephony_device_disconnected(void *telephony_device)
+{
+       DBG("telephony-ofono: device %p disconnected", telephony_device);
+       events_enabled = FALSE;
+}
+
+void telephony_event_reporting_req(void *telephony_device, int ind)
+{
+       events_enabled = ind == 1 ? TRUE : FALSE;
+
+       telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_response_and_hold_req(void *telephony_device, int rh)
+{
+       telephony_response_and_hold_rsp(telephony_device,
+                                               CME_ERROR_NOT_SUPPORTED);
+}
+
+void telephony_last_dialed_number_req(void *telephony_device)
+{
+       DBG("telephony-ofono: last dialed number request");
+
+       if (last_dialed_number)
+               telephony_dial_number_req(telephony_device, last_dialed_number);
+       else
+               telephony_last_dialed_number_rsp(telephony_device,
+                               CME_ERROR_NOT_ALLOWED);
+}
+
+static int send_method_call(const char *dest, const char *path,
+                                const char *interface, const char *method,
+                                DBusPendingCallNotifyFunction cb,
+                                void *user_data, int type, ...)
+{
+       DBusMessage *msg;
+       DBusPendingCall *call;
+       va_list args;
+
+       msg = dbus_message_new_method_call(dest, path, interface, method);
+       if (!msg) {
+               error("Unable to allocate new D-Bus %s message", method);
+               return -ENOMEM;
+       }
+
+       va_start(args, type);
+
+       if (!dbus_message_append_args_valist(msg, type, args)) {
+               dbus_message_unref(msg);
+               va_end(args);
+               return -EIO;
+       }
+
+       va_end(args);
+
+       if (!cb) {
+               g_dbus_send_message(connection, msg);
+               return 0;
+       }
+
+       if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
+               error("Sending %s failed", method);
+               dbus_message_unref(msg);
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(call, cb, user_data, NULL);
+       pending = g_slist_prepend(pending, call);
+       dbus_message_unref(msg);
+
+       return 0;
+}
+
+static int answer_call(struct voice_call *vc)
+{
+       DBG("%s", vc->number);
+       return send_method_call(OFONO_BUS_NAME, vc->obj_path,
+                                               OFONO_VC_INTERFACE, "Answer",
+                                               NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int release_call(struct voice_call *vc)
+{
+       DBG("%s", vc->number);
+       return send_method_call(OFONO_BUS_NAME, vc->obj_path,
+                                               OFONO_VC_INTERFACE, "Hangup",
+                                               NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int release_answer_calls(void)
+{
+       DBG("");
+       return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+                                               OFONO_VCMANAGER_INTERFACE,
+                                               "ReleaseAndAnswer",
+                                               NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int split_call(struct voice_call *call)
+{
+       DBG("%s", call->number);
+       return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+                                               OFONO_VCMANAGER_INTERFACE,
+                                               "PrivateChat",
+                                               NULL, NULL,
+                                               DBUS_TYPE_OBJECT_PATH,
+                                               call->obj_path,
+                                               DBUS_TYPE_INVALID);
+       return -1;
+}
+
+static int swap_calls(void)
+{
+       DBG("");
+       return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+                                               OFONO_VCMANAGER_INTERFACE,
+                                               "SwapCalls",
+                                               NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int create_conference(void)
+{
+       DBG("");
+       return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+                                               OFONO_VCMANAGER_INTERFACE,
+                                               "CreateMultiparty",
+                                               NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int release_conference(void)
+{
+       DBG("");
+       return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+                                               OFONO_VCMANAGER_INTERFACE,
+                                               "HangupMultiparty",
+                                               NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+static int call_transfer(void)
+{
+       DBG("");
+       return send_method_call(OFONO_BUS_NAME, modem_obj_path,
+                                               OFONO_VCMANAGER_INTERFACE,
+                                               "Transfer",
+                                               NULL, NULL, DBUS_TYPE_INVALID);
+}
+
+void telephony_terminate_call_req(void *telephony_device)
+{
+       struct voice_call *call;
+       struct voice_call *alerting;
+       int err;
+
+       call = find_vc_with_status(CALL_STATUS_ACTIVE);
+       if (!call)
+               call = calls->data;
+
+       if (!call) {
+               error("No active call");
+               telephony_terminate_call_rsp(telephony_device,
+                                               CME_ERROR_NOT_ALLOWED);
+               return;
+       }
+
+       alerting = find_vc_with_status(CALL_STATUS_ALERTING);
+       if (call->status == CALL_STATUS_HELD && alerting)
+               err = release_call(alerting);
+       else if (call->conference)
+               err = release_conference();
+       else
+               err = release_call(call);
+
+       if (err < 0)
+               telephony_terminate_call_rsp(telephony_device,
+                                               CME_ERROR_AG_FAILURE);
+       else
+               telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_answer_call_req(void *telephony_device)
+{
+       struct voice_call *vc;
+       int ret;
+
+       vc = find_vc_with_status(CALL_STATUS_INCOMING);
+       if (!vc)
+               vc = find_vc_with_status(CALL_STATUS_ALERTING);
+
+       if (!vc)
+               vc = find_vc_with_status(CALL_STATUS_WAITING);
+
+       if (!vc) {
+               telephony_answer_call_rsp(telephony_device,
+                                       CME_ERROR_NOT_ALLOWED);
+               return;
+       }
+
+       ret = answer_call(vc);
+       if (ret < 0) {
+               telephony_answer_call_rsp(telephony_device,
+                                       CME_ERROR_AG_FAILURE);
+               return;
+       }
+
+       telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_dial_number_req(void *telephony_device, const char *number)
+{
+       const char *clir;
+       int ret;
+
+       DBG("telephony-ofono: dial request to %s", number);
+
+       if (!modem_obj_path) {
+               telephony_dial_number_rsp(telephony_device,
+                                       CME_ERROR_AG_FAILURE);
+               return;
+       }
+
+       if (!strncmp(number, "*31#", 4)) {
+               number += 4;
+               clir = "enabled";
+       } else if (!strncmp(number, "#31#", 4)) {
+               number += 4;
+               clir =  "disabled";
+       } else
+               clir = "default";
+
+       ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
+                       OFONO_VCMANAGER_INTERFACE,
+                        "Dial", NULL, NULL,
+                       DBUS_TYPE_STRING, &number,
+                       DBUS_TYPE_STRING, &clir,
+                       DBUS_TYPE_INVALID);
+
+       if (ret < 0)
+               telephony_dial_number_rsp(telephony_device,
+                       CME_ERROR_AG_FAILURE);
+       else
+               telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
+{
+       char *tone_string;
+       int ret;
+
+       DBG("telephony-ofono: transmit dtmf: %c", tone);
+
+       if (!modem_obj_path) {
+               telephony_transmit_dtmf_rsp(telephony_device,
+                                       CME_ERROR_AG_FAILURE);
+               return;
+       }
+
+       tone_string = g_strdup_printf("%c", tone);
+       ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
+                       OFONO_VCMANAGER_INTERFACE,
+                       "SendTones", NULL, NULL,
+                       DBUS_TYPE_STRING, &tone_string,
+                       DBUS_TYPE_INVALID);
+       g_free(tone_string);
+
+       if (ret < 0)
+               telephony_transmit_dtmf_rsp(telephony_device,
+                       CME_ERROR_AG_FAILURE);
+       else
+               telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_subscriber_number_req(void *telephony_device)
+{
+       DBG("telephony-ofono: subscriber number request");
+
+       if (subscriber_number)
+               telephony_subscriber_number_ind(subscriber_number,
+                                               NUMBER_TYPE_TELEPHONY,
+                                               SUBSCRIBER_SERVICE_VOICE);
+       telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_list_current_calls_req(void *telephony_device)
+{
+       GSList *l;
+       int i;
+
+       DBG("telephony-ofono: list current calls request");
+
+       for (l = calls, i = 1; l != NULL; l = l->next, i++) {
+               struct voice_call *vc = l->data;
+               int direction, multiparty;
+
+               direction = vc->originating ?
+                               CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
+
+               multiparty = vc->conference ?
+                               CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
+
+               DBG("call %s direction %d multiparty %d", vc->number,
+                                                       direction, multiparty);
+
+               telephony_list_current_call_ind(i, direction, vc->status,
+                                       CALL_MODE_VOICE, multiparty,
+                                       vc->number, number_type(vc->number));
+       }
+
+       telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_operator_selection_req(void *telephony_device)
+{
+       DBG("telephony-ofono: operator selection request");
+
+       telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
+                               net.operator_name ? net.operator_name : "");
+       telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+static void foreach_vc_with_status(int status,
+                                       int (*func)(struct voice_call *vc))
+{
+       GSList *l;
+
+       for (l = calls; l != NULL; l = l->next) {
+               struct voice_call *call = l->data;
+
+               if (call->status == status)
+                       func(call);
+       }
+}
+
+void telephony_call_hold_req(void *telephony_device, const char *cmd)
+{
+       const char *idx;
+       struct voice_call *call;
+       int err = 0;
+
+       DBG("telephony-ofono: got call hold request %s", cmd);
+
+       if (strlen(cmd) > 1)
+               idx = &cmd[1];
+       else
+               idx = NULL;
+
+       if (idx)
+               call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
+       else
+               call = NULL;
+
+       switch (cmd[0]) {
+       case '0':
+               if (find_vc_with_status(CALL_STATUS_WAITING))
+                       foreach_vc_with_status(CALL_STATUS_WAITING,
+                                                               release_call);
+               else
+                       foreach_vc_with_status(CALL_STATUS_HELD, release_call);
+               break;
+       case '1':
+               if (idx) {
+                       if (call)
+                               err = release_call(call);
+                       break;
+               }
+               err = release_answer_calls();
+               break;
+       case '2':
+               if (idx) {
+                       if (call)
+                               err = split_call(call);
+               } else {
+                       call = find_vc_with_status(CALL_STATUS_WAITING);
+
+                       if (call)
+                               err = answer_call(call);
+                       else
+                               err = swap_calls();
+               }
+               break;
+       case '3':
+               if (find_vc_with_status(CALL_STATUS_HELD) ||
+                               find_vc_with_status(CALL_STATUS_WAITING))
+                       err = create_conference();
+               break;
+       case '4':
+               err = call_transfer();
+               break;
+       default:
+               DBG("Unknown call hold request");
+               break;
+       }
+
+       if (err)
+               telephony_call_hold_rsp(telephony_device,
+                                       CME_ERROR_AG_FAILURE);
+       else
+               telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
+{
+       DBG("telephony-ofono: got %s NR and EC request",
+                       enable ? "enable" : "disable");
+
+       telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_key_press_req(void *telephony_device, const char *keys)
+{
+       struct voice_call *active, *incoming;
+       int err;
+
+       DBG("telephony-ofono: got key press request for %s", keys);
+
+       incoming = find_vc_with_status(CALL_STATUS_INCOMING);
+
+       active = find_vc_with_status(CALL_STATUS_ACTIVE);
+
+       if (incoming)
+               err = answer_call(incoming);
+       else if (active)
+               err = release_call(active);
+       else
+               err = 0;
+
+       if (err < 0)
+               telephony_key_press_rsp(telephony_device,
+                                                       CME_ERROR_AG_FAILURE);
+       else
+               telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
+}
+
+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
+{
+       DBG("telephony-ofono: got %s voice dial request",
+                       enable ? "enable" : "disable");
+
+       telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
+}
+
+static gboolean iter_get_basic_args(DBusMessageIter *iter,
+                                       int first_arg_type, ...)
+{
+       int type;
+       va_list ap;
+
+       va_start(ap, first_arg_type);
+
+       for (type = first_arg_type; type != DBUS_TYPE_INVALID;
+               type = va_arg(ap, int)) {
+               void *value = va_arg(ap, void *);
+               int real_type = dbus_message_iter_get_arg_type(iter);
+
+               if (real_type != type) {
+                       error("iter_get_basic_args: expected %c but got %c",
+                               (char) type, (char) real_type);
+                       break;
+               }
+
+               dbus_message_iter_get_basic(iter, value);
+               dbus_message_iter_next(iter);
+       }
+
+       va_end(ap);
+
+       return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
+}
+
+static void call_free(void *data)
+{
+       struct voice_call *vc = data;
+
+       DBG("%s", vc->obj_path);
+
+       if (vc->status == CALL_STATUS_ACTIVE)
+               telephony_update_indicator(ofono_indicators, "call",
+                                                       EV_CALL_INACTIVE);
+       else
+               telephony_update_indicator(ofono_indicators, "callsetup",
+                                                       EV_CALLSETUP_INACTIVE);
+
+       if (vc->status == CALL_STATUS_INCOMING)
+               telephony_calling_stopped_ind();
+
+       g_dbus_remove_watch(connection, vc->watch);
+       g_free(vc->obj_path);
+       g_free(vc->number);
+       g_free(vc);
+}
+
+static gboolean handle_vc_property_changed(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct voice_call *vc = data;
+       const char *obj_path = dbus_message_get_path(msg);
+       DBusMessageIter iter, sub;
+       const char *property, *state;
+
+       DBG("path %s", obj_path);
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+               error("Unexpected signature in vc PropertyChanged signal");
+               return TRUE;
+       }
+
+       dbus_message_iter_get_basic(&iter, &property);
+       DBG("property %s", property);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &sub);
+       if (g_str_equal(property, "State")) {
+               dbus_message_iter_get_basic(&sub, &state);
+               DBG("State %s", state);
+               if (g_str_equal(state, "disconnected")) {
+                       calls = g_slist_remove(calls, vc);
+                       call_free(vc);
+               } else if (g_str_equal(state, "active")) {
+                       telephony_update_indicator(ofono_indicators,
+                                                       "call", EV_CALL_ACTIVE);
+                       telephony_update_indicator(ofono_indicators,
+                                                       "callsetup",
+                                                       EV_CALLSETUP_INACTIVE);
+                       if (vc->status == CALL_STATUS_INCOMING)
+                               telephony_calling_stopped_ind();
+                       vc->status = CALL_STATUS_ACTIVE;
+               } else if (g_str_equal(state, "alerting")) {
+                       telephony_update_indicator(ofono_indicators,
+                                       "callsetup", EV_CALLSETUP_ALERTING);
+                       vc->status = CALL_STATUS_ALERTING;
+                       vc->originating = TRUE;
+               } else if (g_str_equal(state, "incoming")) {
+                       /* state change from waiting to incoming */
+                       telephony_update_indicator(ofono_indicators,
+                                       "callsetup", EV_CALLSETUP_INCOMING);
+                       telephony_incoming_call_ind(vc->number,
+                                               NUMBER_TYPE_TELEPHONY);
+                       vc->status = CALL_STATUS_INCOMING;
+                       vc->originating = FALSE;
+               } else if (g_str_equal(state, "held")) {
+                       vc->status = CALL_STATUS_HELD;
+                       if (find_vc_without_status(CALL_STATUS_HELD))
+                               telephony_update_indicator(ofono_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_MULTIPLE);
+                       else
+                               telephony_update_indicator(ofono_indicators,
+                                                       "callheld",
+                                                       EV_CALLHELD_ON_HOLD);
+               }
+       } else if (g_str_equal(property, "Multiparty")) {
+               dbus_bool_t multiparty;
+
+               dbus_message_iter_get_basic(&sub, &multiparty);
+               DBG("Multiparty %s", multiparty ? "True" : "False");
+               vc->conference = multiparty;
+       }
+
+       return TRUE;
+}
+
+static struct voice_call *call_new(const char *path, DBusMessageIter *properties)
+{
+       struct voice_call *vc;
+
+       DBG("%s", path);
+
+       vc = g_new0(struct voice_call, 1);
+       vc->obj_path = g_strdup(path);
+       vc->watch = g_dbus_add_signal_watch(connection, NULL, path,
+                                       OFONO_VC_INTERFACE, "PropertyChanged",
+                                       handle_vc_property_changed, vc, NULL);
+
+       while (dbus_message_iter_get_arg_type(properties)
+                                               == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+               const char *property, *cli, *state;
+               dbus_bool_t multiparty;
+
+               dbus_message_iter_recurse(properties, &entry);
+               dbus_message_iter_get_basic(&entry, &property);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (g_str_equal(property, "LineIdentification")) {
+                       dbus_message_iter_get_basic(&value, &cli);
+                       DBG("cli %s", cli);
+                       vc->number = g_strdup(cli);
+               } else if (g_str_equal(property, "State")) {
+                       dbus_message_iter_get_basic(&value, &state);
+                       DBG("state %s", state);
+                       if (g_str_equal(state, "incoming"))
+                               vc->status = CALL_STATUS_INCOMING;
+                       else if (g_str_equal(state, "dialing"))
+                               vc->status = CALL_STATUS_DIALING;
+                       else if (g_str_equal(state, "alerting"))
+                               vc->status = CALL_STATUS_ALERTING;
+                       else if (g_str_equal(state, "waiting"))
+                               vc->status = CALL_STATUS_WAITING;
+                       else if (g_str_equal(state, "held"))
+                               vc->status = CALL_STATUS_HELD;
+               } else if (g_str_equal(property, "Multiparty")) {
+                       dbus_message_iter_get_basic(&value, &multiparty);
+                       DBG("Multipary %s", multiparty ? "True" : "False");
+                       vc->conference = multiparty;
+               }
+
+               dbus_message_iter_next(properties);
+       }
+
+       switch (vc->status) {
+       case CALL_STATUS_INCOMING:
+               DBG("CALL_STATUS_INCOMING");
+               vc->originating = FALSE;
+               telephony_update_indicator(ofono_indicators, "callsetup",
+                                       EV_CALLSETUP_INCOMING);
+               telephony_incoming_call_ind(vc->number, NUMBER_TYPE_TELEPHONY);
+               break;
+       case CALL_STATUS_DIALING:
+               DBG("CALL_STATUS_DIALING");
+               vc->originating = TRUE;
+               g_free(last_dialed_number);
+               last_dialed_number = g_strdup(vc->number);
+               telephony_update_indicator(ofono_indicators, "callsetup",
+                                       EV_CALLSETUP_OUTGOING);
+               break;
+       case CALL_STATUS_ALERTING:
+               DBG("CALL_STATUS_ALERTING");
+               vc->originating = TRUE;
+               g_free(last_dialed_number);
+               last_dialed_number = g_strdup(vc->number);
+               telephony_update_indicator(ofono_indicators, "callsetup",
+                                       EV_CALLSETUP_ALERTING);
+               break;
+       case CALL_STATUS_WAITING:
+               DBG("CALL_STATUS_WAITING");
+               vc->originating = FALSE;
+               telephony_update_indicator(ofono_indicators, "callsetup",
+                                       EV_CALLSETUP_INCOMING);
+               telephony_call_waiting_ind(vc->number, NUMBER_TYPE_TELEPHONY);
+               break;
+       }
+
+       return vc;
+}
+
+static void remove_pending(DBusPendingCall *call)
+{
+       pending = g_slist_remove(pending, call);
+       dbus_pending_call_unref(call);
+}
+
+static void call_added(const char *path, DBusMessageIter *properties)
+{
+       struct voice_call *vc;
+
+       DBG("%s", path);
+
+       vc = find_vc(path);
+       if (vc)
+               return;
+
+       vc = call_new(path, properties);
+       calls = g_slist_prepend(calls, vc);
+}
+
+static void get_calls_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       DBusMessageIter iter, entry;
+
+       DBG("");
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("ofono replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature");
+               goto done;
+       }
+
+       dbus_message_iter_recurse(&iter, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry)
+                                               == DBUS_TYPE_STRUCT) {
+               const char *path;
+               DBusMessageIter value, properties;
+
+               dbus_message_iter_recurse(&entry, &value);
+               dbus_message_iter_get_basic(&value, &path);
+
+               dbus_message_iter_next(&value);
+               dbus_message_iter_recurse(&value, &properties);
+
+               call_added(path, &properties);
+
+               dbus_message_iter_next(&entry);
+       }
+
+done:
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+static void handle_network_property(const char *property, DBusMessageIter *variant)
+{
+       const char *status, *operator;
+       unsigned int signals_bar;
+
+       if (g_str_equal(property, "Status")) {
+               dbus_message_iter_get_basic(variant, &status);
+               DBG("Status is %s", status);
+               if (g_str_equal(status, "registered")) {
+                       net.status = NETWORK_REG_STATUS_HOME;
+                       telephony_update_indicator(ofono_indicators,
+                                               "roam", EV_ROAM_INACTIVE);
+                       telephony_update_indicator(ofono_indicators,
+                                               "service", EV_SERVICE_PRESENT);
+               } else if (g_str_equal(status, "roaming")) {
+                       net.status = NETWORK_REG_STATUS_ROAM;
+                       telephony_update_indicator(ofono_indicators,
+                                               "roam", EV_ROAM_ACTIVE);
+                       telephony_update_indicator(ofono_indicators,
+                                               "service", EV_SERVICE_PRESENT);
+               } else {
+                       net.status = NETWORK_REG_STATUS_NOSERV;
+                       telephony_update_indicator(ofono_indicators,
+                                               "roam", EV_ROAM_INACTIVE);
+                       telephony_update_indicator(ofono_indicators,
+                                               "service", EV_SERVICE_NONE);
+               }
+       } else if (g_str_equal(property, "Name")) {
+               dbus_message_iter_get_basic(variant, &operator);
+               DBG("Operator is %s", operator);
+               g_free(net.operator_name);
+               net.operator_name = g_strdup(operator);
+       } else if (g_str_equal(property, "SignalStrength")) {
+               dbus_message_iter_get_basic(variant, &signals_bar);
+               DBG("SignalStrength is %d", signals_bar);
+               net.signals_bar = signals_bar;
+               telephony_update_indicator(ofono_indicators, "signal",
+                                               (signals_bar + 20) / 21);
+       }
+}
+
+static int parse_network_properties(DBusMessageIter *properties)
+{
+       int i;
+
+       /* Reset indicators */
+       for (i = 0; ofono_indicators[i].desc != NULL; i++) {
+               if (g_str_equal(ofono_indicators[i].desc, "battchg"))
+                       ofono_indicators[i].val = 5;
+               else
+                       ofono_indicators[i].val = 0;
+       }
+
+       while (dbus_message_iter_get_arg_type(properties)
+                                               == DBUS_TYPE_DICT_ENTRY) {
+               const char *key;
+               DBusMessageIter value, entry;
+
+               dbus_message_iter_recurse(properties, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               handle_network_property(key, &value);
+
+               dbus_message_iter_next(properties);
+       }
+
+       return 0;
+}
+
+static void get_properties_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       DBusMessageIter iter, properties;
+       int ret = 0;
+
+       DBG("");
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("ofono replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature");
+               goto done;
+       }
+
+       dbus_message_iter_recurse(&iter, &properties);
+
+       ret = parse_network_properties(&properties);
+       if (ret < 0) {
+               error("Unable to parse %s.GetProperty reply",
+                                               OFONO_NETWORKREG_INTERFACE);
+               goto done;
+       }
+
+       ret = send_method_call(OFONO_BUS_NAME, modem_obj_path,
+                               OFONO_VCMANAGER_INTERFACE, "GetCalls",
+                               get_calls_reply, NULL, DBUS_TYPE_INVALID);
+       if (ret < 0)
+               error("Unable to send %s.GetCalls",
+                                               OFONO_VCMANAGER_INTERFACE);
+
+done:
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+static void network_found(const char *path)
+{
+       int ret;
+
+       DBG("%s", path);
+
+       modem_obj_path = g_strdup(path);
+
+       ret = send_method_call(OFONO_BUS_NAME, path,
+                               OFONO_NETWORKREG_INTERFACE, "GetProperties",
+                               get_properties_reply, NULL, DBUS_TYPE_INVALID);
+       if (ret < 0)
+               error("Unable to send %s.GetProperties",
+                                               OFONO_NETWORKREG_INTERFACE);
+}
+
+static void modem_removed(const char *path)
+{
+       if (g_strcmp0(modem_obj_path, path) != 0)
+               return;
+
+       DBG("%s", path);
+
+       g_slist_free_full(calls, call_free);
+       calls = NULL;
+
+       g_free(net.operator_name);
+       net.operator_name = NULL;
+       net.status = NETWORK_REG_STATUS_NOSERV;
+       net.signals_bar = 0;
+
+       g_free(modem_obj_path);
+       modem_obj_path = NULL;
+}
+
+static void parse_modem_interfaces(const char *path, DBusMessageIter *ifaces)
+{
+       DBG("%s", path);
+
+       while (dbus_message_iter_get_arg_type(ifaces) == DBUS_TYPE_STRING) {
+               const char *iface;
+
+               dbus_message_iter_get_basic(ifaces, &iface);
+
+               if (g_str_equal(iface, OFONO_NETWORKREG_INTERFACE)) {
+                       network_found(path);
+                       return;
+               }
+
+               dbus_message_iter_next(ifaces);
+       }
+
+       modem_removed(path);
+}
+
+static void modem_added(const char *path, DBusMessageIter *properties)
+{
+       if (modem_obj_path != NULL) {
+               DBG("Ignoring, modem already exist");
+               return;
+       }
+
+       DBG("%s", path);
+
+       while (dbus_message_iter_get_arg_type(properties)
+                                               == DBUS_TYPE_DICT_ENTRY) {
+               const char *key;
+               DBusMessageIter interfaces, value, entry;
+
+               dbus_message_iter_recurse(properties, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_recurse(&entry, &value);
+
+               if (strcasecmp(key, "Interfaces") != 0)
+                       goto next;
+
+               if (dbus_message_iter_get_arg_type(&value)
+                                                       != DBUS_TYPE_ARRAY) {
+                       error("Invalid Signature");
+                       return;
+               }
+
+               dbus_message_iter_recurse(&value, &interfaces);
+
+               parse_modem_interfaces(path, &interfaces);
+
+               if (modem_obj_path != NULL)
+                       return;
+
+       next:
+               dbus_message_iter_next(properties);
+       }
+}
+
+static void get_modems_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusError err;
+       DBusMessage *reply;
+       DBusMessageIter iter, entry;
+
+       DBG("");
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("ofono replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       /* Skip modem selection if a modem already exist */
+       if (modem_obj_path != NULL)
+               goto done;
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature");
+               goto done;
+       }
+
+       dbus_message_iter_recurse(&iter, &entry);
+
+       while (dbus_message_iter_get_arg_type(&entry)
+                                               == DBUS_TYPE_STRUCT) {
+               const char *path;
+               DBusMessageIter item, properties;
+
+               dbus_message_iter_recurse(&entry, &item);
+               dbus_message_iter_get_basic(&item, &path);
+
+               dbus_message_iter_next(&item);
+               dbus_message_iter_recurse(&item, &properties);
+
+               modem_added(path, &properties);
+               if (modem_obj_path != NULL)
+                       break;
+
+               dbus_message_iter_next(&entry);
+       }
+
+done:
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+static gboolean handle_network_property_changed(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, variant;
+       const char *property;
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+               error("Unexpected signature in networkregistration"
+                                       " PropertyChanged signal");
+               return TRUE;
+       }
+       dbus_message_iter_get_basic(&iter, &property);
+       DBG("in handle_registration_property_changed(),"
+                                       " the property is %s", property);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &variant);
+
+       handle_network_property(property, &variant);
+
+       return TRUE;
+}
+
+static void handle_modem_property(const char *path, const char *property,
+                                               DBusMessageIter *variant)
+{
+       DBG("%s", property);
+
+       if (g_str_equal(property, "Interfaces")) {
+               DBusMessageIter interfaces;
+
+               if (dbus_message_iter_get_arg_type(variant)
+                                                       != DBUS_TYPE_ARRAY) {
+                       error("Invalid signature");
+                       return;
+               }
+
+               dbus_message_iter_recurse(variant, &interfaces);
+               parse_modem_interfaces(path, &interfaces);
+       }
+}
+
+static gboolean handle_modem_property_changed(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, variant;
+       const char *property, *path;
+
+       path = dbus_message_get_path(msg);
+
+       /* Ignore if modem already exist and paths doesn't match */
+       if (modem_obj_path != NULL &&
+                               g_str_equal(path, modem_obj_path) == FALSE)
+               return TRUE;
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+               error("Unexpected signature in %s.%s PropertyChanged signal",
+                                       dbus_message_get_interface(msg),
+                                       dbus_message_get_member(msg));
+               return TRUE;
+       }
+
+       dbus_message_iter_get_basic(&iter, &property);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &variant);
+
+       handle_modem_property(path, property, &variant);
+
+       return TRUE;
+}
+
+static gboolean handle_vcmanager_call_added(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, properties;
+       const char *path = dbus_message_get_path(msg);
+
+       /* Ignore call if modem path doesn't math */
+       if (g_strcmp0(modem_obj_path, path) != 0)
+               return TRUE;
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter)
+                                               != DBUS_TYPE_OBJECT_PATH) {
+               error("Unexpected signature in %s.%s signal",
+                                       dbus_message_get_interface(msg),
+                                       dbus_message_get_member(msg));
+               return TRUE;
+       }
+
+       dbus_message_iter_get_basic(&iter, &path);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &properties);
+
+       call_added(path, &properties);
+
+       return TRUE;
+}
+
+static void call_removed(const char *path)
+{
+       struct voice_call *vc;
+
+       DBG("%s", path);
+
+       vc = find_vc(path);
+       if (vc == NULL)
+               return;
+
+       calls = g_slist_remove(calls, vc);
+       call_free(vc);
+}
+
+static gboolean handle_vcmanager_call_removed(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       const char *path = dbus_message_get_path(msg);
+
+       /* Ignore call if modem path doesn't math */
+       if (g_strcmp0(modem_obj_path, path) != 0)
+               return TRUE;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID)) {
+               error("Unexpected signature in %s.%s signal",
+                                       dbus_message_get_interface(msg),
+                                       dbus_message_get_member(msg));
+               return TRUE;
+       }
+
+       call_removed(path);
+
+       return TRUE;
+}
+
+static gboolean handle_manager_modem_added(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, properties;
+       const char *path;
+
+       if (modem_obj_path != NULL)
+               return TRUE;
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter)
+                                               != DBUS_TYPE_OBJECT_PATH) {
+               error("Unexpected signature in %s.%s signal",
+                                       dbus_message_get_interface(msg),
+                                       dbus_message_get_member(msg));
+               return TRUE;
+       }
+
+       dbus_message_iter_get_basic(&iter, &path);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &properties);
+
+       modem_added(path, &properties);
+
+       return TRUE;
+}
+
+static gboolean handle_manager_modem_removed(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       const char *path;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID)) {
+               error("Unexpected signature in %s.%s signal",
+                                       dbus_message_get_interface(msg),
+                                       dbus_message_get_member(msg));
+               return TRUE;
+       }
+
+       modem_removed(path);
+
+       return TRUE;
+}
+
+static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError err;
+       dbus_int32_t level;
+       int *value = user_data;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("hald replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_error_init(&err);
+       if (dbus_message_get_args(reply, &err,
+                               DBUS_TYPE_INT32, &level,
+                               DBUS_TYPE_INVALID) == FALSE) {
+               error("Unable to parse GetPropertyInteger reply: %s, %s",
+                                                       err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       *value = (int) level;
+
+       if (value == &battchg_last)
+               DBG("telephony-ofono: battery.charge_level.last_full"
+                                       " is %d", *value);
+       else if (value == &battchg_design)
+               DBG("telephony-ofono: battery.charge_level.design"
+                                       " is %d", *value);
+       else
+               DBG("telephony-ofono: battery.charge_level.current"
+                                       " is %d", *value);
+
+       if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
+               int new, max;
+
+               if (battchg_last > 0)
+                       max = battchg_last;
+               else
+                       max = battchg_design;
+
+               new = battchg_cur * 5 / max;
+
+               telephony_update_indicator(ofono_indicators, "battchg", new);
+       }
+done:
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+static void hal_get_integer(const char *path, const char *key, void *user_data)
+{
+       send_method_call("org.freedesktop.Hal", path,
+                       "org.freedesktop.Hal.Device",
+                       "GetPropertyInteger",
+                       hal_battery_level_reply, user_data,
+                       DBUS_TYPE_STRING, &key,
+                       DBUS_TYPE_INVALID);
+}
+
+static gboolean handle_hal_property_modified(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       const char *path;
+       DBusMessageIter iter, array;
+       dbus_int32_t num_changes;
+
+       path = dbus_message_get_path(msg);
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+               error("Unexpected signature in hal PropertyModified signal");
+               return TRUE;
+       }
+
+       dbus_message_iter_get_basic(&iter, &num_changes);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature in hal PropertyModified signal");
+               return TRUE;
+       }
+
+       dbus_message_iter_recurse(&iter, &array);
+
+       while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
+               DBusMessageIter prop;
+               const char *name;
+               dbus_bool_t added, removed;
+
+               dbus_message_iter_recurse(&array, &prop);
+
+               if (!iter_get_basic_args(&prop,
+                                       DBUS_TYPE_STRING, &name,
+                                       DBUS_TYPE_BOOLEAN, &added,
+                                       DBUS_TYPE_BOOLEAN, &removed,
+                                       DBUS_TYPE_INVALID)) {
+                       error("Invalid hal PropertyModified parameters");
+                       break;
+               }
+
+               if (g_str_equal(name, "battery.charge_level.last_full"))
+                       hal_get_integer(path, name, &battchg_last);
+               else if (g_str_equal(name, "battery.charge_level.current"))
+                       hal_get_integer(path, name, &battchg_cur);
+               else if (g_str_equal(name, "battery.charge_level.design"))
+                       hal_get_integer(path, name, &battchg_design);
+
+               dbus_message_iter_next(&array);
+       }
+
+       return TRUE;
+}
+
+static void add_watch(const char *sender, const char *path,
+                               const char *interface, const char *member,
+                               GDBusSignalFunction function)
+{
+       guint watch;
+
+       watch = g_dbus_add_signal_watch(connection, sender, path, interface,
+                                       member, function, NULL, NULL);
+
+       watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
+}
+
+static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
+{
+       DBusMessage *reply;
+       DBusError err;
+       DBusMessageIter iter, sub;
+       int type;
+       const char *path;
+
+       DBG("begin of hal_find_device_reply()");
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply)) {
+               error("hald replied with an error: %s, %s",
+                               err.name, err.message);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               error("Unexpected signature in hal_find_device_reply()");
+               goto done;
+       }
+
+       dbus_message_iter_recurse(&iter, &sub);
+
+       type = dbus_message_iter_get_arg_type(&sub);
+
+       if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
+               error("No hal device with battery capability found");
+               goto done;
+       }
+
+       dbus_message_iter_get_basic(&sub, &path);
+
+       DBG("telephony-ofono: found battery device at %s", path);
+
+       add_watch(NULL, path, "org.freedesktop.Hal.Device",
+                       "PropertyModified", handle_hal_property_modified);
+
+       hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
+       hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
+       hal_get_integer(path, "battery.charge_level.design", &battchg_design);
+done:
+       dbus_message_unref(reply);
+       remove_pending(call);
+}
+
+static void handle_service_connect(DBusConnection *conn, void *user_data)
+{
+       DBG("telephony-ofono: %s found", OFONO_BUS_NAME);
+
+       send_method_call(OFONO_BUS_NAME, OFONO_PATH,
+                               OFONO_MANAGER_INTERFACE, "GetModems",
+                               get_modems_reply, NULL, DBUS_TYPE_INVALID);
+}
+
+static void handle_service_disconnect(DBusConnection *conn, void *user_data)
+{
+       DBG("telephony-ofono: %s exitted", OFONO_BUS_NAME);
+
+       if (modem_obj_path)
+               modem_removed(modem_obj_path);
+}
+
+int telephony_init(void)
+{
+       uint32_t features = AG_FEATURE_EC_ANDOR_NR |
+                               AG_FEATURE_INBAND_RINGTONE |
+                               AG_FEATURE_REJECT_A_CALL |
+                               AG_FEATURE_ENHANCED_CALL_STATUS |
+                               AG_FEATURE_ENHANCED_CALL_CONTROL |
+                               AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
+                               AG_FEATURE_THREE_WAY_CALLING;
+       const char *battery_cap = "battery";
+       int ret;
+       guint watch;
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+       add_watch(OFONO_BUS_NAME, NULL, OFONO_MODEM_INTERFACE,
+                       "PropertyChanged", handle_modem_property_changed);
+       add_watch(OFONO_BUS_NAME, NULL, OFONO_NETWORKREG_INTERFACE,
+                       "PropertyChanged", handle_network_property_changed);
+       add_watch(OFONO_BUS_NAME, NULL, OFONO_MANAGER_INTERFACE,
+                       "ModemAdded", handle_manager_modem_added);
+       add_watch(OFONO_BUS_NAME, NULL, OFONO_MANAGER_INTERFACE,
+                       "ModemRemoved", handle_manager_modem_removed);
+       add_watch(OFONO_BUS_NAME, NULL, OFONO_VCMANAGER_INTERFACE,
+                       "CallAdded", handle_vcmanager_call_added);
+       add_watch(OFONO_BUS_NAME, NULL, OFONO_VCMANAGER_INTERFACE,
+                       "CallRemoved", handle_vcmanager_call_removed);
+
+       watch = g_dbus_add_service_watch(connection, OFONO_BUS_NAME,
+                                               handle_service_connect,
+                                               handle_service_disconnect,
+                                               NULL, NULL);
+       if (watch == 0)
+               return -ENOMEM;
+
+       watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
+
+       ret = send_method_call("org.freedesktop.Hal",
+                               "/org/freedesktop/Hal/Manager",
+                               "org.freedesktop.Hal.Manager",
+                               "FindDeviceByCapability",
+                               hal_find_device_reply, NULL,
+                               DBUS_TYPE_STRING, &battery_cap,
+                               DBUS_TYPE_INVALID);
+       if (ret < 0)
+               return ret;
+
+       DBG("telephony_init() successfully");
+
+       telephony_ready_ind(features, ofono_indicators, BTRH_NOT_SUPPORTED,
+                                                               chld_str);
+
+       return ret;
+}
+
+static void remove_watch(gpointer data)
+{
+       g_dbus_remove_watch(connection, GPOINTER_TO_UINT(data));
+}
+
+static void pending_free(void *data)
+{
+       DBusPendingCall *call = data;
+
+       if (!dbus_pending_call_get_completed(call))
+               dbus_pending_call_cancel(call);
+
+       dbus_pending_call_unref(call);
+}
+
+void telephony_exit(void)
+{
+       DBG("");
+
+       g_free(last_dialed_number);
+       last_dialed_number = NULL;
+
+       if (modem_obj_path)
+               modem_removed(modem_obj_path);
+
+       g_slist_free_full(watches, remove_watch);
+       watches = NULL;
+
+       g_slist_free_full(pending, pending_free);
+       pending = NULL;
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+
+       telephony_deinit();
+}
diff --git a/audio/telephony.h b/audio/telephony.h
new file mode 100644 (file)
index 0000000..73b390c
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <glib.h>
+
+/* Response and hold values */
+#define BTRH_NOT_SUPPORTED     -2
+#define BTRH_NONE              -1
+#define BTRH_HOLD              0
+#define BTRH_ACCEPT            1
+#define BTRH_REJECT            2
+
+/* HFP feature bits */
+#define AG_FEATURE_THREE_WAY_CALLING           0x0001
+#define AG_FEATURE_EC_ANDOR_NR                 0x0002
+#define AG_FEATURE_VOICE_RECOGNITION           0x0004
+#define AG_FEATURE_INBAND_RINGTONE             0x0008
+#define AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG   0x0010
+#define AG_FEATURE_REJECT_A_CALL               0x0020
+#define AG_FEATURE_ENHANCED_CALL_STATUS                0x0040
+#define AG_FEATURE_ENHANCED_CALL_CONTROL       0x0080
+#define AG_FEATURE_EXTENDED_ERROR_RESULT_CODES 0x0100
+
+#define HF_FEATURE_EC_ANDOR_NR                 0x0001
+#define HF_FEATURE_CALL_WAITING_AND_3WAY       0x0002
+#define HF_FEATURE_CLI_PRESENTATION            0x0004
+#define HF_FEATURE_VOICE_RECOGNITION           0x0008
+#define HF_FEATURE_REMOTE_VOLUME_CONTROL       0x0010
+#define HF_FEATURE_ENHANCED_CALL_STATUS                0x0020
+#define HF_FEATURE_ENHANCED_CALL_CONTROL       0x0040
+
+/* Indicator event values */
+#define EV_SERVICE_NONE                        0
+#define EV_SERVICE_PRESENT             1
+
+#define EV_CALL_INACTIVE               0
+#define EV_CALL_ACTIVE                 1
+
+#define EV_CALLSETUP_INACTIVE          0
+#define EV_CALLSETUP_INCOMING          1
+#define EV_CALLSETUP_OUTGOING          2
+#define EV_CALLSETUP_ALERTING          3
+
+#define EV_CALLHELD_NONE               0
+#define EV_CALLHELD_MULTIPLE           1
+#define EV_CALLHELD_ON_HOLD            2
+
+#define EV_ROAM_INACTIVE               0
+#define EV_ROAM_ACTIVE                 1
+
+/* Call parameters */
+#define CALL_DIR_OUTGOING              0
+#define CALL_DIR_INCOMING              1
+
+#define CALL_STATUS_ACTIVE             0
+#define CALL_STATUS_HELD               1
+#define CALL_STATUS_DIALING            2
+#define CALL_STATUS_ALERTING           3
+#define CALL_STATUS_INCOMING           4
+#define CALL_STATUS_WAITING            5
+
+#define CALL_MODE_VOICE                        0
+#define CALL_MODE_DATA                 1
+#define CALL_MODE_FAX                  2
+
+#define CALL_MULTIPARTY_NO             0
+#define CALL_MULTIPARTY_YES            1
+
+/* Subscriber number parameters */
+#define SUBSCRIBER_SERVICE_VOICE       4
+#define SUBSCRIBER_SERVICE_FAX         5
+
+/* Operator selection mode values */
+#define OPERATOR_MODE_AUTO             0
+#define OPERATOR_MODE_MANUAL           1
+#define OPERATOR_MODE_DEREGISTER       2
+#define OPERATOR_MODE_MANUAL_AUTO      4
+
+/* Some common number types */
+#define NUMBER_TYPE_UNKNOWN            128
+#define NUMBER_TYPE_TELEPHONY          129
+#define NUMBER_TYPE_INTERNATIONAL      145
+#define NUMBER_TYPE_NATIONAL           161
+#define NUMBER_TYPE_VOIP               255
+
+/* Extended Audio Gateway Error Result Codes */
+typedef enum {
+       CME_ERROR_NONE                  = -1,
+       CME_ERROR_AG_FAILURE            = 0,
+       CME_ERROR_NO_PHONE_CONNECTION   = 1,
+       CME_ERROR_NOT_ALLOWED           = 3,
+       CME_ERROR_NOT_SUPPORTED         = 4,
+       CME_ERROR_PH_SIM_PIN_REQUIRED   = 5,
+       CME_ERROR_SIM_NOT_INSERTED      = 10,
+       CME_ERROR_SIM_PIN_REQUIRED      = 11,
+       CME_ERROR_SIM_PUK_REQUIRED      = 12,
+       CME_ERROR_SIM_FAILURE           = 13,
+       CME_ERROR_SIM_BUSY              = 14,
+       CME_ERROR_INCORRECT_PASSWORD    = 16,
+       CME_ERROR_SIM_PIN2_REQUIRED     = 17,
+       CME_ERROR_SIM_PUK2_REQUIRED     = 18,
+       CME_ERROR_MEMORY_FULL           = 20,
+       CME_ERROR_INVALID_INDEX         = 21,
+       CME_ERROR_MEMORY_FAILURE        = 23,
+       CME_ERROR_TEXT_STRING_TOO_LONG  = 24,
+       CME_ERROR_INVALID_TEXT_STRING   = 25,
+       CME_ERROR_DIAL_STRING_TOO_LONG  = 26,
+       CME_ERROR_INVALID_DIAL_STRING   = 27,
+       CME_ERROR_NO_NETWORK_SERVICE    = 30,
+       CME_ERROR_NETWORK_TIMEOUT       = 31,
+       CME_ERROR_NETWORK_NOT_ALLOWED   = 32,
+} cme_error_t;
+
+struct indicator {
+       const char *desc;
+       const char *range;
+       int val;
+       gboolean ignore_redundant;
+};
+
+/* Notify telephony-*.c of connected/disconnected devices. Implemented by
+ * telephony-*.c
+ */
+void telephony_device_connected(void *telephony_device);
+void telephony_device_disconnected(void *telephony_device);
+
+/* HF requests (sent by the handsfree device). These are implemented by
+ * telephony-*.c
+ */
+void telephony_event_reporting_req(void *telephony_device, int ind);
+void telephony_response_and_hold_req(void *telephony_device, int rh);
+void telephony_last_dialed_number_req(void *telephony_device);
+void telephony_terminate_call_req(void *telephony_device);
+void telephony_answer_call_req(void *telephony_device);
+void telephony_dial_number_req(void *telephony_device, const char *number);
+void telephony_transmit_dtmf_req(void *telephony_device, char tone);
+void telephony_subscriber_number_req(void *telephony_device);
+void telephony_list_current_calls_req(void *telephony_device);
+void telephony_operator_selection_req(void *telephony_device);
+void telephony_call_hold_req(void *telephony_device, const char *cmd);
+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable);
+void telephony_voice_dial_req(void *telephony_device, gboolean enable);
+void telephony_key_press_req(void *telephony_device, const char *keys);
+
+/* AG responses to HF requests. These are implemented by headset.c */
+int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err);
+int telephony_response_and_hold_rsp(void *telephony_device, cme_error_t err);
+int telephony_last_dialed_number_rsp(void *telephony_device, cme_error_t err);
+int telephony_terminate_call_rsp(void *telephony_device, cme_error_t err);
+int telephony_answer_call_rsp(void *telephony_device, cme_error_t err);
+int telephony_dial_number_rsp(void *telephony_device, cme_error_t err);
+int telephony_transmit_dtmf_rsp(void *telephony_device, cme_error_t err);
+int telephony_subscriber_number_rsp(void *telephony_device, cme_error_t err);
+int telephony_list_current_calls_rsp(void *telephony_device, cme_error_t err);
+int telephony_operator_selection_rsp(void *telephony_device, cme_error_t err);
+int telephony_call_hold_rsp(void *telephony_device, cme_error_t err);
+int telephony_nr_and_ec_rsp(void *telephony_device, cme_error_t err);
+int telephony_voice_dial_rsp(void *telephony_device, cme_error_t err);
+int telephony_key_press_rsp(void *telephony_device, cme_error_t err);
+
+/* Event indications by AG. These are implemented by headset.c */
+int telephony_event_ind(int index);
+int telephony_response_and_hold_ind(int rh);
+int telephony_incoming_call_ind(const char *number, int type);
+int telephony_calling_stopped_ind(void);
+int telephony_ready_ind(uint32_t features, const struct indicator *indicators,
+                       int rh, const char *chld);
+int telephony_deinit(void);
+int telephony_list_current_call_ind(int idx, int dir, int status, int mode,
+                                       int mprty, const char *number,
+                                       int type);
+int telephony_subscriber_number_ind(const char *number, int type,
+                                       int service);
+int telephony_call_waiting_ind(const char *number, int type);
+int telephony_operator_selection_ind(int mode, const char *oper);
+
+/* Helper function for quick indicator updates */
+static inline int telephony_update_indicator(struct indicator *indicators,
+                                               const char *desc,
+                                               int new_val)
+{
+       int i;
+       struct indicator *ind = NULL;
+
+       for (i = 0; indicators[i].desc != NULL; i++) {
+               if (g_str_equal(indicators[i].desc, desc)) {
+                       ind = &indicators[i];
+                       break;
+               }
+       }
+
+       if (!ind)
+               return -ENOENT;
+
+       DBG("Telephony indicator \"%s\" %d->%d", desc, ind->val, new_val);
+
+       if (ind->ignore_redundant && ind->val == new_val) {
+               DBG("Ignoring no-change indication");
+               return 0;
+       }
+
+       ind->val = new_val;
+
+       return telephony_event_ind(i);
+}
+
+static inline int telephony_get_indicator(const struct indicator *indicators,
+                                               const char *desc)
+{
+       int i;
+
+       for (i = 0; indicators[i].desc != NULL; i++) {
+               if (g_str_equal(indicators[i].desc, desc))
+                       return indicators[i].val;
+       }
+
+       return -ENOENT;
+}
+
+int telephony_init(void);
+void telephony_exit(void);
diff --git a/audio/transport.c b/audio/transport.c
new file mode 100644 (file)
index 0000000..b015625
--- /dev/null
@@ -0,0 +1,1147 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/adapter.h"
+#include "../src/dbus-common.h"
+
+#include "log.h"
+#include "error.h"
+#include "device.h"
+#include "avdtp.h"
+#include "media.h"
+#include "transport.h"
+#include "a2dp.h"
+#include "headset.h"
+#include "gateway.h"
+#include "avrcp.h"
+
+#define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport"
+
+struct media_request {
+       DBusMessage             *msg;
+       guint                   id;
+};
+
+struct media_owner {
+       struct media_transport  *transport;
+       struct media_request    *pending;
+       char                    *name;
+       char                    *accesstype;
+       guint                   watch;
+};
+
+struct a2dp_transport {
+       struct avdtp            *session;
+       uint16_t                delay;
+       uint16_t                volume;
+};
+
+struct headset_transport {
+       struct audio_device     *device;
+       unsigned int            nrec_id;
+};
+
+struct media_transport {
+       DBusConnection          *conn;
+       char                    *path;          /* Transport object path */
+       struct audio_device     *device;        /* Transport device */
+       struct media_endpoint   *endpoint;      /* Transport endpoint */
+       GSList                  *owners;        /* Transport owners */
+       uint8_t                 *configuration; /* Transport configuration */
+       int                     size;           /* Transport configuration size */
+       int                     fd;             /* Transport file descriptor */
+       uint16_t                imtu;           /* Transport input mtu */
+       uint16_t                omtu;           /* Transport output mtu */
+       gboolean                read_lock;
+       gboolean                write_lock;
+       gboolean                in_use;
+       guint                   (*resume) (struct media_transport *transport,
+                                       struct media_owner *owner);
+       guint                   (*suspend) (struct media_transport *transport,
+                                       struct media_owner *owner);
+       void                    (*cancel) (struct media_transport *transport,
+                                                               guint id);
+       void                    (*get_properties) (
+                                       struct media_transport *transport,
+                                       DBusMessageIter *dict);
+       int                     (*set_property) (
+                                       struct media_transport *transport,
+                                       const char *property,
+                                       DBusMessageIter *value);
+       GDestroyNotify          destroy;
+       void                    *data;
+};
+
+void media_transport_destroy(struct media_transport *transport)
+{
+       char *path;
+
+       path = g_strdup(transport->path);
+       g_dbus_unregister_interface(transport->conn, path,
+                                               MEDIA_TRANSPORT_INTERFACE);
+
+       g_free(path);
+}
+
+static struct media_request *media_request_create(DBusMessage *msg, guint id)
+{
+       struct media_request *req;
+
+       req = g_new0(struct media_request, 1);
+       req->msg = dbus_message_ref(msg);
+       req->id = id;
+
+       DBG("Request created: method=%s id=%u", dbus_message_get_member(msg),
+                                                                       id);
+
+       return req;
+}
+
+static void media_request_reply(struct media_request *req,
+                                               DBusConnection *conn, int err)
+{
+       DBusMessage *reply;
+
+       DBG("Request %s Reply %s", dbus_message_get_member(req->msg),
+                                                       strerror(err));
+
+       if (!err)
+               reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID);
+       else
+               reply = g_dbus_create_error(req->msg,
+                                               ERROR_INTERFACE ".Failed",
+                                               "%s", strerror(err));
+
+       g_dbus_send_message(conn, reply);
+}
+
+static gboolean media_transport_release(struct media_transport *transport,
+                                       const char *accesstype)
+{
+       if (g_strstr_len(accesstype, -1, "r") != NULL) {
+               transport->read_lock = FALSE;
+               DBG("Transport %s: read lock released", transport->path);
+       }
+
+       if (g_strstr_len(accesstype, -1, "w") != NULL) {
+               transport->write_lock = FALSE;
+               DBG("Transport %s: write lock released", transport->path);
+       }
+
+       return TRUE;
+}
+
+static void media_owner_remove(struct media_owner *owner)
+{
+       struct media_transport *transport = owner->transport;
+       struct media_request *req = owner->pending;
+
+       if (!req)
+               return;
+
+       DBG("Owner %s Request %s", owner->name,
+                                       dbus_message_get_member(req->msg));
+
+       if (req->id)
+               transport->cancel(transport, req->id);
+
+       owner->pending = NULL;
+       if (req->msg)
+               dbus_message_unref(req->msg);
+
+       g_free(req);
+}
+
+static void media_owner_free(struct media_owner *owner)
+{
+       DBG("Owner %s", owner->name);
+
+       media_owner_remove(owner);
+
+       g_free(owner->name);
+       g_free(owner->accesstype);
+       g_free(owner);
+}
+
+static void media_transport_remove(struct media_transport *transport,
+                                               struct media_owner *owner)
+{
+       DBG("Transport %s Owner %s", transport->path, owner->name);
+
+       media_transport_release(transport, owner->accesstype);
+
+       /* Reply if owner has a pending request */
+       if (owner->pending)
+               media_request_reply(owner->pending, transport->conn, EIO);
+
+       transport->owners = g_slist_remove(transport->owners, owner);
+
+       if (owner->watch)
+               g_dbus_remove_watch(transport->conn, owner->watch);
+
+       media_owner_free(owner);
+
+       /* Suspend if there is no longer any owner */
+       if (transport->owners == NULL && transport->in_use)
+               transport->suspend(transport, NULL);
+}
+
+static gboolean media_transport_set_fd(struct media_transport *transport,
+                                       int fd, uint16_t imtu, uint16_t omtu)
+{
+       if (transport->fd == fd)
+               return TRUE;
+
+       transport->fd = fd;
+       transport->imtu = imtu;
+       transport->omtu = omtu;
+
+       info("%s: fd(%d) ready", transport->path, fd);
+
+       return TRUE;
+}
+
+static void a2dp_resume_complete(struct avdtp *session,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct media_owner *owner = user_data;
+       struct media_request *req = owner->pending;
+       struct media_transport *transport = owner->transport;
+       struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
+       struct avdtp_stream *stream;
+       int fd;
+       uint16_t imtu, omtu;
+       gboolean ret;
+
+       req->id = 0;
+
+       if (err)
+               goto fail;
+
+       stream = a2dp_sep_get_stream(sep);
+       if (stream == NULL)
+               goto fail;
+
+       ret = avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL);
+       if (ret == FALSE)
+               goto fail;
+
+       media_transport_set_fd(transport, fd, imtu, omtu);
+
+       if (g_strstr_len(owner->accesstype, -1, "r") == NULL)
+               imtu = 0;
+
+       if (g_strstr_len(owner->accesstype, -1, "w") == NULL)
+               omtu = 0;
+
+       ret = g_dbus_send_reply(transport->conn, req->msg,
+                                               DBUS_TYPE_UNIX_FD, &fd,
+                                               DBUS_TYPE_UINT16, &imtu,
+                                               DBUS_TYPE_UINT16, &omtu,
+                                               DBUS_TYPE_INVALID);
+       if (ret == FALSE)
+               goto fail;
+
+       media_owner_remove(owner);
+
+       return;
+
+fail:
+       media_transport_remove(transport, owner);
+}
+
+static guint resume_a2dp(struct media_transport *transport,
+                               struct media_owner *owner)
+{
+       struct a2dp_transport *a2dp = transport->data;
+       struct media_endpoint *endpoint = transport->endpoint;
+       struct audio_device *device = transport->device;
+       struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
+
+       if (a2dp->session == NULL) {
+               a2dp->session = avdtp_get(&device->src, &device->dst);
+               if (a2dp->session == NULL)
+                       return 0;
+       }
+
+       if (transport->in_use == TRUE)
+               goto done;
+
+       transport->in_use = a2dp_sep_lock(sep, a2dp->session);
+       if (transport->in_use == FALSE)
+               return 0;
+
+done:
+       return a2dp_resume(a2dp->session, sep, a2dp_resume_complete, owner);
+}
+
+static void a2dp_suspend_complete(struct avdtp *session,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct media_owner *owner = user_data;
+       struct media_transport *transport = owner->transport;
+       struct a2dp_transport *a2dp = transport->data;
+       struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
+
+       /* Release always succeeds */
+       if (owner->pending) {
+               owner->pending->id = 0;
+               media_request_reply(owner->pending, transport->conn, 0);
+               media_owner_remove(owner);
+       }
+
+       a2dp_sep_unlock(sep, a2dp->session);
+       transport->in_use = FALSE;
+       media_transport_remove(transport, owner);
+}
+
+static guint suspend_a2dp(struct media_transport *transport,
+                                               struct media_owner *owner)
+{
+       struct a2dp_transport *a2dp = transport->data;
+       struct media_endpoint *endpoint = transport->endpoint;
+       struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
+
+       if (!owner) {
+               a2dp_sep_unlock(sep, a2dp->session);
+               transport->in_use = FALSE;
+               return 0;
+       }
+
+       return a2dp_suspend(a2dp->session, sep, a2dp_suspend_complete, owner);
+}
+
+static void cancel_a2dp(struct media_transport *transport, guint id)
+{
+       a2dp_cancel(transport->device, id);
+}
+
+static void headset_resume_complete(struct audio_device *dev, void *user_data)
+{
+       struct media_owner *owner = user_data;
+       struct media_request *req = owner->pending;
+       struct media_transport *transport = owner->transport;
+       int fd;
+       uint16_t imtu, omtu;
+       gboolean ret;
+
+       req->id = 0;
+
+       if (dev == NULL)
+               goto fail;
+
+       fd = headset_get_sco_fd(dev);
+       if (fd < 0)
+               goto fail;
+
+       imtu = 48;
+       omtu = 48;
+
+       media_transport_set_fd(transport, fd, imtu, omtu);
+
+       if (g_strstr_len(owner->accesstype, -1, "r") == NULL)
+               imtu = 0;
+
+       if (g_strstr_len(owner->accesstype, -1, "w") == NULL)
+               omtu = 0;
+
+       ret = g_dbus_send_reply(transport->conn, req->msg,
+                                               DBUS_TYPE_UNIX_FD, &fd,
+                                               DBUS_TYPE_UINT16, &imtu,
+                                               DBUS_TYPE_UINT16, &omtu,
+                                               DBUS_TYPE_INVALID);
+       if (ret == FALSE)
+               goto fail;
+
+       media_owner_remove(owner);
+
+       return;
+
+fail:
+       media_transport_remove(transport, owner);
+}
+
+static guint resume_headset(struct media_transport *transport,
+                               struct media_owner *owner)
+{
+       struct audio_device *device = transport->device;
+
+       if (transport->in_use == TRUE)
+               goto done;
+
+       transport->in_use = headset_lock(device, HEADSET_LOCK_READ |
+                                               HEADSET_LOCK_WRITE);
+       if (transport->in_use == FALSE)
+               return 0;
+
+done:
+       return headset_request_stream(device, headset_resume_complete,
+                                       owner);
+}
+
+static void headset_suspend_complete(struct audio_device *dev, void *user_data)
+{
+       struct media_owner *owner = user_data;
+       struct media_transport *transport = owner->transport;
+
+       /* Release always succeeds */
+       if (owner->pending) {
+               owner->pending->id = 0;
+               media_request_reply(owner->pending, transport->conn, 0);
+               media_owner_remove(owner);
+       }
+
+       headset_unlock(dev, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE);
+       transport->in_use = FALSE;
+       media_transport_remove(transport, owner);
+}
+
+static guint suspend_headset(struct media_transport *transport,
+                                               struct media_owner *owner)
+{
+       struct audio_device *device = transport->device;
+
+       if (!owner) {
+               headset_unlock(device, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE);
+               transport->in_use = FALSE;
+               return 0;
+       }
+
+       return headset_suspend_stream(device, headset_suspend_complete, owner);
+}
+
+static void cancel_headset(struct media_transport *transport, guint id)
+{
+       headset_cancel_stream(transport->device, id);
+}
+
+static void gateway_resume_complete(struct audio_device *dev, GError *err,
+                                                       void *user_data)
+{
+       struct media_owner *owner = user_data;
+       struct media_request *req = owner->pending;
+       struct media_transport *transport = owner->transport;
+       int fd;
+       uint16_t imtu, omtu;
+       gboolean ret;
+
+       req->id = 0;
+
+       if (dev == NULL)
+               goto fail;
+
+       if (err) {
+               error("Failed to resume gateway: error %s", err->message);
+               goto fail;
+       }
+
+       fd = gateway_get_sco_fd(dev);
+       if (fd < 0)
+               goto fail;
+
+       imtu = 48;
+       omtu = 48;
+
+       media_transport_set_fd(transport, fd, imtu, omtu);
+
+       if (g_strstr_len(owner->accesstype, -1, "r") == NULL)
+               imtu = 0;
+
+       if (g_strstr_len(owner->accesstype, -1, "w") == NULL)
+               omtu = 0;
+
+       ret = g_dbus_send_reply(transport->conn, req->msg,
+                                               DBUS_TYPE_UNIX_FD, &fd,
+                                               DBUS_TYPE_UINT16, &imtu,
+                                               DBUS_TYPE_UINT16, &omtu,
+                                               DBUS_TYPE_INVALID);
+       if (ret == FALSE)
+               goto fail;
+
+       media_owner_remove(owner);
+
+       return;
+
+fail:
+       media_transport_remove(transport, owner);
+}
+
+static guint resume_gateway(struct media_transport *transport,
+                               struct media_owner *owner)
+{
+       struct audio_device *device = transport->device;
+
+       if (transport->in_use == TRUE)
+               goto done;
+
+       transport->in_use = gateway_lock(device, GATEWAY_LOCK_READ |
+                                               GATEWAY_LOCK_WRITE);
+       if (transport->in_use == FALSE)
+               return 0;
+
+done:
+       return gateway_request_stream(device, gateway_resume_complete,
+                                       owner);
+}
+
+static gboolean gateway_suspend_complete(gpointer user_data)
+{
+       struct media_owner *owner = user_data;
+       struct media_transport *transport = owner->transport;
+       struct audio_device *device = transport->device;
+
+       /* Release always succeeds */
+       if (owner->pending) {
+               owner->pending->id = 0;
+               media_request_reply(owner->pending, transport->conn, 0);
+               media_owner_remove(owner);
+       }
+
+       gateway_unlock(device, GATEWAY_LOCK_READ | GATEWAY_LOCK_WRITE);
+       transport->in_use = FALSE;
+       media_transport_remove(transport, owner);
+       return FALSE;
+}
+
+static guint suspend_gateway(struct media_transport *transport,
+                                               struct media_owner *owner)
+{
+       struct audio_device *device = transport->device;
+       static int id = 1;
+
+       if (!owner) {
+               gateway_unlock(device, GATEWAY_LOCK_READ | GATEWAY_LOCK_WRITE);
+               transport->in_use = FALSE;
+               return 0;
+       }
+
+       gateway_suspend_stream(device);
+       g_idle_add(gateway_suspend_complete, owner);
+       return id++;
+}
+
+static void cancel_gateway(struct media_transport *transport, guint id)
+{
+       gateway_cancel_stream(transport->device, id);
+}
+
+static void media_owner_exit(DBusConnection *connection, void *user_data)
+{
+       struct media_owner *owner = user_data;
+
+       owner->watch = 0;
+
+       media_owner_remove(owner);
+
+       media_transport_remove(owner->transport, owner);
+}
+
+static gboolean media_transport_acquire(struct media_transport *transport,
+                                                       const char *accesstype)
+{
+       gboolean read_lock = FALSE, write_lock = FALSE;
+
+       if (g_strstr_len(accesstype, -1, "r") != NULL) {
+               if (transport->read_lock == TRUE)
+                       return FALSE;
+               read_lock = TRUE;
+       }
+
+       if (g_strstr_len(accesstype, -1, "w") != NULL) {
+               if (transport->write_lock == TRUE)
+                       return FALSE;
+               write_lock = TRUE;
+       }
+
+       /* Check invalid accesstype */
+       if (read_lock == FALSE && write_lock == FALSE)
+               return FALSE;
+
+       if (read_lock) {
+               transport->read_lock = read_lock;
+               DBG("Transport %s: read lock acquired", transport->path);
+       }
+
+       if (write_lock) {
+               transport->write_lock = write_lock;
+               DBG("Transport %s: write lock acquired", transport->path);
+       }
+
+
+       return TRUE;
+}
+
+static void media_transport_add(struct media_transport *transport,
+                                       struct media_owner *owner)
+{
+       DBG("Transport %s Owner %s", transport->path, owner->name);
+       transport->owners = g_slist_append(transport->owners, owner);
+       owner->transport = transport;
+       owner->watch = g_dbus_add_disconnect_watch(transport->conn, owner->name,
+                                                       media_owner_exit,
+                                                       owner, NULL);
+}
+
+static struct media_owner *media_owner_create(DBusConnection *conn,
+                                               DBusMessage *msg,
+                                               const char *accesstype)
+{
+       struct media_owner *owner;
+
+       owner = g_new0(struct media_owner, 1);
+       owner->name = g_strdup(dbus_message_get_sender(msg));
+       owner->accesstype = g_strdup(accesstype);
+
+       DBG("Owner created: sender=%s accesstype=%s", owner->name,
+                       accesstype);
+
+       return owner;
+}
+
+static void media_owner_add(struct media_owner *owner,
+                                               struct media_request *req)
+{
+       DBG("Owner %s Request %s", owner->name,
+                                       dbus_message_get_member(req->msg));
+
+       owner->pending = req;
+}
+
+static struct media_owner *media_transport_find_owner(
+                                       struct media_transport *transport,
+                                       const char *name)
+{
+       GSList *l;
+
+       for (l = transport->owners; l; l = l->next) {
+               struct media_owner *owner = l->data;
+
+               if (g_strcmp0(owner->name, name) == 0)
+                       return owner;
+       }
+
+       return NULL;
+}
+
+static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct media_transport *transport = data;
+       struct media_owner *owner;
+       struct media_request *req;
+       const char *accesstype, *sender;
+       guint id;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &accesstype,
+                               DBUS_TYPE_INVALID))
+               return NULL;
+
+       sender = dbus_message_get_sender(msg);
+
+       owner = media_transport_find_owner(transport, sender);
+       if (owner != NULL)
+               return btd_error_not_authorized(msg);
+
+       if (media_transport_acquire(transport, accesstype) == FALSE)
+               return btd_error_not_authorized(msg);
+
+       owner = media_owner_create(conn, msg, accesstype);
+       id = transport->resume(transport, owner);
+       if (id == 0) {
+               media_transport_release(transport, accesstype);
+               media_owner_free(owner);
+               return btd_error_not_authorized(msg);
+       }
+
+       req = media_request_create(msg, id);
+       media_owner_add(owner, req);
+       media_transport_add(transport, owner);
+
+       return NULL;
+}
+
+static DBusMessage *release(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct media_transport *transport = data;
+       struct media_owner *owner;
+       const char *accesstype, *sender;
+       struct media_request *req;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &accesstype,
+                               DBUS_TYPE_INVALID))
+               return NULL;
+
+       sender = dbus_message_get_sender(msg);
+
+       owner = media_transport_find_owner(transport, sender);
+       if (owner == NULL)
+               return btd_error_not_authorized(msg);
+
+       if (g_strcmp0(owner->accesstype, accesstype) == 0) {
+               guint id;
+
+               /* Not the last owner, no need to suspend */
+               if (g_slist_length(transport->owners) != 1) {
+                       media_transport_remove(transport, owner);
+                       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+               }
+
+               if (owner->pending) {
+                       const char *member;
+
+                       member = dbus_message_get_member(owner->pending->msg);
+                       /* Cancel Acquire request if that exist */
+                       if (g_str_equal(member, "Acquire"))
+                               media_owner_remove(owner);
+                       else
+                               return btd_error_in_progress(msg);
+               }
+
+               id = transport->suspend(transport, owner);
+               if (id == 0) {
+                       media_transport_remove(transport, owner);
+                       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+               }
+
+               req = media_request_create(msg, id);
+               media_owner_add(owner, req);
+
+               return NULL;
+       } else if (g_strstr_len(owner->accesstype, -1, accesstype) != NULL) {
+               media_transport_release(transport, accesstype);
+               g_strdelimit(owner->accesstype, accesstype, ' ');
+       } else
+               return btd_error_not_authorized(msg);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static int set_property_a2dp(struct media_transport *transport,
+                                               const char *property,
+                                               DBusMessageIter *value)
+{
+       struct a2dp_transport *a2dp = transport->data;
+
+       if (g_strcmp0(property, "Delay") == 0) {
+               if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
+                       return -EINVAL;
+               dbus_message_iter_get_basic(value, &a2dp->delay);
+
+               /* FIXME: send new delay */
+               return 0;
+       } else if (g_strcmp0(property, "Volume") == 0) {
+               uint16_t volume;
+
+               if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(value, &volume);
+
+               if (volume > 127)
+                       return -EINVAL;
+
+               if (a2dp->volume == volume)
+                       return 0;
+
+               return avrcp_set_volume(transport->device, volume);
+       }
+
+       return -EINVAL;
+}
+
+static int set_property_headset(struct media_transport *transport,
+                                               const char *property,
+                                               DBusMessageIter *value)
+{
+       if (g_strcmp0(property, "NREC") == 0) {
+               gboolean nrec;
+
+               if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
+                       return -EINVAL;
+               dbus_message_iter_get_basic(value, &nrec);
+
+               /* FIXME: set new nrec */
+               return 0;
+       } else if (g_strcmp0(property, "InbandRingtone") == 0) {
+               gboolean inband;
+
+               if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN)
+                       return -EINVAL;
+               dbus_message_iter_get_basic(value, &inband);
+
+               /* FIXME: set new inband */
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int set_property_gateway(struct media_transport *transport,
+                                               const char *property,
+                                               DBusMessageIter *value)
+{
+       return -EINVAL;
+}
+
+static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct media_transport *transport = data;
+       DBusMessageIter iter;
+       DBusMessageIter value;
+       const char *property, *sender;
+       GSList *l;
+       int err;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return btd_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return btd_error_invalid_args(msg);
+       dbus_message_iter_recurse(&iter, &value);
+
+       sender = dbus_message_get_sender(msg);
+       err = -EINVAL;
+
+       /* Check if sender has acquired the transport */
+       for (l = transport->owners; l; l = l->next) {
+               struct media_owner *owner = l->data;
+
+               if (g_strcmp0(owner->name, sender) == 0) {
+                       err = transport->set_property(transport, property,
+                                                               &value);
+                       break;
+               }
+       }
+
+       if (err < 0) {
+               if (err == -EINVAL)
+                       return btd_error_invalid_args(msg);
+               return btd_error_failed(msg, strerror(-err));
+       }
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void get_properties_a2dp(struct media_transport *transport,
+                                               DBusMessageIter *dict)
+{
+       struct a2dp_transport *a2dp = transport->data;
+
+       dict_append_entry(dict, "Delay", DBUS_TYPE_UINT16, &a2dp->delay);
+
+       if (a2dp->volume <= 127)
+               dict_append_entry(dict, "Volume", DBUS_TYPE_UINT16,
+                                                       &a2dp->volume);
+}
+
+static void get_properties_headset(struct media_transport *transport,
+                                               DBusMessageIter *dict)
+{
+       gboolean nrec, inband;
+       const char *routing;
+
+       nrec = headset_get_nrec(transport->device);
+       dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &nrec);
+
+       inband = headset_get_inband(transport->device);
+       dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN, &inband);
+
+       routing = headset_get_sco_hci(transport->device) ? "HCI" : "PCM";
+       dict_append_entry(dict, "Routing", DBUS_TYPE_STRING, &routing);
+}
+
+static void get_properties_gateway(struct media_transport *transport,
+                                               DBusMessageIter *dict)
+{
+       /* None */
+}
+
+void transport_get_properties(struct media_transport *transport,
+                                                       DBusMessageIter *iter)
+{
+       DBusMessageIter dict;
+       const char *uuid;
+       uint8_t codec;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       /* Device */
+       dict_append_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH,
+                                               &transport->device->path);
+
+       uuid = media_endpoint_get_uuid(transport->endpoint);
+       dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
+
+       codec = media_endpoint_get_codec(transport->endpoint);
+       dict_append_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec);
+
+       dict_append_array(&dict, "Configuration", DBUS_TYPE_BYTE,
+                               &transport->configuration, transport->size);
+
+       if (transport->get_properties)
+               transport->get_properties(transport, &dict);
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+                                       void *data)
+{
+       struct media_transport *transport = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       transport_get_properties(transport, &iter);
+
+       return reply;
+}
+
+static const GDBusMethodTable transport_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_ASYNC_METHOD("Acquire",
+                       GDBUS_ARGS({ "access_type", "s" }),
+                       GDBUS_ARGS({ "fd", "h" }, { "mtu_r", "q" },
+                                                       { "mtu_w", "q" } ),
+                       acquire) },
+       { GDBUS_ASYNC_METHOD("Release",
+                       GDBUS_ARGS({ "access_type", "s" }), NULL,
+                       release ) },
+       { GDBUS_ASYNC_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+                       NULL, set_property) },
+       { },
+};
+
+static const GDBusSignalTable transport_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static void destroy_a2dp(void *data)
+{
+       struct a2dp_transport *a2dp = data;
+
+       if (a2dp->session)
+               avdtp_unref(a2dp->session);
+
+       g_free(a2dp);
+}
+
+static void destroy_headset(void *data)
+{
+       struct headset_transport *headset = data;
+
+       if (headset->nrec_id > 0)
+               headset_remove_nrec_cb(headset->device, headset->nrec_id);
+
+       g_free(headset);
+}
+
+static void media_transport_free(void *data)
+{
+       struct media_transport *transport = data;
+       GSList *l = transport->owners;
+
+       while (l) {
+               struct media_owner *owner = l->data;
+               l = l->next;
+               media_transport_remove(transport, owner);
+       }
+
+       g_slist_free(transport->owners);
+
+       if (transport->destroy != NULL)
+               transport->destroy(transport->data);
+
+       if (transport->conn)
+               dbus_connection_unref(transport->conn);
+
+       g_free(transport->configuration);
+       g_free(transport->path);
+       g_free(transport);
+}
+
+static void headset_nrec_changed(struct audio_device *dev, gboolean nrec,
+                                                       void *user_data)
+{
+       struct media_transport *transport = user_data;
+
+       DBG("");
+
+       emit_property_changed(transport->conn, transport->path,
+                               MEDIA_TRANSPORT_INTERFACE, "NREC",
+                               DBUS_TYPE_BOOLEAN, &nrec);
+}
+
+struct media_transport *media_transport_create(DBusConnection *conn,
+                                               struct media_endpoint *endpoint,
+                                               struct audio_device *device,
+                                               uint8_t *configuration,
+                                               size_t size)
+{
+       struct media_transport *transport;
+       const char *uuid;
+       static int fd = 0;
+
+       transport = g_new0(struct media_transport, 1);
+       transport->conn = dbus_connection_ref(conn);
+       transport->device = device;
+       transport->endpoint = endpoint;
+       transport->configuration = g_new(uint8_t, size);
+       memcpy(transport->configuration, configuration, size);
+       transport->size = size;
+       transport->path = g_strdup_printf("%s/fd%d", device->path, fd++);
+       transport->fd = -1;
+
+       uuid = media_endpoint_get_uuid(endpoint);
+       if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0 ||
+                       strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
+               struct a2dp_transport *a2dp;
+
+               a2dp = g_new0(struct a2dp_transport, 1);
+               a2dp->volume = -1;
+
+               transport->resume = resume_a2dp;
+               transport->suspend = suspend_a2dp;
+               transport->cancel = cancel_a2dp;
+               transport->get_properties = get_properties_a2dp;
+               transport->set_property = set_property_a2dp;
+               transport->data = a2dp;
+               transport->destroy = destroy_a2dp;
+       } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
+                       strcasecmp(uuid, HSP_AG_UUID) == 0) {
+               struct headset_transport *headset;
+
+               headset = g_new0(struct headset_transport, 1);
+               headset->device = device;
+               headset->nrec_id = headset_add_nrec_cb(device,
+                                                       headset_nrec_changed,
+                                                       transport);
+
+               transport->resume = resume_headset;
+               transport->suspend = suspend_headset;
+               transport->cancel = cancel_headset;
+               transport->get_properties = get_properties_headset;
+               transport->set_property = set_property_headset;
+               transport->data = headset;
+               transport->destroy = destroy_headset;
+       } else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
+                       strcasecmp(uuid, HSP_HS_UUID) == 0) {
+               transport->resume = resume_gateway;
+               transport->suspend = suspend_gateway;
+               transport->cancel = cancel_gateway;
+               transport->get_properties = get_properties_gateway;
+               transport->set_property = set_property_gateway;
+       } else
+               goto fail;
+
+       if (g_dbus_register_interface(transport->conn, transport->path,
+                               MEDIA_TRANSPORT_INTERFACE,
+                               transport_methods, transport_signals, NULL,
+                               transport, media_transport_free) == FALSE) {
+               error("Could not register transport %s", transport->path);
+               goto fail;
+       }
+
+       return transport;
+
+fail:
+       media_transport_free(transport);
+       return NULL;
+}
+
+const char *media_transport_get_path(struct media_transport *transport)
+{
+       return transport->path;
+}
+
+void media_transport_update_delay(struct media_transport *transport,
+                                                       uint16_t delay)
+{
+       struct a2dp_transport *a2dp = transport->data;
+
+       /* Check if delay really changed */
+       if (a2dp->delay == delay)
+               return;
+
+       a2dp->delay = delay;
+
+       emit_property_changed(transport->conn, transport->path,
+                               MEDIA_TRANSPORT_INTERFACE, "Delay",
+                               DBUS_TYPE_UINT16, &a2dp->delay);
+}
+
+struct audio_device *media_transport_get_dev(struct media_transport *transport)
+{
+       return transport->device;
+}
+
+void media_transport_update_volume(struct media_transport *transport,
+                                                               uint8_t volume)
+{
+       struct a2dp_transport *a2dp = transport->data;
+
+       /* Check if volume really changed */
+       if (a2dp->volume == volume)
+               return;
+
+       a2dp->volume = volume;
+
+       emit_property_changed(transport->conn, transport->path,
+                               MEDIA_TRANSPORT_INTERFACE, "Volume",
+                               DBUS_TYPE_UINT16, &a2dp->volume);
+}
diff --git a/audio/transport.h b/audio/transport.h
new file mode 100644 (file)
index 0000000..d20c327
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2007  Nokia Corporation
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct media_transport;
+
+struct media_transport *media_transport_create(DBusConnection *conn,
+                                               struct media_endpoint *endpoint,
+                                               struct audio_device *device,
+                                               uint8_t *configuration,
+                                               size_t size);
+
+void media_transport_destroy(struct media_transport *transport);
+const char *media_transport_get_path(struct media_transport *transport);
+struct audio_device *media_transport_get_dev(struct media_transport *transport);
+void media_transport_update_delay(struct media_transport *transport,
+                                                       uint16_t delay);
+void media_transport_update_volume(struct media_transport *transport,
+                                                               uint8_t volume);
+void transport_get_properties(struct media_transport *transport,
+                                                       DBusMessageIter *iter);
diff --git a/audio/unix.c b/audio/unix.c
new file mode 100644 (file)
index 0000000..9a10764
--- /dev/null
@@ -0,0 +1,1909 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include "log.h"
+#include "ipc.h"
+#include "device.h"
+#include "manager.h"
+#include "avdtp.h"
+#include "media.h"
+#include "a2dp.h"
+#include "headset.h"
+#include "sink.h"
+#include "source.h"
+#include "gateway.h"
+#include "unix.h"
+
+#define check_nul(str) (str[sizeof(str) - 1] == '\0')
+
+typedef enum {
+       TYPE_NONE,
+       TYPE_HEADSET,
+       TYPE_GATEWAY,
+       TYPE_SINK,
+       TYPE_SOURCE
+} service_type_t;
+
+typedef void (*notify_cb_t) (struct audio_device *dev, void *data);
+
+struct a2dp_data {
+       struct avdtp *session;
+       struct avdtp_stream *stream;
+       struct a2dp_sep *sep;
+};
+
+struct headset_data {
+       gboolean locked;
+};
+
+struct unix_client {
+       struct audio_device *dev;
+       GSList *caps;
+       service_type_t type;
+       char *interface;
+       uint8_t seid;
+       union {
+               struct a2dp_data a2dp;
+               struct headset_data hs;
+       } d;
+       int sock;
+       int lock;
+       int data_fd; /* To be deleted once two phase configuration is fully implemented */
+       unsigned int req_id;
+       unsigned int cb_id;
+       gboolean (*cancel) (struct audio_device *dev, unsigned int id);
+};
+
+static GSList *clients = NULL;
+
+static int unix_sock = -1;
+
+static void client_free(void *data)
+{
+       struct unix_client *client = data;
+
+       DBG("client_free(%p)", client);
+
+       if (client->cancel && client->dev && client->req_id > 0)
+               client->cancel(client->dev, client->req_id);
+
+       if (client->sock >= 0)
+               close(client->sock);
+
+       g_slist_free_full(client->caps, g_free);
+
+       g_free(client->interface);
+       g_free(client);
+}
+
+static int set_nonblocking(int fd)
+{
+       long arg;
+
+       arg = fcntl(fd, F_GETFL);
+       if (arg < 0)
+               return -errno;
+
+       /* Return if already nonblocking */
+       if (arg & O_NONBLOCK)
+               return 0;
+
+       arg |= O_NONBLOCK;
+       if (fcntl(fd, F_SETFL, arg) < 0)
+               return -errno;
+
+       return 0;
+}
+
+/* Pass file descriptor through local domain sockets (AF_LOCAL, formerly
+ * AF_UNIX) and the sendmsg() system call with the cmsg_type field of a "struct
+ * cmsghdr" set to SCM_RIGHTS and the data being an integer value equal to the
+ * handle of the file descriptor to be passed. */
+static int unix_sendmsg_fd(int sock, int fd)
+{
+       char cmsg_b[CMSG_SPACE(sizeof(int))], m = 'm';
+       struct cmsghdr *cmsg;
+       struct iovec iov = { &m, sizeof(m) };
+       struct msghdr msgh;
+
+       memset(&msgh, 0, sizeof(msgh));
+       msgh.msg_iov = &iov;
+       msgh.msg_iovlen = 1;
+       msgh.msg_control = &cmsg_b;
+       msgh.msg_controllen = CMSG_LEN(sizeof(int));
+
+       cmsg = CMSG_FIRSTHDR(&msgh);
+       cmsg->cmsg_level = SOL_SOCKET;
+       cmsg->cmsg_type = SCM_RIGHTS;
+       cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+       /* Initialize the payload */
+       memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+       return sendmsg(sock, &msgh, MSG_NOSIGNAL);
+}
+
+static void unix_ipc_sendmsg(struct unix_client *client,
+                                       const bt_audio_msg_header_t *msg)
+{
+       const char *type = bt_audio_strtype(msg->type);
+       const char *name = bt_audio_strname(msg->name);
+
+       DBG("Audio API: %s -> %s", type, name);
+
+       if (send(client->sock, msg, msg->length, 0) < 0)
+               error("Error %s(%d)", strerror(errno), errno);
+}
+
+static void unix_ipc_error(struct unix_client *client, uint8_t name, int err)
+{
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       bt_audio_error_t *rsp = (void *) buf;
+
+       if (!g_slist_find(clients, client))
+               return;
+
+       memset(buf, 0, sizeof(buf));
+       rsp->h.type = BT_ERROR;
+       rsp->h.name = name;
+       rsp->h.length = sizeof(*rsp);
+
+       rsp->posix_errno = err;
+
+       DBG("sending error %s(%d)", strerror(err), err);
+       unix_ipc_sendmsg(client, &rsp->h);
+}
+
+static service_type_t select_service(struct audio_device *dev, const char *interface)
+{
+       if (!interface) {
+               if (dev->sink && avdtp_is_connected(&dev->src, &dev->dst))
+                       return TYPE_SINK;
+               else if (dev->source && avdtp_is_connected(&dev->src,
+                                                               &dev->dst))
+                       return TYPE_SOURCE;
+               else if (dev->headset && headset_is_active(dev))
+                       return TYPE_HEADSET;
+               else if (dev->sink)
+                       return TYPE_SINK;
+               else if (dev->source)
+                       return TYPE_SOURCE;
+               else if (dev->headset)
+                       return TYPE_HEADSET;
+       } else if (!strcmp(interface, AUDIO_SOURCE_INTERFACE) && dev->source)
+               return TYPE_SOURCE;
+       else if (!strcmp(interface, AUDIO_SINK_INTERFACE) && dev->sink)
+               return TYPE_SINK;
+       else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset)
+               return TYPE_HEADSET;
+       else if (!strcmp(interface, AUDIO_GATEWAY_INTERFACE) && dev->gateway)
+               return TYPE_GATEWAY;
+
+       return TYPE_NONE;
+}
+
+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 unix_client *client = user_data;
+       struct a2dp_data *a2dp = &client->d.a2dp;
+
+       switch (new_state) {
+       case AVDTP_STATE_IDLE:
+               if (a2dp->sep) {
+                       a2dp_sep_unlock(a2dp->sep, a2dp->session);
+                       a2dp->sep = NULL;
+               }
+               if (a2dp->session) {
+                       avdtp_unref(a2dp->session);
+                       a2dp->session = NULL;
+               }
+               a2dp->stream = NULL;
+               client->cb_id = 0;
+               break;
+       default:
+               break;
+       }
+}
+
+static uint8_t headset_generate_capability(struct audio_device *dev,
+                                               codec_capabilities_t *codec)
+{
+       pcm_capabilities_t *pcm;
+
+       codec->seid = BT_A2DP_SEID_RANGE + 1;
+       codec->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+       codec->type = BT_HFP_CODEC_PCM;
+       codec->length = sizeof(*pcm);
+
+       pcm = (void *) codec;
+       pcm->sampling_rate = 8000;
+       if (dev->headset) {
+               if (headset_get_nrec(dev))
+                       pcm->flags |= BT_PCM_FLAG_NREC;
+               if (!headset_get_sco_hci(dev))
+                       pcm->flags |= BT_PCM_FLAG_PCM_ROUTING;
+               codec->configured = headset_is_active(dev);
+               codec->lock = headset_get_lock(dev);
+       } else {
+               pcm->flags |= BT_PCM_FLAG_NREC;
+               codec->configured = TRUE;
+               codec->lock = 0;
+       }
+
+       return codec->length;
+}
+
+static void headset_discovery_complete(struct audio_device *dev, void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_get_capabilities_rsp *rsp = (void *) buf;
+       uint8_t length;
+
+       client->req_id = 0;
+
+       if (!dev)
+               goto failed;
+
+       memset(buf, 0, sizeof(buf));
+
+       length = headset_generate_capability(dev, (void *) rsp->data);
+
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_GET_CAPABILITIES;
+       rsp->h.length = sizeof(*rsp) + length;
+
+       ba2str(&dev->src, rsp->source);
+       ba2str(&dev->dst, rsp->destination);
+       strncpy(rsp->object, dev->path, sizeof(rsp->object));
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       return;
+
+failed:
+       error("discovery failed");
+       unix_ipc_error(client, BT_SET_CONFIGURATION, EIO);
+}
+
+static void headset_setup_complete(struct audio_device *dev, void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_set_configuration_rsp *rsp = (void *) buf;
+
+       client->req_id = 0;
+
+       if (!dev)
+               goto failed;
+
+       memset(buf, 0, sizeof(buf));
+
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_SET_CONFIGURATION;
+       rsp->h.length = sizeof(*rsp);
+
+       rsp->link_mtu = 48;
+
+       client->data_fd = headset_get_sco_fd(dev);
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       return;
+
+failed:
+       error("config failed");
+       unix_ipc_error(client, BT_SET_CONFIGURATION, EIO);
+}
+
+static void gateway_setup_complete(struct audio_device *dev, GError *err, void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_set_configuration_rsp *rsp = (void *) buf;
+
+       if (err) {
+               unix_ipc_error(client, BT_SET_CONFIGURATION, err->code);
+               return;
+       }
+
+       client->req_id = 0;
+
+       memset(buf, 0, sizeof(buf));
+
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_SET_CONFIGURATION;
+       rsp->h.length = sizeof(*rsp);
+
+       rsp->link_mtu = 48;
+
+       client->data_fd = gateway_get_sco_fd(dev);
+
+       unix_ipc_sendmsg(client, &rsp->h);
+}
+
+static void headset_resume_complete(struct audio_device *dev, void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_start_stream_rsp *rsp = (void *) buf;
+       struct bt_new_stream_ind *ind = (void *) buf;
+
+       client->req_id = 0;
+
+       if (!dev)
+               goto failed;
+
+       client->data_fd = headset_get_sco_fd(dev);
+       if (client->data_fd < 0) {
+               error("Unable to get a SCO fd");
+               goto failed;
+       }
+
+       memset(buf, 0, sizeof(buf));
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_START_STREAM;
+       rsp->h.length = sizeof(*rsp);
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       memset(buf, 0, sizeof(buf));
+       ind->h.type = BT_INDICATION;
+       ind->h.name = BT_NEW_STREAM;
+       ind->h.length = sizeof(*ind);
+
+       unix_ipc_sendmsg(client, &ind->h);
+
+       if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) {
+               error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno);
+               goto failed;
+       }
+
+       return;
+
+failed:
+       error("headset_resume_complete: resume failed");
+       unix_ipc_error(client, BT_START_STREAM, EIO);
+}
+
+static void gateway_resume_complete(struct audio_device *dev, GError *err, void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_start_stream_rsp *rsp = (void *) buf;
+       struct bt_new_stream_ind *ind = (void *) buf;
+
+       if (err) {
+               unix_ipc_error(client, BT_START_STREAM, err->code);
+               return;
+       }
+
+       memset(buf, 0, sizeof(buf));
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_START_STREAM;
+       rsp->h.length = sizeof(*rsp);
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       memset(buf, 0, sizeof(buf));
+       ind->h.type = BT_INDICATION;
+       ind->h.name = BT_NEW_STREAM;
+       ind->h.length = sizeof(*ind);
+
+       unix_ipc_sendmsg(client, &ind->h);
+
+       client->data_fd = gateway_get_sco_fd(dev);
+       if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) {
+               error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno);
+               unix_ipc_error(client, BT_START_STREAM, EIO);
+       }
+
+       client->req_id = 0;
+}
+
+static void headset_suspend_complete(struct audio_device *dev, void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_stop_stream_rsp *rsp = (void *) buf;
+
+       if (!dev)
+               goto failed;
+
+       memset(buf, 0, sizeof(buf));
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_STOP_STREAM;
+       rsp->h.length = sizeof(*rsp);
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       return;
+
+failed:
+       error("suspend failed");
+       unix_ipc_error(client, BT_STOP_STREAM, EIO);
+}
+
+static void print_mpeg12(struct mpeg_codec_cap *mpeg)
+{
+       DBG("Media Codec: MPEG12"
+               " Channel Modes: %s%s%s%s"
+               " Frequencies: %s%s%s%s%s%s"
+               " Layers: %s%s%s"
+               " CRC: %s",
+               mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO ? "Mono " : "",
+               mpeg->channel_mode & MPEG_CHANNEL_MODE_DUAL_CHANNEL ?
+               "DualChannel " : "",
+               mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO ? "Stereo " : "",
+               mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO ?
+               "JointStereo " : "",
+               mpeg->frequency & MPEG_SAMPLING_FREQ_16000 ? "16Khz " : "",
+               mpeg->frequency & MPEG_SAMPLING_FREQ_22050 ? "22.05Khz " : "",
+               mpeg->frequency & MPEG_SAMPLING_FREQ_24000 ? "24Khz " : "",
+               mpeg->frequency & MPEG_SAMPLING_FREQ_32000 ? "32Khz " : "",
+               mpeg->frequency & MPEG_SAMPLING_FREQ_44100 ? "44.1Khz " : "",
+               mpeg->frequency & MPEG_SAMPLING_FREQ_48000 ? "48Khz " : "",
+               mpeg->layer & MPEG_LAYER_MP1 ? "1 " : "",
+               mpeg->layer & MPEG_LAYER_MP2 ? "2 " : "",
+               mpeg->layer & MPEG_LAYER_MP3 ? "3 " : "",
+               mpeg->crc ? "Yes" : "No");
+}
+
+static void print_sbc(struct sbc_codec_cap *sbc)
+{
+       DBG("Media Codec: SBC"
+               " Channel Modes: %s%s%s%s"
+               " Frequencies: %s%s%s%s"
+               " Subbands: %s%s"
+               " Blocks: %s%s%s%s"
+               " Bitpool: %d-%d",
+               sbc->channel_mode & SBC_CHANNEL_MODE_MONO ? "Mono " : "",
+               sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL ?
+               "DualChannel " : "",
+               sbc->channel_mode & SBC_CHANNEL_MODE_STEREO ? "Stereo " : "",
+               sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO ? "JointStereo" : "",
+               sbc->frequency & SBC_SAMPLING_FREQ_16000 ? "16Khz " : "",
+               sbc->frequency & SBC_SAMPLING_FREQ_32000 ? "32Khz " : "",
+               sbc->frequency & SBC_SAMPLING_FREQ_44100 ? "44.1Khz " : "",
+               sbc->frequency & SBC_SAMPLING_FREQ_48000 ? "48Khz " : "",
+               sbc->subbands & SBC_SUBBANDS_4 ? "4 " : "",
+               sbc->subbands & SBC_SUBBANDS_8 ? "8 " : "",
+               sbc->block_length & SBC_BLOCK_LENGTH_4 ? "4 " : "",
+               sbc->block_length & SBC_BLOCK_LENGTH_8 ? "8 " : "",
+               sbc->block_length & SBC_BLOCK_LENGTH_12 ? "12 " : "",
+               sbc->block_length & SBC_BLOCK_LENGTH_16 ? "16 " : "",
+               sbc->min_bitpool, sbc->max_bitpool);
+}
+
+static int a2dp_append_codec(struct bt_get_capabilities_rsp *rsp,
+                               struct avdtp_service_capability *cap,
+                               uint8_t seid,
+                               uint8_t type,
+                               uint8_t configured,
+                               uint8_t lock)
+{
+       struct avdtp_media_codec_capability *codec_cap = (void *) cap->data;
+       codec_capabilities_t *codec = (void *) rsp + rsp->h.length;
+       size_t space_left;
+
+       if (rsp->h.length > BT_SUGGESTED_BUFFER_SIZE)
+               return -ENOMEM;
+
+       space_left = BT_SUGGESTED_BUFFER_SIZE - rsp->h.length;
+
+       /* endianness prevents direct cast */
+       if (codec_cap->media_codec_type == A2DP_CODEC_SBC) {
+               struct sbc_codec_cap *sbc_cap = (void *) codec_cap;
+               sbc_capabilities_t *sbc = (void *) codec;
+
+               if (space_left < sizeof(sbc_capabilities_t))
+                       return -ENOMEM;
+
+               if (type == AVDTP_SEP_TYPE_SINK)
+                       codec->type = BT_A2DP_SBC_SINK;
+               else if (type == AVDTP_SEP_TYPE_SOURCE)
+                       codec->type = BT_A2DP_SBC_SOURCE;
+               else
+                       return -EINVAL;
+
+               codec->length = sizeof(sbc_capabilities_t);
+
+               sbc->channel_mode = sbc_cap->channel_mode;
+               sbc->frequency = sbc_cap->frequency;
+               sbc->allocation_method = sbc_cap->allocation_method;
+               sbc->subbands = sbc_cap->subbands;
+               sbc->block_length = sbc_cap->block_length;
+               sbc->min_bitpool = sbc_cap->min_bitpool;
+               sbc->max_bitpool = sbc_cap->max_bitpool;
+
+               print_sbc(sbc_cap);
+       } else if (codec_cap->media_codec_type == A2DP_CODEC_MPEG12) {
+               struct mpeg_codec_cap *mpeg_cap = (void *) codec_cap;
+               mpeg_capabilities_t *mpeg = (void *) codec;
+
+               if (space_left < sizeof(mpeg_capabilities_t))
+                       return -ENOMEM;
+
+               if (type == AVDTP_SEP_TYPE_SINK)
+                       codec->type = BT_A2DP_MPEG12_SINK;
+               else if (type == AVDTP_SEP_TYPE_SOURCE)
+                       codec->type = BT_A2DP_MPEG12_SOURCE;
+               else
+                       return -EINVAL;
+
+               codec->length = sizeof(mpeg_capabilities_t);
+
+               mpeg->channel_mode = mpeg_cap->channel_mode;
+               mpeg->crc = mpeg_cap->crc;
+               mpeg->layer = mpeg_cap->layer;
+               mpeg->frequency = mpeg_cap->frequency;
+               mpeg->mpf = mpeg_cap->mpf;
+               mpeg->bitrate = mpeg_cap->bitrate;
+
+               print_mpeg12(mpeg_cap);
+       } else {
+               size_t codec_length, type_length, total_length;
+
+               codec_length = cap->length - (sizeof(struct avdtp_service_capability)
+                               + sizeof(struct avdtp_media_codec_capability));
+               type_length = sizeof(codec_cap->media_codec_type);
+               total_length = type_length + codec_length +
+                               sizeof(codec_capabilities_t);
+
+               if (space_left < total_length)
+                       return -ENOMEM;
+
+               if (type == AVDTP_SEP_TYPE_SINK)
+                       codec->type = BT_A2DP_UNKNOWN_SINK;
+               else if (type == AVDTP_SEP_TYPE_SOURCE)
+                       codec->type = BT_A2DP_UNKNOWN_SOURCE;
+               else
+                       return -EINVAL;
+
+               codec->length = total_length;
+               memcpy(codec->data, &codec_cap->media_codec_type, type_length);
+               memcpy(codec->data + type_length, codec_cap->data,
+                       codec_length);
+       }
+
+       codec->seid = seid;
+       codec->configured = configured;
+       codec->lock = lock;
+       rsp->h.length += codec->length;
+
+       DBG("Append %s seid %d - length %d - total %d",
+               configured ? "configured" : "", seid, codec->length,
+               rsp->h.length);
+
+       return 0;
+}
+
+static void a2dp_discovery_complete(struct avdtp *session, GSList *seps,
+                                       struct avdtp_error *err,
+                                       void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_get_capabilities_rsp *rsp = (void *) buf;
+       struct a2dp_data *a2dp = &client->d.a2dp;
+
+       if (!g_slist_find(clients, client)) {
+               DBG("Client disconnected during discovery");
+               return;
+       }
+
+       if (err)
+               goto failed;
+
+       memset(buf, 0, sizeof(buf));
+       client->req_id = 0;
+
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_GET_CAPABILITIES;
+       rsp->h.length = sizeof(*rsp);
+       ba2str(&client->dev->src, rsp->source);
+       ba2str(&client->dev->dst, rsp->destination);
+       strncpy(rsp->object, client->dev->path, sizeof(rsp->object));
+
+       for (; seps; seps = g_slist_next(seps)) {
+               struct avdtp_remote_sep *rsep = seps->data;
+               struct a2dp_sep *sep;
+               struct avdtp_service_capability *cap;
+               struct avdtp_stream *stream;
+               uint8_t type, seid, configured = 0, lock = 0;
+               GSList *cl;
+
+               type = avdtp_get_type(rsep);
+
+               if (type != AVDTP_SEP_TYPE_SINK &&
+                                               type != AVDTP_SEP_TYPE_SOURCE)
+                       continue;
+
+               cap = avdtp_get_codec(rsep);
+
+               if (cap->category != AVDTP_MEDIA_CODEC)
+                       continue;
+
+               seid = avdtp_get_seid(rsep);
+
+               if (client->seid != 0 && client->seid != seid)
+                       continue;
+
+               stream = avdtp_get_stream(rsep);
+               if (stream) {
+                       configured = 1;
+                       if (client->seid == seid)
+                               cap = avdtp_stream_get_codec(stream);
+               }
+
+               for (cl = clients; cl; cl = cl->next) {
+                       struct unix_client *c = cl->data;
+                       struct a2dp_data *ca2dp = &c->d.a2dp;
+
+                       if (ca2dp->session == session && c->seid == seid) {
+                               lock = c->lock;
+                               break;
+                       }
+               }
+
+               sep = a2dp_get_sep(session, stream);
+               if (sep && a2dp_sep_get_lock(sep))
+                       lock = BT_WRITE_LOCK;
+
+               a2dp_append_codec(rsp, cap, seid, type, configured, lock);
+       }
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       return;
+
+failed:
+       error("discovery failed");
+       unix_ipc_error(client, BT_GET_CAPABILITIES, EIO);
+
+       if (a2dp->sep) {
+               a2dp_sep_unlock(a2dp->sep, a2dp->session);
+               a2dp->sep = NULL;
+       }
+
+       avdtp_unref(a2dp->session);
+       a2dp->session = NULL;
+       a2dp->stream = NULL;
+}
+
+static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_error *err,
+                                       void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_set_configuration_rsp *rsp = (void *) buf;
+       struct a2dp_data *a2dp = &client->d.a2dp;
+       uint16_t imtu, omtu;
+       GSList *caps;
+
+       client->req_id = 0;
+
+       if (err)
+               goto failed;
+
+       memset(buf, 0, sizeof(buf));
+
+       if (!stream)
+               goto failed;
+
+       if (client->cb_id > 0)
+               avdtp_stream_remove_cb(a2dp->session, a2dp->stream,
+                                                               client->cb_id);
+
+       a2dp->sep = sep;
+       a2dp->stream = stream;
+
+       if (!avdtp_stream_get_transport(stream, &client->data_fd, &imtu, &omtu,
+                                       &caps)) {
+               error("Unable to get stream transport");
+               goto failed;
+       }
+
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_SET_CONFIGURATION;
+       rsp->h.length = sizeof(*rsp);
+
+       /* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */
+       rsp->link_mtu = omtu;
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       client->cb_id = avdtp_stream_add_cb(session, stream,
+                                               stream_state_changed, client);
+
+       return;
+
+failed:
+       error("config failed");
+
+       unix_ipc_error(client, BT_SET_CONFIGURATION, EIO);
+
+       avdtp_unref(a2dp->session);
+
+       a2dp->session = NULL;
+       a2dp->stream = NULL;
+       a2dp->sep = NULL;
+}
+
+static void a2dp_resume_complete(struct avdtp *session,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_start_stream_rsp *rsp = (void *) buf;
+       struct bt_new_stream_ind *ind = (void *) buf;
+       struct a2dp_data *a2dp = &client->d.a2dp;
+
+       if (err)
+               goto failed;
+
+       memset(buf, 0, sizeof(buf));
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_START_STREAM;
+       rsp->h.length = sizeof(*rsp);
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       memset(buf, 0, sizeof(buf));
+       ind->h.type = BT_RESPONSE;
+       ind->h.name = BT_NEW_STREAM;
+       rsp->h.length = sizeof(*ind);
+
+       unix_ipc_sendmsg(client, &ind->h);
+
+       if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) {
+               error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno);
+               goto failed;
+       }
+
+       return;
+
+failed:
+       error("resume failed");
+
+       unix_ipc_error(client, BT_START_STREAM, EIO);
+
+       if (client->cb_id > 0) {
+               avdtp_stream_remove_cb(a2dp->session, a2dp->stream,
+                                       client->cb_id);
+               client->cb_id = 0;
+       }
+
+       if (a2dp->sep) {
+               a2dp_sep_unlock(a2dp->sep, a2dp->session);
+               a2dp->sep = NULL;
+       }
+
+       avdtp_unref(a2dp->session);
+       a2dp->session = NULL;
+       a2dp->stream = NULL;
+}
+
+static void a2dp_suspend_complete(struct avdtp *session,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_stop_stream_rsp *rsp = (void *) buf;
+
+       if (err)
+               goto failed;
+
+       memset(buf, 0, sizeof(buf));
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_STOP_STREAM;
+       rsp->h.length = sizeof(*rsp);
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       return;
+
+failed:
+       error("suspend failed");
+
+       unix_ipc_error(client, BT_STOP_STREAM, EIO);
+}
+
+static void start_discovery(struct audio_device *dev, struct unix_client *client)
+{
+       struct a2dp_data *a2dp;
+       int err = 0;
+
+       switch (client->type) {
+       case TYPE_SINK:
+       case TYPE_SOURCE:
+               a2dp = &client->d.a2dp;
+
+               if (!a2dp->session)
+                       a2dp->session = avdtp_get(&dev->src, &dev->dst);
+
+               if (!a2dp->session) {
+                       error("Unable to get a session");
+                       goto failed;
+               }
+
+               err = avdtp_discover(a2dp->session, a2dp_discovery_complete,
+                                       client);
+               if (err) {
+                       if (a2dp->session) {
+                               avdtp_unref(a2dp->session);
+                               a2dp->session = NULL;
+                       }
+                       goto failed;
+               }
+               break;
+
+       case TYPE_HEADSET:
+       case TYPE_GATEWAY:
+               headset_discovery_complete(dev, client);
+               break;
+
+       default:
+               error("No known services for device");
+               goto failed;
+       }
+
+       client->dev = dev;
+
+       return;
+
+failed:
+       unix_ipc_error(client, BT_GET_CAPABILITIES, err ? : EIO);
+}
+
+static void open_complete(struct audio_device *dev, void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_open_rsp *rsp = (void *) buf;
+
+       memset(buf, 0, sizeof(buf));
+
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_OPEN;
+       rsp->h.length = sizeof(*rsp);
+
+       ba2str(&dev->src, rsp->source);
+       ba2str(&dev->dst, rsp->destination);
+       strncpy(rsp->object, dev->path, sizeof(rsp->object));
+
+       unix_ipc_sendmsg(client, &rsp->h);
+}
+
+static void start_open(struct audio_device *dev, struct unix_client *client)
+{
+       struct a2dp_data *a2dp;
+       struct headset_data *hs;
+       struct avdtp_remote_sep *rsep;
+       gboolean unref_avdtp_on_fail = FALSE;
+
+       switch (client->type) {
+       case TYPE_SINK:
+       case TYPE_SOURCE:
+               a2dp = &client->d.a2dp;
+
+               if (!a2dp->session) {
+                       a2dp->session = avdtp_get(&dev->src, &dev->dst);
+                       unref_avdtp_on_fail = TRUE;
+               }
+
+               if (!a2dp->session) {
+                       error("Unable to get a session");
+                       goto failed;
+               }
+
+               if (a2dp->sep) {
+                       error("Client already has an opened session");
+                       goto failed;
+               }
+
+               rsep = avdtp_get_remote_sep(a2dp->session, client->seid);
+               if (!rsep) {
+                       error("Invalid seid %d", client->seid);
+                       goto failed;
+               }
+
+               a2dp->sep = a2dp_get(a2dp->session, rsep);
+               if (!a2dp->sep) {
+                       error("seid %d not available or locked", client->seid);
+                       goto failed;
+               }
+
+               if (!a2dp_sep_lock(a2dp->sep, a2dp->session)) {
+                       error("Unable to open seid %d", client->seid);
+                       a2dp->sep = NULL;
+                       goto failed;
+               }
+
+               break;
+
+       case TYPE_HEADSET:
+               hs = &client->d.hs;
+
+               if (hs->locked) {
+                       error("Client already has an opened session");
+                       goto failed;
+               }
+
+               hs->locked = headset_lock(dev, client->lock);
+               if (!hs->locked) {
+                       error("Unable to open seid %d", client->seid);
+                       goto failed;
+               }
+               break;
+
+        case TYPE_GATEWAY:
+                break;
+       default:
+               error("No known services for device");
+               goto failed;
+       }
+
+       client->dev = dev;
+
+       open_complete(dev, client);
+
+       return;
+
+failed:
+       if (unref_avdtp_on_fail && a2dp->session) {
+               avdtp_unref(a2dp->session);
+               a2dp->session = NULL;
+       }
+       unix_ipc_error(client, BT_OPEN, EINVAL);
+}
+
+static void start_config(struct audio_device *dev, struct unix_client *client)
+{
+       struct a2dp_data *a2dp;
+       struct headset_data *hs;
+       unsigned int id;
+
+       switch (client->type) {
+       case TYPE_SINK:
+       case TYPE_SOURCE:
+               a2dp = &client->d.a2dp;
+
+               if (!a2dp->session)
+                       a2dp->session = avdtp_get(&dev->src, &dev->dst);
+
+               if (!a2dp->session) {
+                       error("Unable to get a session");
+                       goto failed;
+               }
+
+               if (!a2dp->sep) {
+                       error("seid %d not opened", client->seid);
+                       goto failed;
+               }
+
+               id = a2dp_config(a2dp->session, a2dp->sep, a2dp_config_complete,
+                                       client->caps, client);
+               client->cancel = a2dp_cancel;
+               break;
+
+       case TYPE_HEADSET:
+               hs = &client->d.hs;
+
+               if (!hs->locked) {
+                       error("seid %d not opened", client->seid);
+                       goto failed;
+               }
+
+               id = headset_config_stream(dev, TRUE, headset_setup_complete,
+                                               client);
+               client->cancel = headset_cancel_stream;
+               break;
+       case TYPE_GATEWAY:
+               id = gateway_config_stream(dev, gateway_setup_complete, client);
+               client->cancel = gateway_cancel_stream;
+               break;
+
+       default:
+               error("No known services for device");
+               goto failed;
+       }
+
+       if (id == 0) {
+               error("config failed");
+               goto failed;
+       }
+
+       client->req_id = id;
+       g_slist_free(client->caps);
+       client->caps = NULL;
+
+       return;
+
+failed:
+       unix_ipc_error(client, BT_SET_CONFIGURATION, EIO);
+}
+
+static void start_resume(struct audio_device *dev, struct unix_client *client)
+{
+       struct a2dp_data *a2dp = NULL;
+       struct headset_data *hs;
+       unsigned int id;
+       struct avdtp *session = NULL;
+
+       switch (client->type) {
+       case TYPE_SINK:
+       case TYPE_SOURCE:
+               a2dp = &client->d.a2dp;
+
+               if (!a2dp->sep) {
+                       error("seid not opened");
+                       goto failed;
+               }
+
+               if (!a2dp->session) {
+                       session = avdtp_get(&dev->src, &dev->dst);
+                       if (!session) {
+                               error("Unable to get a session");
+                               goto failed;
+                       }
+                       a2dp->session = session;
+               }
+
+               id = a2dp_resume(a2dp->session, a2dp->sep, a2dp_resume_complete,
+                                       client);
+               client->cancel = a2dp_cancel;
+
+               break;
+
+       case TYPE_HEADSET:
+               hs = &client->d.hs;
+
+               if (!hs->locked) {
+                       error("seid not opened");
+                       goto failed;
+               }
+
+               id = headset_request_stream(dev, headset_resume_complete,
+                                               client);
+               client->cancel = headset_cancel_stream;
+               break;
+
+       case TYPE_GATEWAY:
+               id = gateway_request_stream(dev, gateway_resume_complete,
+                                               client);
+               client->cancel = gateway_cancel_stream;
+               break;
+
+       default:
+               error("No known services for device");
+               goto failed;
+       }
+
+       if (id == 0) {
+               error("start_resume: resume failed");
+               goto failed;
+       }
+
+       client->req_id = id;
+
+       return;
+
+failed:
+       if (session) {
+               avdtp_unref(session);
+               a2dp->session = NULL;
+       }
+
+       unix_ipc_error(client, BT_START_STREAM, EIO);
+}
+
+static void start_suspend(struct audio_device *dev, struct unix_client *client)
+{
+       struct a2dp_data *a2dp = NULL;
+       struct headset_data *hs;
+       unsigned int id;
+       struct avdtp *session = NULL;
+
+       switch (client->type) {
+       case TYPE_SINK:
+       case TYPE_SOURCE:
+               a2dp = &client->d.a2dp;
+
+               if (!a2dp->sep) {
+                       error("seid not opened");
+                       goto failed;
+               }
+
+               if (!a2dp->session) {
+                       session = avdtp_get(&dev->src, &dev->dst);
+                       if (!session) {
+                               error("Unable to get a session");
+                               goto failed;
+                       }
+                       a2dp->session = session;
+               }
+
+               if (!a2dp->sep) {
+                       error("Unable to get a sep");
+                       goto failed;
+               }
+
+               id = a2dp_suspend(a2dp->session, a2dp->sep,
+                                       a2dp_suspend_complete, client);
+               client->cancel = a2dp_cancel;
+               break;
+
+       case TYPE_HEADSET:
+               hs = &client->d.hs;
+
+               if (!hs->locked) {
+                       error("seid not opened");
+                       goto failed;
+               }
+
+               id = headset_suspend_stream(dev, headset_suspend_complete,
+                                               client);
+               client->cancel = headset_cancel_stream;
+               break;
+
+       case TYPE_GATEWAY:
+               gateway_suspend_stream(dev);
+               client->cancel = gateway_cancel_stream;
+               headset_suspend_complete(dev, client);
+               id = 1;
+               break;
+
+       default:
+               error("No known services for device");
+               goto failed;
+       }
+
+       if (id == 0) {
+               error("suspend failed");
+               goto failed;
+       }
+
+       return;
+
+failed:
+       if (session) {
+               avdtp_unref(session);
+               a2dp->session = NULL;
+       }
+
+       unix_ipc_error(client, BT_STOP_STREAM, EIO);
+}
+
+static void close_complete(struct audio_device *dev, void *user_data)
+{
+       struct unix_client *client = user_data;
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_close_rsp *rsp = (void *) buf;
+
+       memset(buf, 0, sizeof(buf));
+
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_CLOSE;
+       rsp->h.length = sizeof(*rsp);
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       return;
+}
+
+static void start_close(struct audio_device *dev, struct unix_client *client,
+                       gboolean reply)
+{
+       struct a2dp_data *a2dp;
+       struct headset_data *hs;
+
+       if (!client->dev)
+               goto failed;
+
+       switch (client->type) {
+       case TYPE_HEADSET:
+               hs = &client->d.hs;
+
+               if (client->dev && hs->locked) {
+                       headset_unlock(client->dev, client->lock);
+                       hs->locked = FALSE;
+               }
+               break;
+        case TYPE_GATEWAY:
+                break;
+       case TYPE_SOURCE:
+       case TYPE_SINK:
+               a2dp = &client->d.a2dp;
+
+               if (client->cb_id > 0) {
+                       avdtp_stream_remove_cb(a2dp->session, a2dp->stream,
+                                                               client->cb_id);
+                       client->cb_id = 0;
+               }
+               if (a2dp->sep) {
+                       a2dp_sep_unlock(a2dp->sep, a2dp->session);
+                       a2dp->sep = NULL;
+               }
+               if (a2dp->session) {
+                       avdtp_unref(a2dp->session);
+                       a2dp->session = NULL;
+               }
+               a2dp->stream = NULL;
+               break;
+       default:
+               error("No known services for device");
+               goto failed;
+       }
+
+       if (!reply)
+               return;
+
+       close_complete(dev, client);
+       client->dev = NULL;
+
+       return;
+
+failed:
+       if (reply)
+               unix_ipc_error(client, BT_STOP_STREAM, EINVAL);
+}
+
+static void handle_getcapabilities_req(struct unix_client *client,
+                                       struct bt_get_capabilities_req *req)
+{
+       struct audio_device *dev;
+       bdaddr_t src, dst;
+       int err = EIO;
+       const char *interface;
+
+       if (!check_nul(req->source) || !check_nul(req->destination) ||
+                       !check_nul(req->object)) {
+               err = EINVAL;
+               goto failed;
+       }
+
+       str2ba(req->source, &src);
+       str2ba(req->destination, &dst);
+
+       if (!manager_find_device(req->object, &src, &dst, NULL, FALSE))
+               goto failed;
+
+       if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO)
+               interface = AUDIO_HEADSET_INTERFACE;
+       else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+               interface = AUDIO_SINK_INTERFACE;
+       else
+               interface = client->interface;
+
+       dev = manager_find_device(req->object, &src, &dst, interface, TRUE);
+       if (!dev && (req->flags & BT_FLAG_AUTOCONNECT))
+               dev = manager_find_device(req->object, &src, &dst,
+                                                       interface, FALSE);
+
+       if (!dev) {
+               if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO)
+                       interface = AUDIO_GATEWAY_INTERFACE;
+               else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
+                       interface = AUDIO_SOURCE_INTERFACE;
+               else
+                       interface = NULL;
+               dev = manager_find_device(req->object, &src, &dst,
+                                                       interface, TRUE);
+               if (!dev && (req->flags & BT_FLAG_AUTOCONNECT))
+                       dev = manager_find_device(req->object, &src, &dst,
+                                                       interface, FALSE);
+       }
+
+       if (!dev) {
+               error("Unable to find a matching device");
+               goto failed;
+       }
+
+       client->type = select_service(dev, interface);
+       if (client->type == TYPE_NONE) {
+               error("No matching service found");
+               goto failed;
+       }
+
+       if (g_strcmp0(interface, client->interface) != 0) {
+               g_free(client->interface);
+               client->interface = g_strdup(interface);
+       }
+
+       client->seid = req->seid;
+
+       start_discovery(dev, client);
+
+       return;
+
+failed:
+       unix_ipc_error(client, BT_GET_CAPABILITIES, err);
+}
+
+static int handle_sco_open(struct unix_client *client, struct bt_open_req *req)
+{
+       if (!client->interface)
+               client->interface = g_strdup(AUDIO_HEADSET_INTERFACE);
+       else if (!g_str_equal(client->interface, AUDIO_HEADSET_INTERFACE) &&
+               !g_str_equal(client->interface, AUDIO_GATEWAY_INTERFACE))
+               return -EIO;
+
+       DBG("open sco - object=%s source=%s destination=%s lock=%s%s",
+                       strcmp(req->object, "") ? req->object : "ANY",
+                       strcmp(req->source, "") ? req->source : "ANY",
+                       strcmp(req->destination, "") ? req->destination : "ANY",
+                       req->lock & BT_READ_LOCK ? "read" : "",
+                       req->lock & BT_WRITE_LOCK ? "write" : "");
+
+       return 0;
+}
+
+static int handle_a2dp_open(struct unix_client *client, struct bt_open_req *req)
+{
+       if (!client->interface)
+               /* FIXME: are we treating a sink or a source? */
+               client->interface = g_strdup(AUDIO_SINK_INTERFACE);
+       else if (!g_str_equal(client->interface, AUDIO_SINK_INTERFACE) &&
+                       !g_str_equal(client->interface, AUDIO_SOURCE_INTERFACE))
+               return -EIO;
+
+       DBG("open a2dp - object=%s source=%s destination=%s lock=%s%s",
+                       strcmp(req->object, "") ? req->object : "ANY",
+                       strcmp(req->source, "") ? req->source : "ANY",
+                       strcmp(req->destination, "") ? req->destination : "ANY",
+                       req->lock & BT_READ_LOCK ? "read" : "",
+                       req->lock & BT_WRITE_LOCK ? "write" : "");
+
+       return 0;
+}
+
+static void handle_open_req(struct unix_client *client, struct bt_open_req *req)
+{
+       struct audio_device *dev;
+       bdaddr_t src, dst;
+       int err = 0;
+
+       if (!check_nul(req->source) || !check_nul(req->destination) ||
+                       !check_nul(req->object)) {
+               err = EINVAL;
+               goto failed;
+       }
+
+       str2ba(req->source, &src);
+       str2ba(req->destination, &dst);
+
+       if (req->seid > BT_A2DP_SEID_RANGE) {
+               err = handle_sco_open(client, req);
+               if (err < 0) {
+                       err = -err;
+                       goto failed;
+               }
+       } else {
+               err = handle_a2dp_open(client, req);
+               if (err < 0) {
+                       err = -err;
+                       goto failed;
+               }
+       }
+
+       if (!manager_find_device(req->object, &src, &dst, NULL, FALSE))
+               goto failed;
+
+       dev = manager_find_device(req->object, &src, &dst, client->interface,
+                               TRUE);
+       if (!dev)
+               dev = manager_find_device(req->object, &src, &dst,
+                                       client->interface, FALSE);
+
+       if (!dev)
+               goto failed;
+
+       client->seid = req->seid;
+       client->lock = req->lock;
+
+       start_open(dev, client);
+
+       return;
+
+failed:
+       unix_ipc_error(client, BT_OPEN, err ? : EIO);
+}
+
+static int handle_sco_transport(struct unix_client *client,
+                               struct bt_set_configuration_req *req)
+{
+       struct audio_device *dev = client->dev;
+
+       if (!client->interface) {
+               if (dev->headset)
+                       client->interface = g_strdup(AUDIO_HEADSET_INTERFACE);
+               else if (dev->gateway)
+                       client->interface = g_strdup(AUDIO_GATEWAY_INTERFACE);
+               else
+                       return -EIO;
+       } else if (!g_str_equal(client->interface, AUDIO_HEADSET_INTERFACE) &&
+                       !g_str_equal(client->interface, AUDIO_GATEWAY_INTERFACE))
+               return -EIO;
+
+       return 0;
+}
+
+static int handle_a2dp_transport(struct unix_client *client,
+                               struct bt_set_configuration_req *req)
+{
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct sbc_codec_cap sbc_cap;
+       struct mpeg_codec_cap mpeg_cap;
+
+       if (!client->interface)
+               /* FIXME: are we treating a sink or a source? */
+               client->interface = g_strdup(AUDIO_SINK_INTERFACE);
+       else if (!g_str_equal(client->interface, AUDIO_SINK_INTERFACE) &&
+                       !g_str_equal(client->interface, AUDIO_SOURCE_INTERFACE))
+               return -EIO;
+
+       g_slist_free_full(client->caps, g_free);
+       client->caps = NULL;
+
+       media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+                                               NULL, 0);
+
+       client->caps = g_slist_append(client->caps, media_transport);
+
+       if (req->codec.type == BT_A2DP_MPEG12_SINK ||
+               req->codec.type == BT_A2DP_MPEG12_SOURCE) {
+               mpeg_capabilities_t *mpeg = (void *) &req->codec;
+
+               memset(&mpeg_cap, 0, sizeof(mpeg_cap));
+
+               mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+               mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12;
+               mpeg_cap.channel_mode = mpeg->channel_mode;
+               mpeg_cap.crc = mpeg->crc;
+               mpeg_cap.layer = mpeg->layer;
+               mpeg_cap.frequency = mpeg->frequency;
+               mpeg_cap.mpf = mpeg->mpf;
+               mpeg_cap.bitrate = mpeg->bitrate;
+
+               media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap,
+                                                       sizeof(mpeg_cap));
+
+               print_mpeg12(&mpeg_cap);
+       } else if (req->codec.type == BT_A2DP_SBC_SINK ||
+                       req->codec.type == BT_A2DP_SBC_SOURCE) {
+               sbc_capabilities_t *sbc = (void *) &req->codec;
+
+               memset(&sbc_cap, 0, sizeof(sbc_cap));
+
+               sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO;
+               sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC;
+               sbc_cap.channel_mode = sbc->channel_mode;
+               sbc_cap.frequency = sbc->frequency;
+               sbc_cap.allocation_method = sbc->allocation_method;
+               sbc_cap.subbands = sbc->subbands;
+               sbc_cap.block_length = sbc->block_length;
+               sbc_cap.min_bitpool = sbc->min_bitpool;
+               sbc_cap.max_bitpool = sbc->max_bitpool;
+
+               media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap,
+                                                       sizeof(sbc_cap));
+
+               print_sbc(&sbc_cap);
+       } else
+               return -EINVAL;
+
+       client->caps = g_slist_append(client->caps, media_codec);
+
+       return 0;
+}
+
+static void handle_setconfiguration_req(struct unix_client *client,
+                                       struct bt_set_configuration_req *req)
+{
+       int err = 0;
+
+       if (req->codec.seid != client->seid) {
+               error("Unable to set configuration: seid %d not opened",
+                               req->codec.seid);
+               goto failed;
+       }
+
+       if (!client->dev)
+               goto failed;
+
+       if (req->codec.transport == BT_CAPABILITIES_TRANSPORT_SCO) {
+               err = handle_sco_transport(client, req);
+               if (err < 0) {
+                       err = -err;
+                       goto failed;
+               }
+       } else if (req->codec.transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+               err = handle_a2dp_transport(client, req);
+               if (err < 0) {
+                       err = -err;
+                       goto failed;
+               }
+       }
+
+       start_config(client->dev, client);
+
+       return;
+
+failed:
+       unix_ipc_error(client, BT_SET_CONFIGURATION, err ? : EIO);
+}
+
+static void handle_streamstart_req(struct unix_client *client,
+                                       struct bt_start_stream_req *req)
+{
+       if (!client->dev)
+               goto failed;
+
+       start_resume(client->dev, client);
+
+       return;
+
+failed:
+       unix_ipc_error(client, BT_START_STREAM, EIO);
+}
+
+static void handle_streamstop_req(struct unix_client *client,
+                                       struct bt_stop_stream_req *req)
+{
+       if (!client->dev)
+               goto failed;
+
+       start_suspend(client->dev, client);
+
+       return;
+
+failed:
+       unix_ipc_error(client, BT_STOP_STREAM, EIO);
+}
+
+static void handle_close_req(struct unix_client *client,
+                               struct bt_close_req *req)
+{
+       if (!client->dev)
+               goto failed;
+
+       start_close(client->dev, client, TRUE);
+
+       return;
+
+failed:
+       unix_ipc_error(client, BT_CLOSE, EIO);
+}
+
+static void handle_control_req(struct unix_client *client,
+                                       struct bt_control_req *req)
+{
+       /* FIXME: really implement that */
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_set_configuration_rsp *rsp = (void *) buf;
+
+       memset(buf, 0, sizeof(buf));
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_CONTROL;
+       rsp->h.length = sizeof(*rsp);
+
+       unix_ipc_sendmsg(client, &rsp->h);
+}
+
+static void handle_delay_report_req(struct unix_client *client,
+                                       struct bt_delay_report_req *req)
+{
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       struct bt_set_configuration_rsp *rsp = (void *) buf;
+       struct a2dp_data *a2dp;
+       int err;
+
+       if (!client->dev) {
+               err = -ENODEV;
+               goto failed;
+       }
+
+       switch (client->type) {
+       case TYPE_HEADSET:
+        case TYPE_GATEWAY:
+               err = -EINVAL;
+               goto failed;
+       case TYPE_SOURCE:
+       case TYPE_SINK:
+               a2dp = &client->d.a2dp;
+               if (a2dp->session && a2dp->stream) {
+                       err = avdtp_delay_report(a2dp->session, a2dp->stream,
+                                                               req->delay);
+                       if (err < 0)
+                               goto failed;
+               } else {
+                       err = -EINVAL;
+                       goto failed;
+               }
+               break;
+       default:
+               error("No known services for device");
+               err = -EINVAL;
+               goto failed;
+       }
+
+       memset(buf, 0, sizeof(buf));
+       rsp->h.type = BT_RESPONSE;
+       rsp->h.name = BT_DELAY_REPORT;
+       rsp->h.length = sizeof(*rsp);
+
+       unix_ipc_sendmsg(client, &rsp->h);
+
+       return;
+
+failed:
+       unix_ipc_error(client, BT_DELAY_REPORT, -err);
+}
+
+static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       char buf[BT_SUGGESTED_BUFFER_SIZE];
+       bt_audio_msg_header_t *msghdr = (void *) buf;
+       struct unix_client *client = data;
+       int len;
+       const char *type, *name;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       if (cond & (G_IO_HUP | G_IO_ERR)) {
+               DBG("Unix client disconnected (fd=%d)", client->sock);
+
+               goto failed;
+       }
+
+       memset(buf, 0, sizeof(buf));
+
+       len = recv(client->sock, buf, sizeof(buf), 0);
+       if (len < 0) {
+               error("recv: %s (%d)", strerror(errno), errno);
+               goto failed;
+       }
+
+       type = bt_audio_strtype(msghdr->type);
+       name = bt_audio_strname(msghdr->name);
+
+       DBG("Audio API: %s <- %s", type, name);
+
+       if (msghdr->length != len) {
+               error("Invalid message: length mismatch");
+               goto failed;
+       }
+
+       switch (msghdr->name) {
+       case BT_GET_CAPABILITIES:
+               handle_getcapabilities_req(client,
+                               (struct bt_get_capabilities_req *) msghdr);
+               break;
+       case BT_OPEN:
+               handle_open_req(client,
+                               (struct bt_open_req *) msghdr);
+               break;
+       case BT_SET_CONFIGURATION:
+               handle_setconfiguration_req(client,
+                               (struct bt_set_configuration_req *) msghdr);
+               break;
+       case BT_START_STREAM:
+               handle_streamstart_req(client,
+                               (struct bt_start_stream_req *) msghdr);
+               break;
+       case BT_STOP_STREAM:
+               handle_streamstop_req(client,
+                               (struct bt_stop_stream_req *) msghdr);
+               break;
+       case BT_CLOSE:
+               handle_close_req(client,
+                               (struct bt_close_req *) msghdr);
+               break;
+       case BT_CONTROL:
+               handle_control_req(client,
+                               (struct bt_control_req *) msghdr);
+               break;
+       case BT_DELAY_REPORT:
+               handle_delay_report_req(client,
+                               (struct bt_delay_report_req *) msghdr);
+               break;
+       default:
+               error("Audio API: received unexpected message name %d",
+                               msghdr->name);
+       }
+
+       return TRUE;
+
+failed:
+       clients = g_slist_remove(clients, client);
+       start_close(client->dev, client, FALSE);
+       client_free(client);
+       return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct sockaddr_un addr;
+       socklen_t addrlen;
+       int sk, cli_sk;
+       struct unix_client *client;
+       GIOChannel *io;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       if (cond & (G_IO_HUP | G_IO_ERR)) {
+               g_io_channel_shutdown(chan, TRUE, NULL);
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(chan);
+
+       memset(&addr, 0, sizeof(addr));
+       addrlen = sizeof(addr);
+
+       cli_sk = accept(sk, (struct sockaddr *) &addr, &addrlen);
+       if (cli_sk < 0) {
+               error("accept: %s (%d)", strerror(errno), errno);
+               return TRUE;
+       }
+
+       DBG("Accepted new client connection on unix socket (fd=%d)", cli_sk);
+       set_nonblocking(cli_sk);
+
+       client = g_new0(struct unix_client, 1);
+       client->sock = cli_sk;
+       clients = g_slist_append(clients, client);
+
+       io = g_io_channel_unix_new(cli_sk);
+       g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                                       client_cb, client);
+       g_io_channel_unref(io);
+
+       return TRUE;
+}
+
+void unix_device_removed(struct audio_device *dev)
+{
+       GSList *l;
+
+       DBG("unix_device_removed(%p)", dev);
+
+       l = clients;
+       while (l) {
+               struct unix_client *client = l->data;
+
+               l = l->next;
+
+               if (client->dev == dev) {
+                       clients = g_slist_remove(clients, client);
+                       start_close(client->dev, client, FALSE);
+                       client_free(client);
+               }
+       }
+}
+
+void unix_delay_report(struct audio_device *dev, uint8_t seid, uint16_t delay)
+{
+       GSList *l;
+       struct bt_delay_report_ind ind;
+
+       DBG("unix_delay_report(%p): %u.%ums", dev, delay / 10, delay % 10);
+
+       memset(&ind, 0, sizeof(ind));
+       ind.h.type = BT_INDICATION;
+       ind.h.name = BT_DELAY_REPORT;
+       ind.h.length = sizeof(ind);
+       ind.delay = delay;
+
+       for (l = clients; l != NULL; l = g_slist_next(l)) {
+               struct unix_client *client = l->data;
+
+               if (client->dev != dev || client->seid != seid)
+                       continue;
+
+               unix_ipc_sendmsg(client, (void *) &ind);
+       }
+}
+
+int unix_init(void)
+{
+       GIOChannel *io;
+       struct sockaddr_un addr = {
+               AF_UNIX, BT_IPC_SOCKET_NAME
+       };
+
+       int sk, err;
+
+       sk = socket(PF_LOCAL, SOCK_STREAM, 0);
+       if (sk < 0) {
+               err = -errno;
+               error("Can't create unix socket: %s (%d)", strerror(-err),
+                                                                       -err);
+               return err;
+       }
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               err = -errno;
+               error("Can't bind unix socket: %s (%d)", strerror(-err),
+                                                                       -err);
+               close(sk);
+               return err;
+       }
+
+       set_nonblocking(sk);
+
+       if (listen(sk, 1) < 0) {
+               err = -errno;
+               error("Can't listen on unix socket: %s (%d)", strerror(-err),
+                                                                       -err);
+               close(sk);
+               return err;
+       }
+
+       unix_sock = sk;
+
+       io = g_io_channel_unix_new(sk);
+       g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                                       server_cb, NULL);
+       g_io_channel_unref(io);
+
+       DBG("Unix socket created: %d", sk);
+
+       return 0;
+}
+
+void unix_exit(void)
+{
+       g_slist_free_full(clients, client_free);
+       if (unix_sock >= 0) {
+               close(unix_sock);
+               unix_sock = -1;
+       }
+}
diff --git a/audio/unix.h b/audio/unix.h
new file mode 100644 (file)
index 0000000..74ca16d
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void unix_device_removed(struct audio_device *dev);
+
+void unix_delay_report(struct audio_device *dev, uint8_t seid, uint16_t delay);
+
+int unix_init(void);
+void unix_exit(void);
diff --git a/bluez.pc.in b/bluez.pc.in
new file mode 100644 (file)
index 0000000..3d6e596
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+Name: BlueZ
+Description: Bluetooth protocol stack for Linux
+Version: @VERSION@
+Libs: -L${libdir} -lbluetooth
+Cflags: -I${includedir}
diff --git a/btio/btio.c b/btio/btio.c
new file mode 100644 (file)
index 0000000..e81fb75
--- /dev/null
@@ -0,0 +1,1460 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sco.h>
+
+#include <glib.h>
+
+#include "btio.h"
+
+#ifndef BT_FLUSHABLE
+#define BT_FLUSHABLE   8
+#endif
+
+#define ERROR_FAILED(gerr, str, err) \
+               g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \
+                               str ": %s (%d)", strerror(err), err)
+
+#define DEFAULT_DEFER_TIMEOUT 30
+
+struct set_opts {
+       bdaddr_t src;
+       bdaddr_t dst;
+       uint8_t dst_type;
+       int defer;
+       int sec_level;
+       uint8_t channel;
+       uint16_t psm;
+       uint16_t cid;
+       uint16_t mtu;
+       uint16_t imtu;
+       uint16_t omtu;
+       int master;
+       uint8_t mode;
+       int flushable;
+       uint32_t priority;
+};
+
+struct connect {
+       BtIOConnect connect;
+       gpointer user_data;
+       GDestroyNotify destroy;
+};
+
+struct accept {
+       BtIOConnect connect;
+       gpointer user_data;
+       GDestroyNotify destroy;
+};
+
+struct server {
+       BtIOConnect connect;
+       BtIOConfirm confirm;
+       gpointer user_data;
+       GDestroyNotify destroy;
+};
+
+static void server_remove(struct server *server)
+{
+       if (server->destroy)
+               server->destroy(server->user_data);
+       g_free(server);
+}
+
+static void connect_remove(struct connect *conn)
+{
+       if (conn->destroy)
+               conn->destroy(conn->user_data);
+       g_free(conn);
+}
+
+static void accept_remove(struct accept *accept)
+{
+       if (accept->destroy)
+               accept->destroy(accept->user_data);
+       g_free(accept);
+}
+
+static gboolean check_nval(GIOChannel *io)
+{
+       struct pollfd fds;
+
+       memset(&fds, 0, sizeof(fds));
+       fds.fd = g_io_channel_unix_get_fd(io);
+       fds.events = POLLNVAL;
+
+       if (poll(&fds, 1, 0) > 0 && (fds.revents & POLLNVAL))
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean accept_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct accept *accept = user_data;
+       GError *err = NULL;
+
+       /* If the user aborted this accept attempt */
+       if ((cond & G_IO_NVAL) || check_nval(io))
+               return FALSE;
+
+       if (cond & (G_IO_HUP | G_IO_ERR))
+               g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED,
+                               "HUP or ERR on socket");
+
+       accept->connect(io, err, accept->user_data);
+
+       g_clear_error(&err);
+
+       return FALSE;
+}
+
+static gboolean connect_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct connect *conn = user_data;
+       GError *gerr = NULL;
+
+       /* If the user aborted this connect attempt */
+       if ((cond & G_IO_NVAL) || check_nval(io))
+               return FALSE;
+
+       if (cond & G_IO_OUT) {
+               int err, sk_err = 0, sock = g_io_channel_unix_get_fd(io);
+               socklen_t len = sizeof(sk_err);
+
+               if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+                       err = -errno;
+               else
+                       err = -sk_err;
+
+               if (err < 0)
+                       g_set_error(&gerr, BT_IO_ERROR,
+                                       BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
+                                       strerror(-err), -err);
+       } else if (cond & (G_IO_HUP | G_IO_ERR))
+               g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+                               "HUP or ERR on socket");
+
+       conn->connect(io, gerr, conn->user_data);
+
+       if (gerr)
+               g_error_free(gerr);
+
+       return FALSE;
+}
+
+static gboolean server_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct server *server = user_data;
+       int srv_sock, cli_sock;
+       GIOChannel *cli_io;
+
+       /* If the user closed the server */
+       if ((cond & G_IO_NVAL) || check_nval(io))
+               return FALSE;
+
+       srv_sock = g_io_channel_unix_get_fd(io);
+
+       cli_sock = accept(srv_sock, NULL, NULL);
+       if (cli_sock < 0)
+               return TRUE;
+
+       cli_io = g_io_channel_unix_new(cli_sock);
+
+       g_io_channel_set_close_on_unref(cli_io, TRUE);
+       g_io_channel_set_flags(cli_io, G_IO_FLAG_NONBLOCK, NULL);
+
+       if (server->confirm)
+               server->confirm(cli_io, server->user_data);
+       else
+               server->connect(cli_io, NULL, server->user_data);
+
+       g_io_channel_unref(cli_io);
+
+       return TRUE;
+}
+
+static void server_add(GIOChannel *io, BtIOConnect connect,
+                               BtIOConfirm confirm, gpointer user_data,
+                               GDestroyNotify destroy)
+{
+       struct server *server;
+       GIOCondition cond;
+
+       server = g_new0(struct server, 1);
+       server->connect = connect;
+       server->confirm = confirm;
+       server->user_data = user_data;
+       server->destroy = destroy;
+
+       cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+       g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, server_cb, server,
+                                       (GDestroyNotify) server_remove);
+}
+
+static void connect_add(GIOChannel *io, BtIOConnect connect,
+                               gpointer user_data, GDestroyNotify destroy)
+{
+       struct connect *conn;
+       GIOCondition cond;
+
+       conn = g_new0(struct connect, 1);
+       conn->connect = connect;
+       conn->user_data = user_data;
+       conn->destroy = destroy;
+
+       cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+       g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb, conn,
+                                       (GDestroyNotify) connect_remove);
+}
+
+static void accept_add(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+                                                       GDestroyNotify destroy)
+{
+       struct accept *accept;
+       GIOCondition cond;
+
+       accept = g_new0(struct accept, 1);
+       accept->connect = connect;
+       accept->user_data = user_data;
+       accept->destroy = destroy;
+
+       cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+       g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, accept_cb, accept,
+                                       (GDestroyNotify) accept_remove);
+}
+
+static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm,
+                                               uint16_t cid, GError **err)
+{
+       struct sockaddr_l2 addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, src);
+
+       if (cid)
+               addr.l2_cid = htobs(cid);
+       else
+               addr.l2_psm = htobs(psm);
+
+       if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               int error = -errno;
+               ERROR_FAILED(err, "l2cap_bind", errno);
+               return error;
+       }
+
+       return 0;
+}
+
+static int l2cap_connect(int sock, const bdaddr_t *dst, uint8_t dst_type,
+                                               uint16_t psm, uint16_t cid)
+{
+       int err;
+       struct sockaddr_l2 addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, dst);
+       if (cid)
+               addr.l2_cid = htobs(cid);
+       else
+               addr.l2_psm = htobs(psm);
+
+       addr.l2_bdaddr_type = dst_type;
+
+       err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+               return -errno;
+
+       return 0;
+}
+
+static int l2cap_set_master(int sock, int master)
+{
+       int flags;
+       socklen_t len;
+
+       len = sizeof(flags);
+       if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len) < 0)
+               return -errno;
+
+       if (master) {
+               if (flags & L2CAP_LM_MASTER)
+                       return 0;
+               flags |= L2CAP_LM_MASTER;
+       } else {
+               if (!(flags & L2CAP_LM_MASTER))
+                       return 0;
+               flags &= ~L2CAP_LM_MASTER;
+       }
+
+       if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, sizeof(flags)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int rfcomm_set_master(int sock, int master)
+{
+       int flags;
+       socklen_t len;
+
+       len = sizeof(flags);
+       if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, &len) < 0)
+               return -errno;
+
+       if (master) {
+               if (flags & RFCOMM_LM_MASTER)
+                       return 0;
+               flags |= RFCOMM_LM_MASTER;
+       } else {
+               if (!(flags & RFCOMM_LM_MASTER))
+                       return 0;
+               flags &= ~RFCOMM_LM_MASTER;
+       }
+
+       if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags, sizeof(flags)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int l2cap_set_lm(int sock, int level)
+{
+       int lm_map[] = {
+               0,
+               L2CAP_LM_AUTH,
+               L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT,
+               L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE,
+       }, opt = lm_map[level];
+
+       if (setsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int rfcomm_set_lm(int sock, int level)
+{
+       int lm_map[] = {
+               0,
+               RFCOMM_LM_AUTH,
+               RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT,
+               RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE,
+       }, opt = lm_map[level];
+
+       if (setsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static gboolean set_sec_level(int sock, BtIOType type, int level, GError **err)
+{
+       struct bt_security sec;
+       int ret;
+
+       if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) {
+               g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                               "Valid security level range is %d-%d",
+                               BT_SECURITY_LOW, BT_SECURITY_HIGH);
+               return FALSE;
+       }
+
+       memset(&sec, 0, sizeof(sec));
+       sec.level = level;
+
+       if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec,
+                                                       sizeof(sec)) == 0)
+               return TRUE;
+
+       if (errno != ENOPROTOOPT) {
+               ERROR_FAILED(err, "setsockopt(BT_SECURITY)", errno);
+               return FALSE;
+       }
+
+       if (type == BT_IO_L2CAP)
+               ret = l2cap_set_lm(sock, level);
+       else
+               ret = rfcomm_set_lm(sock, level);
+
+       if (ret < 0) {
+               ERROR_FAILED(err, "setsockopt(LM)", -ret);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int l2cap_get_lm(int sock, int *sec_level)
+{
+       int opt;
+       socklen_t len;
+
+       len = sizeof(opt);
+       if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &opt, &len) < 0)
+               return -errno;
+
+       *sec_level = 0;
+
+       if (opt & L2CAP_LM_AUTH)
+               *sec_level = BT_SECURITY_LOW;
+       if (opt & L2CAP_LM_ENCRYPT)
+               *sec_level = BT_SECURITY_MEDIUM;
+       if (opt & L2CAP_LM_SECURE)
+               *sec_level = BT_SECURITY_HIGH;
+
+       return 0;
+}
+
+static int rfcomm_get_lm(int sock, int *sec_level)
+{
+       int opt;
+       socklen_t len;
+
+       len = sizeof(opt);
+       if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &opt, &len) < 0)
+               return -errno;
+
+       *sec_level = 0;
+
+       if (opt & RFCOMM_LM_AUTH)
+               *sec_level = BT_SECURITY_LOW;
+       if (opt & RFCOMM_LM_ENCRYPT)
+               *sec_level = BT_SECURITY_MEDIUM;
+       if (opt & RFCOMM_LM_SECURE)
+               *sec_level = BT_SECURITY_HIGH;
+
+       return 0;
+}
+
+static gboolean get_sec_level(int sock, BtIOType type, int *level,
+                                                               GError **err)
+{
+       struct bt_security sec;
+       socklen_t len;
+       int ret;
+
+       memset(&sec, 0, sizeof(sec));
+       len = sizeof(sec);
+       if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+               *level = sec.level;
+               return TRUE;
+       }
+
+       if (errno != ENOPROTOOPT) {
+               ERROR_FAILED(err, "getsockopt(BT_SECURITY)", errno);
+               return FALSE;
+       }
+
+       if (type == BT_IO_L2CAP)
+               ret = l2cap_get_lm(sock, level);
+       else
+               ret = rfcomm_get_lm(sock, level);
+
+       if (ret < 0) {
+               ERROR_FAILED(err, "getsockopt(LM)", -ret);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int l2cap_set_flushable(int sock, gboolean flushable)
+{
+       int f;
+
+       f = flushable;
+       if (setsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, sizeof(f)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int set_priority(int sock, uint32_t prio)
+{
+       if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static gboolean get_key_size(int sock, int *size, GError **err)
+{
+       struct bt_security sec;
+       socklen_t len;
+
+       memset(&sec, 0, sizeof(sec));
+       len = sizeof(sec);
+       if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+               *size = sec.key_size;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu,
+                               uint16_t omtu, uint8_t mode, int master,
+                               int flushable, uint32_t priority, GError **err)
+{
+       if (imtu || omtu || mode) {
+               struct l2cap_options l2o;
+               socklen_t len;
+
+               memset(&l2o, 0, sizeof(l2o));
+               len = sizeof(l2o);
+               if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+                                                               &len) < 0) {
+                       ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+                       return FALSE;
+               }
+
+               if (imtu)
+                       l2o.imtu = imtu;
+               if (omtu)
+                       l2o.omtu = omtu;
+               if (mode)
+                       l2o.mode = mode;
+
+               if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o,
+                                                       sizeof(l2o)) < 0) {
+                       ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+                       return FALSE;
+               }
+       }
+
+       if (master >= 0 && l2cap_set_master(sock, master) < 0) {
+               ERROR_FAILED(err, "l2cap_set_master", errno);
+               return FALSE;
+       }
+
+       if (flushable >= 0 && l2cap_set_flushable(sock, flushable) < 0) {
+               ERROR_FAILED(err, "l2cap_set_flushable", errno);
+               return FALSE;
+       }
+
+       if (priority > 0 && set_priority(sock, priority) < 0) {
+               ERROR_FAILED(err, "set_priority", errno);
+               return FALSE;
+       }
+
+       if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
+               return FALSE;
+
+       return TRUE;
+}
+
+static int rfcomm_bind(int sock,
+               const bdaddr_t *src, uint8_t channel, GError **err)
+{
+       struct sockaddr_rc addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, src);
+       addr.rc_channel = channel;
+
+       if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               int error = -errno;
+               ERROR_FAILED(err, "rfcomm_bind", errno);
+               return error;
+       }
+
+       return 0;
+}
+
+static int rfcomm_connect(int sock, const bdaddr_t *dst, uint8_t channel)
+{
+       int err;
+       struct sockaddr_rc addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, dst);
+       addr.rc_channel = channel;
+
+       err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+               return -errno;
+
+       return 0;
+}
+
+static gboolean rfcomm_set(int sock, int sec_level, int master, GError **err)
+{
+       if (sec_level && !set_sec_level(sock, BT_IO_RFCOMM, sec_level, err))
+               return FALSE;
+
+       if (master >= 0 && rfcomm_set_master(sock, master) < 0) {
+               ERROR_FAILED(err, "rfcomm_set_master", errno);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int sco_bind(int sock, const bdaddr_t *src, GError **err)
+{
+       struct sockaddr_sco addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sco_family = AF_BLUETOOTH;
+       bacpy(&addr.sco_bdaddr, src);
+
+       if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               int error = -errno;
+               ERROR_FAILED(err, "sco_bind", errno);
+               return error;
+       }
+
+       return 0;
+}
+
+static int sco_connect(int sock, const bdaddr_t *dst)
+{
+       struct sockaddr_sco addr;
+       int err;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sco_family = AF_BLUETOOTH;
+       bacpy(&addr.sco_bdaddr, dst);
+
+       err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+               return -errno;
+
+       return 0;
+}
+
+static gboolean sco_set(int sock, uint16_t mtu, GError **err)
+{
+       struct sco_options sco_opt;
+       socklen_t len;
+
+       if (!mtu)
+               return TRUE;
+
+       len = sizeof(sco_opt);
+       memset(&sco_opt, 0, len);
+       if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+               ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+               return FALSE;
+       }
+
+       sco_opt.mtu = mtu;
+       if (setsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt,
+                                               sizeof(sco_opt)) < 0) {
+               ERROR_FAILED(err, "setsockopt(SCO_OPTIONS)", errno);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean parse_set_opts(struct set_opts *opts, GError **err,
+                                               BtIOOption opt1, va_list args)
+{
+       BtIOOption opt = opt1;
+       const char *str;
+
+       memset(opts, 0, sizeof(*opts));
+
+       /* Set defaults */
+       opts->defer = DEFAULT_DEFER_TIMEOUT;
+       opts->master = -1;
+       opts->mode = L2CAP_MODE_BASIC;
+       opts->flushable = -1;
+       opts->priority = 0;
+       opts->dst_type = BDADDR_BREDR;
+
+       while (opt != BT_IO_OPT_INVALID) {
+               switch (opt) {
+               case BT_IO_OPT_SOURCE:
+                       str = va_arg(args, const char *);
+                       str2ba(str, &opts->src);
+                       break;
+               case BT_IO_OPT_SOURCE_BDADDR:
+                       bacpy(&opts->src, va_arg(args, const bdaddr_t *));
+                       break;
+               case BT_IO_OPT_DEST:
+                       str2ba(va_arg(args, const char *), &opts->dst);
+                       break;
+               case BT_IO_OPT_DEST_BDADDR:
+                       bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
+                       break;
+               case BT_IO_OPT_DEST_TYPE:
+                       opts->dst_type = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_DEFER_TIMEOUT:
+                       opts->defer = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_SEC_LEVEL:
+                       opts->sec_level = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_CHANNEL:
+                       opts->channel = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_PSM:
+                       opts->psm = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_CID:
+                       opts->cid = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_MTU:
+                       opts->mtu = va_arg(args, int);
+                       opts->imtu = opts->mtu;
+                       opts->omtu = opts->mtu;
+                       break;
+               case BT_IO_OPT_OMTU:
+                       opts->omtu = va_arg(args, int);
+                       if (!opts->mtu)
+                               opts->mtu = opts->omtu;
+                       break;
+               case BT_IO_OPT_IMTU:
+                       opts->imtu = va_arg(args, int);
+                       if (!opts->mtu)
+                               opts->mtu = opts->imtu;
+                       break;
+               case BT_IO_OPT_MASTER:
+                       opts->master = va_arg(args, gboolean);
+                       break;
+               case BT_IO_OPT_MODE:
+                       opts->mode = va_arg(args, int);
+                       break;
+               case BT_IO_OPT_FLUSHABLE:
+                       opts->flushable = va_arg(args, gboolean);
+                       break;
+               case BT_IO_OPT_PRIORITY:
+                       opts->priority = va_arg(args, int);
+                       break;
+               default:
+                       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                       "Unknown option %d", opt);
+                       return FALSE;
+               }
+
+               opt = va_arg(args, int);
+       }
+
+       return TRUE;
+}
+
+static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
+                               socklen_t len, GError **err)
+{
+       socklen_t olen;
+
+       memset(src, 0, len);
+       olen = len;
+       if (getsockname(sock, src, &olen) < 0) {
+               ERROR_FAILED(err, "getsockname", errno);
+               return FALSE;
+       }
+
+       memset(dst, 0, len);
+       olen = len;
+       if (getpeername(sock, dst, &olen) < 0) {
+               ERROR_FAILED(err, "getpeername", errno);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int l2cap_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+       struct l2cap_conninfo info;
+       socklen_t len;
+
+       len = sizeof(info);
+       if (getsockopt(sock, SOL_L2CAP, L2CAP_CONNINFO, &info, &len) < 0)
+               return -errno;
+
+       if (handle)
+               *handle = info.hci_handle;
+
+       if (dev_class)
+               memcpy(dev_class, info.dev_class, 3);
+
+       return 0;
+}
+
+static int l2cap_get_flushable(int sock, gboolean *flushable)
+{
+       int f;
+       socklen_t len;
+
+       f = 0;
+       len = sizeof(f);
+       if (getsockopt(sock, SOL_BLUETOOTH, BT_FLUSHABLE, &f, &len) < 0)
+               return -errno;
+
+       if (f)
+               *flushable = TRUE;
+       else
+               *flushable = FALSE;
+
+       return 0;
+}
+
+static int get_priority(int sock, uint32_t *prio)
+{
+       socklen_t len;
+
+       len = sizeof(*prio);
+       if (getsockopt(sock, SOL_SOCKET, SO_PRIORITY, prio, &len) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
+                                                               va_list args)
+{
+       BtIOOption opt = opt1;
+       struct sockaddr_l2 src, dst;
+       struct l2cap_options l2o;
+       int flags;
+       uint8_t dev_class[3];
+       uint16_t handle;
+       socklen_t len;
+       gboolean flushable = FALSE;
+       uint32_t priority;
+
+       len = sizeof(l2o);
+       memset(&l2o, 0, len);
+       if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+               ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno);
+               return FALSE;
+       }
+
+       if (!get_peers(sock, (struct sockaddr *) &src,
+                               (struct sockaddr *) &dst, sizeof(src), err))
+               return FALSE;
+
+       while (opt != BT_IO_OPT_INVALID) {
+               switch (opt) {
+               case BT_IO_OPT_SOURCE:
+                       ba2str(&src.l2_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_SOURCE_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
+                       break;
+               case BT_IO_OPT_DEST:
+                       ba2str(&dst.l2_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_DEST_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
+                       break;
+               case BT_IO_OPT_DEST_TYPE:
+                       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                                       "Not implemented");
+                       return FALSE;
+               case BT_IO_OPT_DEFER_TIMEOUT:
+                       len = sizeof(int);
+                       if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+                                       va_arg(args, int *), &len) < 0) {
+                               ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+                                                                       errno);
+                               return FALSE;
+                       }
+                       break;
+               case BT_IO_OPT_SEC_LEVEL:
+                       if (!get_sec_level(sock, BT_IO_L2CAP,
+                                               va_arg(args, int *), err))
+                               return FALSE;
+                       break;
+               case BT_IO_OPT_KEY_SIZE:
+                       if (!get_key_size(sock, va_arg(args, int *), err))
+                               return FALSE;
+                       break;
+               case BT_IO_OPT_PSM:
+                       *(va_arg(args, uint16_t *)) = src.l2_psm ?
+                                       btohs(src.l2_psm) : btohs(dst.l2_psm);
+                       break;
+               case BT_IO_OPT_CID:
+                       *(va_arg(args, uint16_t *)) = src.l2_cid ?
+                                       btohs(src.l2_cid) : btohs(dst.l2_cid);
+                       break;
+               case BT_IO_OPT_OMTU:
+                       *(va_arg(args, uint16_t *)) = l2o.omtu;
+                       break;
+               case BT_IO_OPT_IMTU:
+                       *(va_arg(args, uint16_t *)) = l2o.imtu;
+                       break;
+               case BT_IO_OPT_MASTER:
+                       len = sizeof(flags);
+                       if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags,
+                                                               &len) < 0) {
+                               ERROR_FAILED(err, "getsockopt(L2CAP_LM)",
+                                                                       errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, gboolean *)) =
+                               (flags & L2CAP_LM_MASTER) ? TRUE : FALSE;
+                       break;
+               case BT_IO_OPT_HANDLE:
+                       if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, uint16_t *)) = handle;
+                       break;
+               case BT_IO_OPT_CLASS:
+                       if (l2cap_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "L2CAP_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       memcpy(va_arg(args, uint8_t *), dev_class, 3);
+                       break;
+               case BT_IO_OPT_MODE:
+                       *(va_arg(args, uint8_t *)) = l2o.mode;
+                       break;
+               case BT_IO_OPT_FLUSHABLE:
+                       if (l2cap_get_flushable(sock, &flushable) < 0) {
+                               ERROR_FAILED(err, "get_flushable", errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, gboolean *)) = flushable;
+                       break;
+               case BT_IO_OPT_PRIORITY:
+                       if (get_priority(sock, &priority) < 0) {
+                               ERROR_FAILED(err, "get_priority", errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, uint32_t *)) = priority;
+                       break;
+               default:
+                       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                       "Unknown option %d", opt);
+                       return FALSE;
+               }
+
+               opt = va_arg(args, int);
+       }
+
+       return TRUE;
+}
+
+static int rfcomm_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+       struct rfcomm_conninfo info;
+       socklen_t len;
+
+       len = sizeof(info);
+       if (getsockopt(sock, SOL_RFCOMM, RFCOMM_CONNINFO, &info, &len) < 0)
+               return -errno;
+
+       if (handle)
+               *handle = info.hci_handle;
+
+       if (dev_class)
+               memcpy(dev_class, info.dev_class, 3);
+
+       return 0;
+}
+
+static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
+                                                               va_list args)
+{
+       BtIOOption opt = opt1;
+       struct sockaddr_rc src, dst;
+       int flags;
+       socklen_t len;
+       uint8_t dev_class[3];
+       uint16_t handle;
+
+       if (!get_peers(sock, (struct sockaddr *) &src,
+                               (struct sockaddr *) &dst, sizeof(src), err))
+               return FALSE;
+
+       while (opt != BT_IO_OPT_INVALID) {
+               switch (opt) {
+               case BT_IO_OPT_SOURCE:
+                       ba2str(&src.rc_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_SOURCE_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
+                       break;
+               case BT_IO_OPT_DEST:
+                       ba2str(&dst.rc_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_DEST_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
+                       break;
+               case BT_IO_OPT_DEFER_TIMEOUT:
+                       len = sizeof(int);
+                       if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
+                                       va_arg(args, int *), &len) < 0) {
+                               ERROR_FAILED(err, "getsockopt(DEFER_SETUP)",
+                                                                       errno);
+                               return FALSE;
+                       }
+                       break;
+               case BT_IO_OPT_SEC_LEVEL:
+                       if (!get_sec_level(sock, BT_IO_RFCOMM,
+                                               va_arg(args, int *), err))
+                               return FALSE;
+                       break;
+               case BT_IO_OPT_CHANNEL:
+                       *(va_arg(args, uint8_t *)) = src.rc_channel ?
+                                       src.rc_channel : dst.rc_channel;
+                       break;
+               case BT_IO_OPT_SOURCE_CHANNEL:
+                       *(va_arg(args, uint8_t *)) = src.rc_channel;
+                       break;
+               case BT_IO_OPT_DEST_CHANNEL:
+                       *(va_arg(args, uint8_t *)) = dst.rc_channel;
+                       break;
+               case BT_IO_OPT_MASTER:
+                       len = sizeof(flags);
+                       if (getsockopt(sock, SOL_RFCOMM, RFCOMM_LM, &flags,
+                                                               &len) < 0) {
+                               ERROR_FAILED(err, "getsockopt(RFCOMM_LM)",
+                                                                       errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, gboolean *)) =
+                               (flags & RFCOMM_LM_MASTER) ? TRUE : FALSE;
+                       break;
+               case BT_IO_OPT_HANDLE:
+                       if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, uint16_t *)) = handle;
+                       break;
+               case BT_IO_OPT_CLASS:
+                       if (rfcomm_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "RFCOMM_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       memcpy(va_arg(args, uint8_t *), dev_class, 3);
+                       break;
+               default:
+                       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                       "Unknown option %d", opt);
+                       return FALSE;
+               }
+
+               opt = va_arg(args, int);
+       }
+
+       return TRUE;
+}
+
+static int sco_get_info(int sock, uint16_t *handle, uint8_t *dev_class)
+{
+       struct sco_conninfo info;
+       socklen_t len;
+
+       len = sizeof(info);
+       if (getsockopt(sock, SOL_SCO, SCO_CONNINFO, &info, &len) < 0)
+               return -errno;
+
+       if (handle)
+               *handle = info.hci_handle;
+
+       if (dev_class)
+               memcpy(dev_class, info.dev_class, 3);
+
+       return 0;
+}
+
+static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
+{
+       BtIOOption opt = opt1;
+       struct sockaddr_sco src, dst;
+       struct sco_options sco_opt;
+       socklen_t len;
+       uint8_t dev_class[3];
+       uint16_t handle;
+
+       len = sizeof(sco_opt);
+       memset(&sco_opt, 0, len);
+       if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0) {
+               ERROR_FAILED(err, "getsockopt(SCO_OPTIONS)", errno);
+               return FALSE;
+       }
+
+       if (!get_peers(sock, (struct sockaddr *) &src,
+                               (struct sockaddr *) &dst, sizeof(src), err))
+               return FALSE;
+
+       while (opt != BT_IO_OPT_INVALID) {
+               switch (opt) {
+               case BT_IO_OPT_SOURCE:
+                       ba2str(&src.sco_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_SOURCE_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &src.sco_bdaddr);
+                       break;
+               case BT_IO_OPT_DEST:
+                       ba2str(&dst.sco_bdaddr, va_arg(args, char *));
+                       break;
+               case BT_IO_OPT_DEST_BDADDR:
+                       bacpy(va_arg(args, bdaddr_t *), &dst.sco_bdaddr);
+                       break;
+               case BT_IO_OPT_MTU:
+               case BT_IO_OPT_IMTU:
+               case BT_IO_OPT_OMTU:
+                       *(va_arg(args, uint16_t *)) = sco_opt.mtu;
+                       break;
+               case BT_IO_OPT_HANDLE:
+                       if (sco_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "SCO_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       *(va_arg(args, uint16_t *)) = handle;
+                       break;
+               case BT_IO_OPT_CLASS:
+                       if (sco_get_info(sock, &handle, dev_class) < 0) {
+                               ERROR_FAILED(err, "SCO_CONNINFO", errno);
+                               return FALSE;
+                       }
+                       memcpy(va_arg(args, uint8_t *), dev_class, 3);
+                       break;
+               default:
+                       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                       "Unknown option %d", opt);
+                       return FALSE;
+               }
+
+               opt = va_arg(args, int);
+       }
+
+       return TRUE;
+}
+
+static gboolean get_valist(GIOChannel *io, BtIOType type, GError **err,
+                                               BtIOOption opt1, va_list args)
+{
+       int sock;
+
+       sock = g_io_channel_unix_get_fd(io);
+
+       switch (type) {
+       case BT_IO_L2RAW:
+       case BT_IO_L2CAP:
+       case BT_IO_L2ERTM:
+               return l2cap_get(sock, err, opt1, args);
+       case BT_IO_RFCOMM:
+               return rfcomm_get(sock, err, opt1, args);
+       case BT_IO_SCO:
+               return sco_get(sock, err, opt1, args);
+       }
+
+       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                       "Unknown BtIO type %d", type);
+       return FALSE;
+}
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+                                       GDestroyNotify destroy, GError **err)
+{
+       int sock;
+       char c;
+       struct pollfd pfd;
+
+       sock = g_io_channel_unix_get_fd(io);
+
+       memset(&pfd, 0, sizeof(pfd));
+       pfd.fd = sock;
+       pfd.events = POLLOUT;
+
+       if (poll(&pfd, 1, 0) < 0) {
+               ERROR_FAILED(err, "poll", errno);
+               return FALSE;
+       }
+
+       if (!(pfd.revents & POLLOUT)) {
+               if (read(sock, &c, 1) < 0) {
+                       ERROR_FAILED(err, "read", errno);
+                       return FALSE;
+               }
+       }
+
+       accept_add(io, connect, user_data, destroy);
+
+       return TRUE;
+}
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+                                                       BtIOOption opt1, ...)
+{
+       va_list args;
+       gboolean ret;
+       struct set_opts opts;
+       int sock;
+
+       va_start(args, opt1);
+       ret = parse_set_opts(&opts, err, opt1, args);
+       va_end(args);
+
+       if (!ret)
+               return ret;
+
+       sock = g_io_channel_unix_get_fd(io);
+
+       switch (type) {
+       case BT_IO_L2RAW:
+       case BT_IO_L2CAP:
+       case BT_IO_L2ERTM:
+               return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
+                               opts.mode, opts.master, opts.flushable,
+                               opts.priority, err);
+       case BT_IO_RFCOMM:
+               return rfcomm_set(sock, opts.sec_level, opts.master, err);
+       case BT_IO_SCO:
+               return sco_set(sock, opts.mtu, err);
+       }
+
+       g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                       "Unknown BtIO type %d", type);
+       return FALSE;
+}
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+                                                       BtIOOption opt1, ...)
+{
+       va_list args;
+       gboolean ret;
+
+       va_start(args, opt1);
+       ret = get_valist(io, type, err, opt1, args);
+       va_end(args);
+
+       return ret;
+}
+
+static GIOChannel *create_io(BtIOType type, gboolean server,
+                                       struct set_opts *opts, GError **err)
+{
+       int sock;
+       GIOChannel *io;
+
+       switch (type) {
+       case BT_IO_L2RAW:
+               sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+               if (sock < 0) {
+                       ERROR_FAILED(err, "socket(RAW, L2CAP)", errno);
+                       return NULL;
+               }
+               if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
+                                                       opts->cid, err) < 0)
+                       goto failed;
+               if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, -1, 0, err))
+                       goto failed;
+               break;
+       case BT_IO_L2CAP:
+               sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+               if (sock < 0) {
+                       ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno);
+                       return NULL;
+               }
+               if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
+                                                       opts->cid, err) < 0)
+                       goto failed;
+               if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+                               opts->mode, opts->master, opts->flushable,
+                               opts->priority, err))
+                       goto failed;
+               break;
+       case BT_IO_L2ERTM:
+               sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_L2CAP);
+               if (sock < 0) {
+                       ERROR_FAILED(err, "socket(STREAM, L2CAP)", errno);
+                       return NULL;
+               }
+               if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
+                                                       opts->cid, err) < 0)
+                       goto failed;
+               if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+                               opts->mode, opts->master, opts->flushable,
+                               opts->priority, err))
+                       goto failed;
+               break;
+       case BT_IO_RFCOMM:
+               sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+               if (sock < 0) {
+                       ERROR_FAILED(err, "socket(STREAM, RFCOMM)", errno);
+                       return NULL;
+               }
+               if (rfcomm_bind(sock, &opts->src,
+                                       server ? opts->channel : 0, err) < 0)
+                       goto failed;
+               if (!rfcomm_set(sock, opts->sec_level, opts->master, err))
+                       goto failed;
+               break;
+       case BT_IO_SCO:
+               sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+               if (sock < 0) {
+                       ERROR_FAILED(err, "socket(SEQPACKET, SCO)", errno);
+                       return NULL;
+               }
+               if (sco_bind(sock, &opts->src, err) < 0)
+                       goto failed;
+               if (!sco_set(sock, opts->mtu, err))
+                       goto failed;
+               break;
+       default:
+               g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                               "Unknown BtIO type %d", type);
+               return NULL;
+       }
+
+       io = g_io_channel_unix_new(sock);
+
+       g_io_channel_set_close_on_unref(io, TRUE);
+       g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+       return io;
+
+failed:
+       close(sock);
+
+       return NULL;
+}
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+                               gpointer user_data, GDestroyNotify destroy,
+                               GError **gerr, BtIOOption opt1, ...)
+{
+       GIOChannel *io;
+       va_list args;
+       struct set_opts opts;
+       int err, sock;
+       gboolean ret;
+
+       va_start(args, opt1);
+       ret = parse_set_opts(&opts, gerr, opt1, args);
+       va_end(args);
+
+       if (ret == FALSE)
+               return NULL;
+
+       io = create_io(type, FALSE, &opts, gerr);
+       if (io == NULL)
+               return NULL;
+
+       sock = g_io_channel_unix_get_fd(io);
+
+       switch (type) {
+       case BT_IO_L2RAW:
+               err = l2cap_connect(sock, &opts.dst, opts.dst_type, 0,
+                                                               opts.cid);
+               break;
+       case BT_IO_L2CAP:
+       case BT_IO_L2ERTM:
+               err = l2cap_connect(sock, &opts.dst, opts.dst_type,
+                                                       opts.psm, opts.cid);
+               break;
+       case BT_IO_RFCOMM:
+               err = rfcomm_connect(sock, &opts.dst, opts.channel);
+               break;
+       case BT_IO_SCO:
+               err = sco_connect(sock, &opts.dst);
+               break;
+       default:
+               g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                                               "Unknown BtIO type %d", type);
+               return NULL;
+       }
+
+       if (err < 0) {
+               g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
+                               "connect: %s (%d)", strerror(-err), -err);
+               g_io_channel_unref(io);
+               return NULL;
+       }
+
+       connect_add(io, connect, user_data, destroy);
+
+       return io;
+}
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+                               BtIOConfirm confirm, gpointer user_data,
+                               GDestroyNotify destroy, GError **err,
+                               BtIOOption opt1, ...)
+{
+       GIOChannel *io;
+       va_list args;
+       struct set_opts opts;
+       int sock;
+       gboolean ret;
+
+       if (type == BT_IO_L2RAW) {
+               g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+                               "Server L2CAP RAW sockets not supported");
+               return NULL;
+       }
+
+       va_start(args, opt1);
+       ret = parse_set_opts(&opts, err, opt1, args);
+       va_end(args);
+
+       if (ret == FALSE)
+               return NULL;
+
+       io = create_io(type, TRUE, &opts, err);
+       if (io == NULL)
+               return NULL;
+
+       sock = g_io_channel_unix_get_fd(io);
+
+       if (confirm)
+               setsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, &opts.defer,
+                                                       sizeof(opts.defer));
+
+       if (listen(sock, 5) < 0) {
+               ERROR_FAILED(err, "listen", errno);
+               g_io_channel_unref(io);
+               return NULL;
+       }
+
+       server_add(io, connect, confirm, user_data, destroy);
+
+       return io;
+}
+
+GQuark bt_io_error_quark(void)
+{
+       return g_quark_from_static_string("bt-io-error-quark");
+}
diff --git a/btio/btio.h b/btio/btio.h
new file mode 100644 (file)
index 0000000..cf0e070
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef BT_IO_H
+#define BT_IO_H
+
+#include <glib.h>
+
+typedef enum {
+       BT_IO_ERROR_DISCONNECTED,
+       BT_IO_ERROR_CONNECT_FAILED,
+       BT_IO_ERROR_FAILED,
+       BT_IO_ERROR_INVALID_ARGS,
+} BtIOError;
+
+#define BT_IO_ERROR bt_io_error_quark()
+
+GQuark bt_io_error_quark(void);
+
+typedef enum {
+       BT_IO_L2RAW,
+       BT_IO_L2CAP,
+       BT_IO_L2ERTM,
+       BT_IO_RFCOMM,
+       BT_IO_SCO,
+} BtIOType;
+
+typedef enum {
+       BT_IO_OPT_INVALID = 0,
+       BT_IO_OPT_SOURCE,
+       BT_IO_OPT_SOURCE_BDADDR,
+       BT_IO_OPT_DEST,
+       BT_IO_OPT_DEST_BDADDR,
+       BT_IO_OPT_DEST_TYPE,
+       BT_IO_OPT_DEFER_TIMEOUT,
+       BT_IO_OPT_SEC_LEVEL,
+       BT_IO_OPT_KEY_SIZE,
+       BT_IO_OPT_CHANNEL,
+       BT_IO_OPT_SOURCE_CHANNEL,
+       BT_IO_OPT_DEST_CHANNEL,
+       BT_IO_OPT_PSM,
+       BT_IO_OPT_CID,
+       BT_IO_OPT_MTU,
+       BT_IO_OPT_OMTU,
+       BT_IO_OPT_IMTU,
+       BT_IO_OPT_MASTER,
+       BT_IO_OPT_HANDLE,
+       BT_IO_OPT_CLASS,
+       BT_IO_OPT_MODE,
+       BT_IO_OPT_FLUSHABLE,
+       BT_IO_OPT_PRIORITY,
+} BtIOOption;
+
+typedef enum {
+       BT_IO_SEC_SDP = 0,
+       BT_IO_SEC_LOW,
+       BT_IO_SEC_MEDIUM,
+       BT_IO_SEC_HIGH,
+} BtIOSecLevel;
+
+typedef enum {
+       BT_IO_MODE_BASIC = 0,
+       BT_IO_MODE_RETRANS,
+       BT_IO_MODE_FLOWCTL,
+       BT_IO_MODE_ERTM,
+       BT_IO_MODE_STREAMING
+} BtIOMode;
+
+typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
+
+typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
+
+gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data,
+                                       GDestroyNotify destroy, GError **err);
+
+gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err,
+                                               BtIOOption opt1, ...);
+
+gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err,
+                                               BtIOOption opt1, ...);
+
+GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,
+                               gpointer user_data, GDestroyNotify destroy,
+                               GError **err, BtIOOption opt1, ...);
+
+GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect,
+                               BtIOConfirm confirm, gpointer user_data,
+                               GDestroyNotify destroy, GError **err,
+                               BtIOOption opt1, ...);
+
+#endif
diff --git a/compat/bnep.c b/compat/bnep.c
new file mode 100644 (file)
index 0000000..281350b
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+
+#include <netinet/in.h>
+
+#include "pand.h"
+
+static int ctl;
+
+/* Compatibility with old ioctls */
+#define OLD_BNEPCONADD      1
+#define OLD_BNEPCONDEL      2
+#define OLD_BNEPGETCONLIST  3
+#define OLD_BNEPGETCONINFO  4
+
+static unsigned long bnepconnadd;
+static unsigned long bnepconndel;
+static unsigned long bnepgetconnlist;
+static unsigned long bnepgetconninfo;
+
+static struct {
+       char     *str;
+       uint16_t uuid;
+} __svc[] = {
+       { "PANU", BNEP_SVC_PANU },
+       { "NAP",  BNEP_SVC_NAP  },
+       { "GN",   BNEP_SVC_GN   },
+       { NULL }
+};
+
+int bnep_str2svc(char *svc, uint16_t *uuid)
+{
+       int i;
+       for (i = 0; __svc[i].str; i++)
+               if (!strcasecmp(svc, __svc[i].str)) {
+                       *uuid = __svc[i].uuid;
+                       return 0;
+               }
+       return -1;
+}
+
+char *bnep_svc2str(uint16_t uuid)
+{
+       int i;
+       for (i = 0; __svc[i].str; i++)
+               if (__svc[i].uuid == uuid)
+                       return __svc[i].str;
+       return NULL;
+}
+
+int bnep_init(void)
+{
+       ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
+       if (ctl < 0) {
+               perror("Failed to open control socket");
+               return 1;
+       }
+
+       /* Temporary ioctl compatibility hack */
+       {
+               struct bnep_connlist_req req;
+               struct bnep_conninfo ci[1];
+
+               req.cnum = 1;
+               req.ci   = ci;
+
+               if (!ioctl(ctl, BNEPGETCONNLIST, &req)) {
+                       /* New ioctls */
+                       bnepconnadd     = BNEPCONNADD;
+                       bnepconndel     = BNEPCONNDEL;
+                       bnepgetconnlist = BNEPGETCONNLIST;
+                       bnepgetconninfo = BNEPGETCONNINFO;
+               } else {
+                       /* Old ioctls */
+                       bnepconnadd     = OLD_BNEPCONADD;
+                       bnepconndel     = OLD_BNEPCONDEL;
+                       bnepgetconnlist = OLD_BNEPGETCONLIST;
+                       bnepgetconninfo = OLD_BNEPGETCONINFO;
+               }
+       }
+
+       return 0;
+}
+
+int bnep_cleanup(void)
+{
+       close(ctl);
+       return 0;
+}
+
+int bnep_show_connections(void)
+{
+       struct bnep_connlist_req req;
+       struct bnep_conninfo ci[48];
+       unsigned int i;
+
+       req.cnum = 48;
+       req.ci   = ci;
+       if (ioctl(ctl, bnepgetconnlist, &req)) {
+               perror("Failed to get connection list");
+               return -1;
+       }
+
+       for (i = 0; i < req.cnum; i++) {
+               char addr[18];
+               ba2str((bdaddr_t *) ci[i].dst, addr);
+               printf("%s %s %s\n", ci[i].device,
+                       addr, bnep_svc2str(ci[i].role));
+       }
+       return 0;
+}
+
+int bnep_kill_connection(uint8_t *dst)
+{
+       struct bnep_conndel_req req;
+
+       memcpy(req.dst, dst, ETH_ALEN);
+       req.flags = 0;
+       if (ioctl(ctl, bnepconndel, &req)) {
+               perror("Failed to kill connection");
+               return -1;
+       }
+       return 0;
+}
+
+int bnep_kill_all_connections(void)
+{
+       struct bnep_connlist_req req;
+       struct bnep_conninfo ci[48];
+       unsigned int i;
+
+       req.cnum = 48;
+       req.ci   = ci;
+       if (ioctl(ctl, bnepgetconnlist, &req)) {
+               perror("Failed to get connection list");
+               return -1;
+       }
+
+       for (i = 0; i < req.cnum; i++) {
+               struct bnep_conndel_req req;
+               memcpy(req.dst, ci[i].dst, ETH_ALEN);
+               req.flags = 0;
+               ioctl(ctl, bnepconndel, &req);
+       }
+       return 0;
+}
+
+static int bnep_connadd(int sk, uint16_t role, char *dev)
+{
+       struct bnep_connadd_req req;
+
+       strncpy(req.device, dev, 16);
+       req.device[15] = '\0';
+       req.sock = sk;
+       req.role = role;
+       if (ioctl(ctl, bnepconnadd, &req))
+               return -1;
+       strncpy(dev, req.device, 16);
+       return 0;
+}
+
+struct __service_16 {
+       uint16_t dst;
+       uint16_t src;
+} __attribute__ ((packed));
+
+struct __service_32 {
+       uint16_t unused1;
+       uint16_t dst;
+       uint16_t unused2;
+       uint16_t src;
+} __attribute__ ((packed));
+
+struct __service_128 {
+       uint16_t unused1;
+       uint16_t dst;
+       uint16_t unused2[8];
+       uint16_t src;
+       uint16_t unused3[7];
+} __attribute__ ((packed));
+
+int bnep_accept_connection(int sk, uint16_t role, char *dev)
+{
+       struct bnep_setup_conn_req *req;
+       struct bnep_control_rsp *rsp;
+       unsigned char pkt[BNEP_MTU];
+       ssize_t r;
+
+       r = recv(sk, pkt, BNEP_MTU, 0);
+       if (r <= 0)
+               return -1;
+
+       errno = EPROTO;
+
+       if ((size_t) r < sizeof(*req))
+               return -1;
+
+       req = (void *) pkt;
+
+       /* Highest known Control command ID
+        * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
+       if (req->type == BNEP_CONTROL &&
+                               req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+               uint8_t pkt[3];
+
+               pkt[0] = BNEP_CONTROL;
+               pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
+               pkt[2] = req->ctrl;
+
+               send(sk, pkt, sizeof(pkt), 0);
+
+               return -1;
+       }
+
+       if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
+               return -1;
+
+       /* FIXME: Check role UUIDs */
+
+       rsp = (void *) pkt;
+       rsp->type = BNEP_CONTROL;
+       rsp->ctrl = BNEP_SETUP_CONN_RSP;
+       rsp->resp = htons(BNEP_SUCCESS);
+       if (send(sk, rsp, sizeof(*rsp), 0) < 0)
+               return -1;
+
+       return bnep_connadd(sk, role, dev);
+}
+
+/* Create BNEP connection
+ * sk      - Connect L2CAP socket
+ * role    - Local role
+ * service - Remote service
+ * dev     - Network device (contains actual dev name on return)
+ */
+int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev)
+{
+       struct bnep_setup_conn_req *req;
+       struct bnep_control_rsp *rsp;
+       struct __service_16 *s;
+       struct timeval timeo;
+       unsigned char pkt[BNEP_MTU];
+       ssize_t r;
+
+       /* Send request */
+       req = (void *) pkt;
+       req->type = BNEP_CONTROL;
+       req->ctrl = BNEP_SETUP_CONN_REQ;
+       req->uuid_size = 2;     /* 16bit UUID */
+
+       s = (void *) req->service;
+       s->dst = htons(svc);
+       s->src = htons(role);
+
+       memset(&timeo, 0, sizeof(timeo));
+       timeo.tv_sec = 30;
+
+       setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+
+       if (send(sk, pkt, sizeof(*req) + sizeof(*s), 0) < 0)
+               return -1;
+
+receive:
+       /* Get response */
+       r = recv(sk, pkt, BNEP_MTU, 0);
+       if (r <= 0)
+               return -1;
+
+       memset(&timeo, 0, sizeof(timeo));
+       timeo.tv_sec = 0;
+
+       setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+
+       errno = EPROTO;
+
+       if ((size_t) r < sizeof(*rsp))
+               return -1;
+
+       rsp = (void *) pkt;
+       if (rsp->type != BNEP_CONTROL)
+               return -1;
+
+       if (rsp->ctrl != BNEP_SETUP_CONN_RSP)
+               goto receive;
+
+       r = ntohs(rsp->resp);
+
+       switch (r) {
+       case BNEP_SUCCESS:
+               break;
+
+       case BNEP_CONN_INVALID_DST:
+       case BNEP_CONN_INVALID_SRC:
+       case BNEP_CONN_INVALID_SVC:
+               errno = EPROTO;
+               return -1;
+
+       case BNEP_CONN_NOT_ALLOWED:
+               errno = EACCES;
+               return -1;
+       }
+
+       return bnep_connadd(sk, role, dev);
+}
diff --git a/compat/dun.c b/compat/dun.c
new file mode 100644 (file)
index 0000000..3f3a0d4
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <dirent.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "dund.h"
+#include "lib.h"
+
+#define PROC_BASE  "/proc"
+
+static int for_each_port(int (*func)(struct rfcomm_dev_info *, unsigned long), unsigned long arg)
+{
+       struct rfcomm_dev_list_req *dl;
+       struct rfcomm_dev_info *di;
+       long r = 0;
+       int  sk, i;
+
+       sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+       if (sk < 0 ) {
+               perror("Can't open RFCOMM control socket");
+               exit(1);
+       }
+
+       dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+       if (!dl) {
+               perror("Can't allocate request memory");
+               close(sk);
+               exit(1);
+       }
+
+       dl->dev_num = RFCOMM_MAX_DEV;
+       di = dl->dev_info;
+
+       if (ioctl(sk, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+               perror("Can't get device list");
+               exit(1);
+       }
+
+       for (i = 0; i < dl->dev_num; i++) {
+               r = func(di + i, arg);
+               if (r) break;
+       }
+
+       close(sk);
+       free(dl);
+       return r;
+}
+
+static int uses_rfcomm(char *path, char *dev)
+{
+       struct dirent *de;
+       DIR   *dir;
+
+       dir = opendir(path);
+       if (!dir)
+               return 0;
+
+       if (chdir(path) < 0)
+               return 0;
+
+       while ((de = readdir(dir)) != NULL) {
+               char link[PATH_MAX + 1];
+               int  len = readlink(de->d_name, link, PATH_MAX);
+               if (len > 0) {
+                       link[len] = 0;
+                       if (strstr(link, dev)) {
+                               closedir(dir);
+                               return 1;
+                       }
+               }
+       }
+
+       closedir(dir);
+
+       return 0;
+}
+
+static int find_pppd(int id, pid_t *pid)
+{
+       struct dirent *de;
+       char  path[PATH_MAX + 1];
+       char  dev[10];
+       int   empty = 1;
+       DIR   *dir;
+
+       dir = opendir(PROC_BASE);
+       if (!dir) {
+               perror(PROC_BASE);
+               return -1;
+       }
+
+       sprintf(dev, "rfcomm%d", id);
+
+       *pid = 0;
+       while ((de = readdir(dir)) != NULL) {
+               empty = 0;
+               if (isdigit(de->d_name[0])) {
+                       sprintf(path, "%s/%s/fd", PROC_BASE, de->d_name);
+                       if (uses_rfcomm(path, dev)) {
+                               *pid = atoi(de->d_name);
+                               break;
+                       }
+               }
+       }
+       closedir(dir);
+
+       if (empty)
+               fprintf(stderr, "%s is empty (not mounted ?)\n", PROC_BASE);
+
+       return *pid != 0;
+}
+
+static int dun_exec(char *tty, char *prog, char **args)
+{
+       int pid = fork();
+       int fd;
+
+       switch (pid) {
+       case -1:
+               return -1;
+
+       case 0:
+               break;
+
+       default:
+               return pid;
+       }
+
+       setsid();
+
+       /* Close all FDs */
+       for (fd = 3; fd < 20; fd++)
+               close(fd);
+
+       execvp(prog, args);
+
+       syslog(LOG_ERR, "Error while executing %s", prog);
+
+       exit(1);
+}
+
+static int dun_create_tty(int sk, char *tty, int size)
+{
+       struct sockaddr_rc sa;
+       struct stat st;
+       socklen_t alen;
+       int id, try = 30;
+
+       struct rfcomm_dev_req req = {
+               .flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
+               .dev_id = -1
+       };
+
+       alen = sizeof(sa);
+       if (getpeername(sk, (struct sockaddr *) &sa, &alen) < 0)
+               return -1;
+       bacpy(&req.dst, &sa.rc_bdaddr);
+
+       alen = sizeof(sa);
+       if (getsockname(sk, (struct sockaddr *) &sa, &alen) < 0)
+               return -1;
+       bacpy(&req.src, &sa.rc_bdaddr);
+       req.channel = sa.rc_channel;
+
+       id = ioctl(sk, RFCOMMCREATEDEV, &req);
+       if (id < 0)
+               return id;
+
+       snprintf(tty, size, "/dev/rfcomm%d", id);
+       while (stat(tty, &st) < 0) {
+               snprintf(tty, size, "/dev/bluetooth/rfcomm/%d", id);
+               if (stat(tty, &st) < 0) {
+                       snprintf(tty, size, "/dev/rfcomm%d", id);
+                       if (try--) {
+                               usleep(100 * 1000);
+                               continue;
+                       }
+
+                       memset(&req, 0, sizeof(req));
+                       req.dev_id = id;
+                       ioctl(sk, RFCOMMRELEASEDEV, &req);
+
+                       return -1;
+               }
+       }
+
+       return id;
+}
+
+int dun_init(void)
+{
+       return 0;
+}
+
+int dun_cleanup(void)
+{
+       return 0;
+}
+
+static int show_conn(struct rfcomm_dev_info *di, unsigned long arg)
+{
+       pid_t pid;
+
+       if (di->state == BT_CONNECTED &&
+               (di->flags & (1<<RFCOMM_REUSE_DLC)) &&
+               (di->flags & (1<<RFCOMM_TTY_ATTACHED)) &&
+               (di->flags & (1<<RFCOMM_RELEASE_ONHUP))) {
+
+               if (find_pppd(di->id, &pid)) {
+                       char dst[18];
+                       ba2str(&di->dst, dst);
+
+                       printf("rfcomm%d: %s channel %d pppd pid %d\n",
+                                       di->id, dst, di->channel, pid);
+               }
+       }
+       return 0;
+}
+
+static int kill_conn(struct rfcomm_dev_info *di, unsigned long arg)
+{
+       bdaddr_t *dst = (bdaddr_t *) arg;
+       pid_t pid;
+
+       if (di->state == BT_CONNECTED &&
+               (di->flags & (1<<RFCOMM_REUSE_DLC)) &&
+               (di->flags & (1<<RFCOMM_TTY_ATTACHED)) &&
+               (di->flags & (1<<RFCOMM_RELEASE_ONHUP))) {
+
+               if (dst && bacmp(&di->dst, dst))
+                       return 0;
+
+               if (find_pppd(di->id, &pid)) {
+                       if (kill(pid, SIGINT) < 0)
+                               perror("Kill");
+
+                       if (!dst)
+                               return 0;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int dun_show_connections(void)
+{
+       for_each_port(show_conn, 0);
+       return 0;
+}
+
+int dun_kill_connection(uint8_t *dst)
+{
+       for_each_port(kill_conn, (unsigned long) dst);
+       return 0;
+}
+
+int dun_kill_all_connections(void)
+{
+       for_each_port(kill_conn, 0);
+       return 0;
+}
+
+int dun_open_connection(int sk, char *pppd, char **args, int wait)
+{
+       char tty[100];
+       int  pid;
+
+       if (dun_create_tty(sk, tty, sizeof(tty) - 1) < 0) {
+               syslog(LOG_ERR, "RFCOMM TTY creation failed. %s(%d)", strerror(errno), errno);
+               return -1;
+       }
+
+       args[0] = "pppd";
+       args[1] = tty;
+       args[2] = "nodetach";
+
+       pid = dun_exec(tty, pppd, args);
+       if (pid < 0) {
+               syslog(LOG_ERR, "Exec failed. %s(%d)", strerror(errno), errno);
+               return -1;
+       }
+
+       if (wait) {
+               int status;
+               waitpid(pid, &status, 0);
+               /* FIXME: Check for waitpid errors */
+       }
+
+       return 0;
+}
diff --git a/compat/dund.1 b/compat/dund.1
new file mode 100644 (file)
index 0000000..09fb7f7
--- /dev/null
@@ -0,0 +1,72 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.29.
+.TH BlueZ "1" "February 2003" "DUN daemon" "User Commands"
+.SH NAME
+dund \- BlueZ Bluetooth dial-up networking daemon
+.SH DESCRIPTION
+DUN daemon
+.SH SYNOPSIS
+dund <options> [pppd options]
+.SH OPTIONS
+.TP
+\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR
+Show active DUN connections
+.TP
+\fB\-\-listen\fR \fB\-s\fR
+Listen for DUN connections
+.TP
+\fB\-\-dialup\fR \fB\-u\fR
+Listen for dialup/telephone connections
+.TP
+\fB\-\-connect\fR \fB\-c\fR <bdaddr>
+Create DUN connection
+.TP
+\fB\-\-mrouter\fR \fB\-m\fR <bdaddr>
+Create mRouter connection
+.TP
+\fB\-\-search\fR \fB\-Q[duration]\fR
+Search and connect
+.TP
+\fB\-\-kill\fR \fB\-k\fR <bdaddr>
+Kill DUN connection
+.TP
+\fB\-\-killall\fR \fB\-K\fR
+Kill all DUN connections
+.TP
+\fB\-\-channel\fR \fB\-C\fR <channel>
+RFCOMM channel
+.TP
+\fB\-\-device\fR \fB\-i\fR <bdaddr>
+Source bdaddr
+.TP
+\fB\-\-nosdp\fR \fB\-D\fR
+Disable SDP
+.TP
+\fB\-\-auth\fR \fB\-A\fR
+Enable authentification
+.TP
+\fB\-\-encrypt\fR \fB\-E\fR
+Enable encryption
+.TP
+\fB\-\-secure\fR \fB\-S\fR
+Secure connection
+.TP
+\fB\-\-master\fR \fB\-M\fR
+Become the master of a piconet
+.TP
+\fB\-\-nodetach\fR \fB\-n\fR
+Do not become a daemon
+.TP
+\fB\-\-persist\fR \fB\-p[interval]\fR
+Persist mode
+.TP
+\fB\-\-pppd\fR \fB\-d\fR <pppd>
+Location of the PPP daemon (pppd)
+.TP
+\fB\-\-msdun\fR \fB\-X\fR [timeo]
+Enable Microsoft dialup networking support
+.TP
+\fB\-\-activesync\fR \fB\-a\fR
+Enable Microsoft ActiveSync networking
+.TP
+\fB\-\-cache\fR \fB\-C\fR [valid]
+Enable address cache
diff --git a/compat/dund.c b/compat/dund.c
new file mode 100644 (file)
index 0000000..af1b536
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/hidp.h>
+
+#include "sdp.h"
+#include "dund.h"
+#include "lib.h"
+
+volatile sig_atomic_t __io_canceled;
+
+/* MS dialup networking support (i.e. CLIENT / CLIENTSERVER thing) */
+static int msdun = 0;
+
+static char *pppd = "/usr/sbin/pppd";
+static char *pppd_opts[DUN_MAX_PPP_OPTS] =
+       {
+               /* First 3 are reserved */
+               "", "", "",
+               "noauth",
+               "noipdefault",
+               NULL
+       };
+
+static int  detach = 1;
+static int  persist;
+static int  use_sdp = 1;
+static int  auth;
+static int  encrypt;
+static int  secure;
+static int  master;
+static int  type = LANACCESS;
+static int  search_duration = 10;
+static uint use_cache;
+
+static int  channel;
+
+static struct {
+       uint     valid;
+       char     dst[40];
+       bdaddr_t bdaddr;
+       int      channel;
+} cache;
+
+static bdaddr_t src_addr = *BDADDR_ANY;
+static int src_dev = -1;
+
+volatile int terminate;
+
+enum {
+       NONE,
+       SHOW,
+       LISTEN,
+       CONNECT,
+       KILL
+} modes;
+
+static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter);
+
+static int do_listen(void)
+{
+       struct sockaddr_rc sa;
+       int sk, lm;
+
+       if (type == MROUTER) {
+               if (!cache.valid)
+                       return -1;
+
+               if (create_connection(cache.dst, &cache.bdaddr, type) < 0) {
+                       syslog(LOG_ERR, "Cannot connect to mRouter device. %s(%d)",
+                                                               strerror(errno), errno);
+                       return -1;
+               }
+       }
+
+       if (!channel)
+               channel = DUN_DEFAULT_CHANNEL;
+
+       if (use_sdp)
+               dun_sdp_register(&src_addr, channel, type);
+
+       if (type == MROUTER)
+               syslog(LOG_INFO, "Waiting for mRouter callback on channel %d", channel);
+
+       /* Create RFCOMM socket */
+       sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+       if (sk < 0) {
+               syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)",
+                               strerror(errno), errno);
+               return -1;
+       }
+
+       sa.rc_family  = AF_BLUETOOTH;
+       sa.rc_channel = channel;
+       sa.rc_bdaddr  = src_addr;
+
+       if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) {
+               syslog(LOG_ERR, "Bind failed. %s(%d)", strerror(errno), errno);
+               return -1;
+       }
+
+       /* Set link mode */
+       lm = 0;
+       if (master)
+               lm |= RFCOMM_LM_MASTER;
+       if (auth)
+               lm |= RFCOMM_LM_AUTH;
+       if (encrypt)
+               lm |= RFCOMM_LM_ENCRYPT;
+       if (secure)
+               lm |= RFCOMM_LM_SECURE;
+
+       if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
+               syslog(LOG_ERR, "Failed to set link mode. %s(%d)", strerror(errno), errno);
+               return -1;
+       }
+
+       listen(sk, 10);
+
+       while (!terminate) {
+               socklen_t alen = sizeof(sa);
+               int nsk;
+               char ba[40];
+               char ch[10];
+
+               nsk = accept(sk, (struct sockaddr *) &sa, &alen);
+               if (nsk < 0) {
+                       syslog(LOG_ERR, "Accept failed. %s(%d)", strerror(errno), errno);
+                       continue;
+               }
+
+               switch (fork()) {
+               case 0:
+                       break;
+               case -1:
+                       syslog(LOG_ERR, "Fork failed. %s(%d)", strerror(errno), errno);
+               default:
+                       close(nsk);
+                       if (type == MROUTER) {
+                               close(sk);
+                               terminate = 1;
+                       }
+                       continue;
+               }
+
+               close(sk);
+
+               if (msdun && ms_dun(nsk, 1, msdun) < 0) {
+                       syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno);
+                       exit(0);
+               }
+
+               ba2str(&sa.rc_bdaddr, ba);
+               snprintf(ch, sizeof(ch), "%d", channel);
+
+               /* Setup environment */
+               setenv("DUN_BDADDR",  ba, 1);
+               setenv("DUN_CHANNEL", ch, 1);
+
+               if (!dun_open_connection(nsk, pppd, pppd_opts, 0))
+                       syslog(LOG_INFO, "New connection from %s", ba);
+
+               close(nsk);
+               exit(0);
+       }
+
+       if (use_sdp)
+               dun_sdp_unregister();
+       return 0;
+}
+
+/* Connect and initiate RFCOMM session
+ * Returns:
+ *   -1 - critical error (exit persist mode)
+ *   1  - non critical error
+ *   0  - success
+ */
+static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter)
+{
+       struct sockaddr_rc sa;
+       int sk, err = 0, ch;
+
+       if (use_cache && cache.valid && cache.channel) {
+               /* Use cached channel */
+               ch = cache.channel;
+
+       } else if (!channel) {
+               syslog(LOG_INFO, "Searching for %s on %s", mrouter ? "SP" : "LAP", dst);
+
+               if (dun_sdp_search(&src_addr, bdaddr, &ch, mrouter) <= 0)
+                       return 0;
+       } else
+               ch = channel;
+
+       syslog(LOG_INFO, "Connecting to %s channel %d", dst, ch);
+
+       sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+       if (sk < 0) {
+               syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)",
+                    strerror(errno), errno);
+               return -1;
+       }
+
+       sa.rc_family  = AF_BLUETOOTH;
+       sa.rc_channel = 0;
+       sa.rc_bdaddr  = src_addr;
+
+       if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)))
+               syslog(LOG_ERR, "Bind failed. %s(%d)",
+                       strerror(errno), errno);
+
+       sa.rc_channel = ch;
+       sa.rc_bdaddr  = *bdaddr;
+
+       if (!connect(sk, (struct sockaddr *) &sa, sizeof(sa)) ) {
+               if (mrouter) {
+                       sleep(1);
+                       close(sk);
+                       return 0;
+               }
+
+               syslog(LOG_INFO, "Connection established");
+
+               if (msdun && ms_dun(sk, 0, msdun) < 0) {
+                       syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno);
+                       err = 1;
+                       goto out;
+               }
+
+               if (!dun_open_connection(sk, pppd, pppd_opts, (persist > 0)))
+                       err = 0;
+               else
+                       err = 1;
+       } else {
+               syslog(LOG_ERR, "Connect to %s failed. %s(%d)",
+                               dst, strerror(errno), errno);
+               err = 1;
+       }
+
+out:
+       if (use_cache) {
+               if (!err) {
+                       /* Succesesful connection, validate cache */
+                       strcpy(cache.dst, dst);
+                       bacpy(&cache.bdaddr, bdaddr);
+                       cache.channel = ch;
+                       cache.valid   = use_cache;
+               } else {
+                       cache.channel = 0;
+                       cache.valid--;
+               }
+       }
+
+       close(sk);
+       return err;
+}
+
+/* Search and connect
+ * Returns:
+ *   -1 - critical error (exit persist mode)
+ *   1  - non critical error
+ *   0  - success
+ */
+static int do_connect(void)
+{
+       inquiry_info *ii;
+       int reconnect = 0;
+       int i, n, r = 0;
+
+       do {
+               if (reconnect)
+                       sleep(persist);
+               reconnect = 1;
+
+               if (cache.valid) {
+                       /* Use cached bdaddr */
+                       r = create_connection(cache.dst, &cache.bdaddr, 0);
+                       if (r < 0) {
+                               terminate = 1;
+                               break;
+                       }
+                       continue;
+               }
+
+               syslog(LOG_INFO, "Inquiring");
+
+               /* FIXME: Should we use non general LAP here ? */
+
+               ii = NULL;
+               n  = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0);
+               if (n < 0) {
+                       syslog(LOG_ERR, "Inquiry failed. %s(%d)", strerror(errno), errno);
+                       continue;
+               }
+
+               for (i = 0; i < n; i++) {
+                       char dst[40];
+                       ba2str(&ii[i].bdaddr, dst);
+
+                       r = create_connection(dst, &ii[i].bdaddr, 0);
+                       if (r < 0) {
+                               terminate = 1;
+                               break;
+                       }
+               }
+               bt_free(ii);
+       } while (!terminate && persist);
+
+       return r;
+}
+
+static void do_show(void)
+{
+       dun_show_connections();
+}
+
+static void do_kill(char *dst)
+{
+       if (dst) {
+               bdaddr_t ba;
+               str2ba(dst, &ba);
+               dun_kill_connection((void *) &ba);
+       } else
+               dun_kill_all_connections();
+}
+
+static void sig_hup(int sig)
+{
+       return;
+}
+
+static void sig_term(int sig)
+{
+       io_cancel();
+       terminate = 1;
+}
+
+static struct option main_lopts[] = {
+       { "help",       0, 0, 'h' },
+       { "listen",     0, 0, 's' },
+       { "connect",    1, 0, 'c' },
+       { "search",     2, 0, 'Q' },
+       { "kill",       1, 0, 'k' },
+       { "killall",    0, 0, 'K' },
+       { "channel",    1, 0, 'P' },
+       { "device",     1, 0, 'i' },
+       { "nosdp",      0, 0, 'D' },
+       { "list",       0, 0, 'l' },
+       { "show",       0, 0, 'l' },
+       { "nodetach",   0, 0, 'n' },
+       { "persist",    2, 0, 'p' },
+       { "auth",       0, 0, 'A' },
+       { "encrypt",    0, 0, 'E' },
+       { "secure",     0, 0, 'S' },
+       { "master",     0, 0, 'M' },
+       { "cache",      0, 0, 'C' },
+       { "pppd",       1, 0, 'd' },
+       { "msdun",      2, 0, 'X' },
+       { "activesync", 0, 0, 'a' },
+       { "mrouter",    1, 0, 'm' },
+       { "dialup",     0, 0, 'u' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *main_sopts = "hsc:k:Kr:i:lnp::DQ::AESMP:C::P:Xam:u";
+
+static const char *main_help =
+       "Bluetooth LAP (LAN Access over PPP) daemon version %s\n"
+       "Usage:\n"
+       "\tdund <options> [pppd options]\n"
+       "Options:\n"
+       "\t--show --list -l          Show active LAP connections\n"
+       "\t--listen -s               Listen for LAP connections\n"
+       "\t--dialup -u               Pretend to be a dialup/telephone\n"
+       "\t--connect -c <bdaddr>     Create LAP connection\n"
+       "\t--mrouter -m <bdaddr>     Create mRouter connection\n"
+       "\t--search -Q[duration]     Search and connect\n"
+       "\t--kill -k <bdaddr>        Kill LAP connection\n"
+       "\t--killall -K              Kill all LAP connections\n"
+       "\t--channel -P <channel>    RFCOMM channel\n"
+       "\t--device -i <bdaddr>      Source bdaddr\n"
+       "\t--nosdp -D                Disable SDP\n"
+       "\t--auth -A                 Enable authentication\n"
+       "\t--encrypt -E              Enable encryption\n"
+       "\t--secure -S               Secure connection\n"
+       "\t--master -M               Become the master of a piconet\n"
+       "\t--nodetach -n             Do not become a daemon\n"
+       "\t--persist -p[interval]    Persist mode\n"
+       "\t--pppd -d <pppd>          Location of the PPP daemon (pppd)\n"
+       "\t--msdun -X[timeo]         Enable Microsoft dialup networking support\n"
+       "\t--activesync -a           Enable Microsoft ActiveSync networking\n"
+       "\t--cache -C[valid]         Enable address cache\n";
+
+int main(int argc, char *argv[])
+{
+       char *dst = NULL, *src = NULL;
+       struct sigaction sa;
+       int mode = NONE;
+       int opt;
+
+       while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) {
+               switch(opt) {
+               case 'l':
+                       mode = SHOW;
+                       detach = 0;
+                       break;
+
+               case 's':
+                       mode = LISTEN;
+                       type = LANACCESS;
+                       break;
+
+               case 'c':
+                       mode = CONNECT;
+                       dst  = strdup(optarg);
+                       break;
+
+               case 'Q':
+                       mode = CONNECT;
+                       dst  = NULL;
+                       if (optarg)
+                               search_duration = atoi(optarg);
+                       break;
+
+               case 'k':
+                       mode = KILL;
+                       detach = 0;
+                       dst  = strdup(optarg);
+                       break;
+
+               case 'K':
+                       mode = KILL;
+                       detach = 0;
+                       dst  = NULL;
+                       break;
+
+               case 'P':
+                       channel = atoi(optarg);
+                       break;
+
+               case 'i':
+                       src = strdup(optarg);
+                       break;
+
+               case 'D':
+                       use_sdp = 0;
+                       break;
+
+               case 'A':
+                       auth = 1;
+                       break;
+
+               case 'E':
+                       encrypt = 1;
+                       break;
+
+               case 'S':
+                       secure = 1;
+                       break;
+
+               case 'M':
+                       master = 1;
+                       break;
+
+               case 'n':
+                       detach = 0;
+                       break;
+
+               case 'p':
+                       if (optarg)
+                               persist = atoi(optarg);
+                       else
+                               persist = 5;
+                       break;
+
+               case 'C':
+                       if (optarg)
+                               use_cache = atoi(optarg);
+                       else
+                               use_cache = 2;
+                       break;
+
+               case 'd':
+                       pppd  = strdup(optarg);
+                       break;
+
+               case 'X':
+                       if (optarg)
+                               msdun = atoi(optarg);
+                       else
+                               msdun = 10;
+                       break;
+
+               case 'a':
+                       msdun = 10;
+                       type = ACTIVESYNC;
+                       break;
+
+               case 'm':
+                       mode = LISTEN;
+                       dst  = strdup(optarg);
+                       type = MROUTER;
+                       break;
+
+               case 'u':
+                       mode = LISTEN;
+                       type = DIALUP;
+                       break;
+
+               case 'h':
+               default:
+                       printf(main_help, VERSION);
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       /* The rest is pppd options */
+       if (argc > 0) {
+               for (opt = 3; argc && opt < DUN_MAX_PPP_OPTS - 1;
+                                                       argc--, opt++)
+                       pppd_opts[opt] = *argv++;
+               pppd_opts[opt] = NULL;
+       }
+
+       io_init();
+
+       if (dun_init()) {
+               free(dst);
+               return -1;
+       }
+
+       /* Check non daemon modes first */
+       switch (mode) {
+       case SHOW:
+               do_show();
+               free(dst);
+               return 0;
+
+       case KILL:
+               do_kill(dst);
+               free(dst);
+               return 0;
+
+       case NONE:
+               printf(main_help, VERSION);
+               free(dst);
+               return 0;
+       }
+
+       /* Initialize signals */
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       if (detach && daemon(0, 0)) {
+               perror("Can't start daemon");
+               exit(1);
+       }
+
+       openlog("dund", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+       syslog(LOG_INFO, "Bluetooth DUN daemon version %s", VERSION);
+
+       if (src) {
+               src_dev = hci_devid(src);
+               if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) {
+                       syslog(LOG_ERR, "Invalid source. %s(%d)", strerror(errno), errno);
+                       free(dst);
+                       return -1;
+               }
+       }
+
+       if (dst) {
+               strncpy(cache.dst, dst, sizeof(cache.dst) - 1);
+               str2ba(dst, &cache.bdaddr);
+
+               /* Disable cache invalidation */
+               use_cache = cache.valid = ~0;
+       }
+
+       switch (mode) {
+       case CONNECT:
+               do_connect();
+               break;
+
+       case LISTEN:
+               do_listen();
+               break;
+       }
+
+       free(dst);
+       return 0;
+}
diff --git a/compat/dund.h b/compat/dund.h
new file mode 100644 (file)
index 0000000..e3a4ef6
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define DUN_CONFIG_DIR "/etc/bluetooth/dun"
+
+#define DUN_DEFAULT_CHANNEL    1
+
+#define DUN_MAX_PPP_OPTS       40
+
+int dun_init(void);
+int dun_cleanup(void);
+
+int dun_show_connections(void);
+int dun_kill_connection(uint8_t *dst);
+int dun_kill_all_connections(void);
+
+int dun_open_connection(int sk, char *pppd, char **pppd_opts, int wait);
+
+int ms_dun(int fd, int server, int timeo);
diff --git a/compat/fakehid.c b/compat/fakehid.c
new file mode 100644 (file)
index 0000000..66161b3
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/hidp.h>
+
+#include "hidd.h"
+#include "uinput.h"
+
+#include <math.h>
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+}
+
+static void sig_term(int sig)
+{
+       __io_canceled = 1;
+}
+
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+       struct uinput_event event;
+
+       if (fd <= fileno(stderr))
+               return -EINVAL;
+
+       memset(&event, 0, sizeof(event));
+       event.type = type;
+       event.code = code;
+       event.value = value;
+
+       return write(fd, &event, sizeof(event));
+}
+
+static int uinput_create(char *name, int keyboard, int mouse)
+{
+       struct uinput_dev dev;
+       int fd, aux;
+
+       fd = open("/dev/uinput", O_RDWR);
+       if (fd < 0) {
+               fd = open("/dev/input/uinput", O_RDWR);
+               if (fd < 0) {
+                       fd = open("/dev/misc/uinput", O_RDWR);
+                       if (fd < 0) {
+                               fprintf(stderr, "Can't open input device: %s (%d)\n",
+                                                       strerror(errno), errno);
+                               return -1;
+                       }
+               }
+       }
+
+       memset(&dev, 0, sizeof(dev));
+
+       if (name)
+               strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+       dev.id.bustype = BUS_BLUETOOTH;
+       dev.id.vendor  = 0x0000;
+       dev.id.product = 0x0000;
+       dev.id.version = 0x0000;
+
+       if (write(fd, &dev, sizeof(dev)) < 0) {
+               fprintf(stderr, "Can't write device information: %s (%d)\n",
+                                                       strerror(errno), errno);
+               close(fd);
+               return -1;
+       }
+
+       if (mouse) {
+               ioctl(fd, UI_SET_EVBIT, EV_REL);
+
+               for (aux = REL_X; aux <= REL_MISC; aux++)
+                       ioctl(fd, UI_SET_RELBIT, aux);
+       }
+
+       if (keyboard) {
+               ioctl(fd, UI_SET_EVBIT, EV_KEY);
+               ioctl(fd, UI_SET_EVBIT, EV_LED);
+               ioctl(fd, UI_SET_EVBIT, EV_REP);
+
+               for (aux = KEY_RESERVED; aux <= KEY_UNKNOWN; aux++)
+                       ioctl(fd, UI_SET_KEYBIT, aux);
+               /*
+                *for (aux = LED_NUML; aux <= LED_MISC; aux++)
+                *      ioctl(fd, UI_SET_LEDBIT, aux);
+                */
+       }
+
+       if (mouse) {
+               ioctl(fd, UI_SET_EVBIT, EV_KEY);
+
+               for (aux = BTN_LEFT; aux <= BTN_BACK; aux++)
+                       ioctl(fd, UI_SET_KEYBIT, aux);
+       }
+
+       ioctl(fd, UI_DEV_CREATE);
+
+       return fd;
+}
+
+static int rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+       struct sockaddr_rc addr;
+       int sk;
+
+       sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+       if (sk < 0) {
+               fprintf(stderr, "Can't create socket: %s (%d)\n",
+                                                       strerror(errno), errno);
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, src);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               fprintf(stderr, "Can't bind socket: %s (%d)\n",
+                                                       strerror(errno), errno);
+               close(sk);
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, dst);
+       addr.rc_channel = channel;
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               fprintf(stderr, "Can't connect: %s (%d)\n",
+                                                       strerror(errno), errno);
+               close(sk);
+               return -1;
+       }
+
+       return sk;
+}
+
+static void func(int fd)
+{
+}
+
+static void back(int fd)
+{
+}
+
+static void next(int fd)
+{
+}
+
+static void button(int fd, unsigned int button, int is_press)
+{
+       switch (button) {
+       case 1:
+               send_event(fd, EV_KEY, BTN_LEFT, is_press);
+               break;
+       case 3:
+               send_event(fd, EV_KEY, BTN_RIGHT, is_press);
+               break;
+       }
+
+       send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static void move(int fd, unsigned int direction)
+{
+       double angle;
+       int32_t x, y;
+
+       angle = (direction * 22.5) * 3.1415926 / 180;
+       x = (int) (sin(angle) * 8);
+       y = (int) (cos(angle) * -8);
+
+       send_event(fd, EV_REL, REL_X, x);
+       send_event(fd, EV_REL, REL_Y, y);
+
+       send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static inline void epox_decode(int fd, unsigned char event)
+{
+       switch (event) {
+       case 48:
+               func(fd); break;
+       case 55:
+               back(fd); break;
+       case 56:
+               next(fd); break;
+       case 53:
+               button(fd, 1, 1); break;
+       case 121:
+               button(fd, 1, 0); break;
+       case 113:
+               break;
+       case 54:
+               button(fd, 3, 1); break;
+       case 120:
+               button(fd, 3, 0); break;
+       case 112:
+               break;
+       case 51:
+               move(fd, 0); break;
+       case 97:
+               move(fd, 1); break;
+       case 65:
+               move(fd, 2); break;
+       case 98:
+               move(fd, 3); break;
+       case 50:
+               move(fd, 4); break;
+       case 99:
+               move(fd, 5); break;
+       case 67:
+               move(fd, 6); break;
+       case 101:
+               move(fd, 7); break;
+       case 52:
+               move(fd, 8); break;
+       case 100:
+               move(fd, 9); break;
+       case 66:
+               move(fd, 10); break;
+       case 102:
+               move(fd, 11); break;
+       case 49:
+               move(fd, 12); break;
+       case 103:
+               move(fd, 13); break;
+       case 57:
+               move(fd, 14); break;
+       case 104:
+               move(fd, 15); break;
+       case 69:
+               break;
+       default:
+               printf("Unknown event code %d\n", event);
+               break;
+       }
+}
+
+int epox_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+       unsigned char buf[16];
+       struct sigaction sa;
+       struct pollfd p;
+       sigset_t sigs;
+       char addr[18];
+       int i, fd, sk, len;
+
+       sk = rfcomm_connect(src, dst, channel);
+       if (sk < 0)
+               return -1;
+
+       fd = uinput_create("Bluetooth Presenter", 0, 1);
+       if (fd < 0) {
+               close(sk);
+               return -1;
+       }
+
+       ba2str(dst, addr);
+
+       printf("Connected to %s on channel %d\n", addr, channel);
+       printf("Press CTRL-C for hangup\n");
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       sigfillset(&sigs);
+       sigdelset(&sigs, SIGCHLD);
+       sigdelset(&sigs, SIGPIPE);
+       sigdelset(&sigs, SIGTERM);
+       sigdelset(&sigs, SIGINT);
+       sigdelset(&sigs, SIGHUP);
+
+       p.fd = sk;
+       p.events = POLLIN | POLLERR | POLLHUP;
+
+       while (!__io_canceled) {
+               p.revents = 0;
+               if (ppoll(&p, 1, NULL, &sigs) < 1)
+                       continue;
+
+               len = read(sk, buf, sizeof(buf));
+               if (len < 0)
+                       break;
+
+               for (i = 0; i < len; i++)
+                       epox_decode(fd, buf[i]);
+       }
+
+       printf("Disconnected\n");
+
+       ioctl(fd, UI_DEV_DESTROY);
+
+       close(fd);
+       close(sk);
+
+       return 0;
+}
+
+int headset_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+       printf("Not implemented\n");
+       return -1;
+}
+
+/* The strange meta key close to Ctrl has been assigned to Esc,
+   Fn key to CtrlR and the left space to Alt*/
+
+static unsigned char jthree_keycodes[63] = {
+       KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6,
+       KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T,
+       KEY_A, KEY_S, KEY_D, KEY_F, KEY_G,
+       KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B,
+       KEY_LEFTALT, KEY_TAB, KEY_CAPSLOCK, KEY_ESC,
+       KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE,
+       KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE,
+       KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER,
+       KEY_N, KEY_M, KEY_COMMA, KEY_DOT, KEY_SLASH, KEY_UP,
+       KEY_SPACE, KEY_COMPOSE, KEY_LEFT, KEY_DOWN, KEY_RIGHT,
+       KEY_LEFTCTRL, KEY_RIGHTSHIFT, KEY_LEFTSHIFT, KEY_DELETE, KEY_RIGHTCTRL, KEY_RIGHTALT,
+};
+
+static inline void jthree_decode(int fd, unsigned char event)
+{
+       if (event > 63)
+               send_event(fd, EV_KEY, jthree_keycodes[event & 0x3f], 0);
+       else
+               send_event(fd, EV_KEY, jthree_keycodes[event - 1], 1);
+}
+
+int jthree_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+       unsigned char buf[16];
+       struct sigaction sa;
+       struct pollfd p;
+       sigset_t sigs;
+       char addr[18];
+       int i, fd, sk, len;
+
+       sk = rfcomm_connect(src, dst, channel);
+       if (sk < 0)
+               return -1;
+
+       fd = uinput_create("J-Three Keyboard", 1, 0);
+       if (fd < 0) {
+               close(sk);
+               return -1;
+       }
+
+       ba2str(dst, addr);
+
+       printf("Connected to %s on channel %d\n", addr, channel);
+       printf("Press CTRL-C for hangup\n");
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       sigfillset(&sigs);
+       sigdelset(&sigs, SIGCHLD);
+       sigdelset(&sigs, SIGPIPE);
+       sigdelset(&sigs, SIGTERM);
+       sigdelset(&sigs, SIGINT);
+       sigdelset(&sigs, SIGHUP);
+
+       p.fd = sk;
+       p.events = POLLIN | POLLERR | POLLHUP;
+
+       while (!__io_canceled) {
+               p.revents = 0;
+               if (ppoll(&p, 1, NULL, &sigs) < 1)
+                       continue;
+
+               len = read(sk, buf, sizeof(buf));
+               if (len < 0)
+                       break;
+
+               for (i = 0; i < len; i++)
+                       jthree_decode(fd, buf[i]);
+       }
+
+       printf("Disconnected\n");
+
+       ioctl(fd, UI_DEV_DESTROY);
+
+       close(fd);
+       close(sk);
+
+       return 0;
+}
+
+static const int celluon_xlate_num[10] = {
+       KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9
+};
+
+static const int celluon_xlate_char[26] = {
+       KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J,
+       KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
+       KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z
+};
+
+static int celluon_xlate(int c)
+{
+       if (c >= '0' && c <= '9')
+               return celluon_xlate_num[c - '0'];
+
+       if (c >= 'A' && c <= 'Z')
+               return celluon_xlate_char[c - 'A'];
+
+       switch (c) {
+       case 0x08:
+               return KEY_BACKSPACE;
+       case 0x09:
+               return KEY_TAB;
+       case 0x0d:
+               return KEY_ENTER;
+       case 0x11:
+               return KEY_LEFTCTRL;
+       case 0x14:
+               return KEY_CAPSLOCK;
+       case 0x20:
+               return KEY_SPACE;
+       case 0x25:
+               return KEY_LEFT;
+       case 0x26:
+               return KEY_UP;
+       case 0x27:
+               return KEY_RIGHT;
+       case 0x28:
+               return KEY_DOWN;
+       case 0x2e:
+               return KEY_DELETE;
+       case 0x5b:
+               return KEY_MENU;
+       case 0xa1:
+               return KEY_RIGHTSHIFT;
+       case 0xa0:
+               return KEY_LEFTSHIFT;
+       case 0xba:
+               return KEY_SEMICOLON;
+       case 0xbd:
+               return KEY_MINUS;
+       case 0xbc:
+               return KEY_COMMA;
+       case 0xbb:
+               return KEY_EQUAL;
+       case 0xbe:
+               return KEY_DOT;
+       case 0xbf:
+               return KEY_SLASH;
+       case 0xc0:
+               return KEY_GRAVE;
+       case 0xdb:
+               return KEY_LEFTBRACE;
+       case 0xdc:
+               return KEY_BACKSLASH;
+       case 0xdd:
+               return KEY_RIGHTBRACE;
+       case 0xde:
+               return KEY_APOSTROPHE;
+       case 0xff03:
+               return KEY_HOMEPAGE;
+       case 0xff04:
+               return KEY_TIME;
+       case 0xff06:
+               return KEY_OPEN;
+       case 0xff07:
+               return KEY_LIST;
+       case 0xff08:
+               return KEY_MAIL;
+       case 0xff30:
+               return KEY_CALC;
+       case 0xff1a: /* Map FN to ALT */
+               return KEY_LEFTALT;
+       case 0xff2f:
+               return KEY_INFO;
+       default:
+               printf("Unknown key %x\n", c);
+               return c;
+       }
+}
+
+struct celluon_state {
+       int len;        /* Expected length of current packet */
+       int count;      /* Number of bytes received */
+       int action;
+       int key;
+};
+
+static void celluon_decode(int fd, struct celluon_state *s, uint8_t c)
+{
+       if (s->count < 2 && c != 0xa5) {
+               /* Lost Sync */
+               s->count = 0;
+               return;
+       }
+
+       switch (s->count) {
+       case 0:
+               /* New packet - Reset state */
+               s->len = 30;
+               s->key = 0;
+               break;
+       case 1:
+               break;
+       case 6:
+               s->action = c;
+               break;
+       case 28:
+               s->key = c;
+               if (c == 0xff)
+                       s->len = 31;
+               break;
+       case 29:
+       case 30:
+               if (s->count == s->len - 1) {
+                       /* TODO: Verify checksum */
+                       if (s->action < 2) {
+                               send_event(fd, EV_KEY, celluon_xlate(s->key),
+                                                               s->action);
+                       }
+                       s->count = -1;
+               } else {
+                       s->key = (s->key << 8) | c;
+               }
+               break;
+       }
+
+       s->count++;
+
+       return;
+}
+
+int celluon_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel)
+{
+       unsigned char buf[16];
+       struct sigaction sa;
+       struct pollfd p;
+       sigset_t sigs;
+       char addr[18];
+       int i, fd, sk, len;
+       struct celluon_state s;
+
+       sk = rfcomm_connect(src, dst, channel);
+       if (sk < 0)
+               return -1;
+
+       fd = uinput_create("Celluon Keyboard", 1, 0);
+       if (fd < 0) {
+               close(sk);
+               return -1;
+       }
+
+       ba2str(dst, addr);
+
+       printf("Connected to %s on channel %d\n", addr, channel);
+       printf("Press CTRL-C for hangup\n");
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       sigfillset(&sigs);
+       sigdelset(&sigs, SIGCHLD);
+       sigdelset(&sigs, SIGPIPE);
+       sigdelset(&sigs, SIGTERM);
+       sigdelset(&sigs, SIGINT);
+       sigdelset(&sigs, SIGHUP);
+
+       p.fd = sk;
+       p.events = POLLIN | POLLERR | POLLHUP;
+
+       memset(&s, 0, sizeof(s));
+
+       while (!__io_canceled) {
+               p.revents = 0;
+               if (ppoll(&p, 1, NULL, &sigs) < 1)
+                       continue;
+
+               len = read(sk, buf, sizeof(buf));
+               if (len < 0)
+                       break;
+
+               for (i = 0; i < len; i++)
+                       celluon_decode(fd, &s, buf[i]);
+       }
+
+       printf("Disconnected\n");
+
+       ioctl(fd, UI_DEV_DESTROY);
+
+       close(fd);
+       close(sk);
+
+       return 0;
+}
diff --git a/compat/hidd.1 b/compat/hidd.1
new file mode 100644 (file)
index 0000000..b186ac2
--- /dev/null
@@ -0,0 +1,41 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.33.
+.TH HIDD "1" "May 2004" "hidd - Bluetooth HID daemon" "User Commands"
+.SH NAME
+hidd \- Bluetooth HID daemon
+.SH DESCRIPTION
+hidd - Bluetooth HID daemon
+.SS "Usage:"
+.IP
+hidd [options] [commands]
+.SH OPTIONS
+.TP
+\fB\-i\fR <hciX|bdaddr>
+Local HCI device or BD Address
+.TP
+\fB\-t\fR <timeout>
+Set idle timeout (in minutes)
+.TP
+\fB\-n\fR, \fB\-\-nodaemon\fR
+Don't fork daemon to background
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display help
+.SS "Commands:"
+.TP
+\fB\-\-server\fR
+Start HID server
+.TP
+\fB\-\-search\fR
+Search for HID devices
+.TP
+\fB\-\-connect\fR <bdaddr>
+Connect remote HID device
+.TP
+\fB\-\-kill\fR <bdaddr>
+Terminate HID connection
+.TP
+\fB\-\-killall\fR
+Terminate all connections
+.TP
+\fB\-\-show\fR
+List current HID connections
diff --git a/compat/hidd.c b/compat/hidd.c
new file mode 100644 (file)
index 0000000..f8a0dfd
--- /dev/null
@@ -0,0 +1,848 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/hidp.h>
+
+#include "sdp.h"
+#include "hidd.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+enum {
+       NONE,
+       SHOW,
+       SERVER,
+       SEARCH,
+       CONNECT,
+       KILL
+};
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+}
+
+static void sig_term(int sig)
+{
+       __io_canceled = 1;
+}
+
+static int l2cap_connect(bdaddr_t *src, bdaddr_t *dst, unsigned short psm)
+{
+       struct sockaddr_l2 addr;
+       struct l2cap_options opts;
+       int sk;
+
+       if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0)
+               return -1;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family  = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, src);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       memset(&opts, 0, sizeof(opts));
+       opts.imtu = HIDP_DEFAULT_MTU;
+       opts.omtu = HIDP_DEFAULT_MTU;
+       opts.flush_to = 0xffff;
+
+       setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family  = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, dst);
+       addr.l2_psm = htobs(psm);
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       return sk;
+}
+
+static int l2cap_listen(const bdaddr_t *bdaddr, unsigned short psm, int lm, int backlog)
+{
+       struct sockaddr_l2 addr;
+       struct l2cap_options opts;
+       int sk;
+
+       if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0)
+               return -1;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, bdaddr);
+       addr.l2_psm = htobs(psm);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm));
+
+       memset(&opts, 0, sizeof(opts));
+       opts.imtu = HIDP_DEFAULT_MTU;
+       opts.omtu = HIDP_DEFAULT_MTU;
+       opts.flush_to = 0xffff;
+
+       setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts));
+
+       if (listen(sk, backlog) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       return sk;
+}
+
+static int l2cap_accept(int sk, bdaddr_t *bdaddr)
+{
+       struct sockaddr_l2 addr;
+       socklen_t addrlen;
+       int nsk;
+
+       memset(&addr, 0, sizeof(addr));
+       addrlen = sizeof(addr);
+
+       if ((nsk = accept(sk, (struct sockaddr *) &addr, &addrlen)) < 0)
+               return -1;
+
+       if (bdaddr)
+               bacpy(bdaddr, &addr.l2_bdaddr);
+
+       return nsk;
+}
+
+static int request_authentication(bdaddr_t *src, bdaddr_t *dst)
+{
+       struct hci_conn_info_req *cr;
+       char addr[18];
+       int err, dd, dev_id;
+
+       ba2str(src, addr);
+       dev_id = hci_devid(addr);
+       if (dev_id < 0)
+               return dev_id;
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0)
+               return dd;
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr)
+               return -ENOMEM;
+
+       bacpy(&cr->bdaddr, dst);
+       cr->type = ACL_LINK;
+       err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr);
+       if (err < 0) {
+               free(cr);
+               hci_close_dev(dd);
+               return err;
+       }
+
+       err = hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000);
+
+       free(cr);
+       hci_close_dev(dd);
+
+       return err;
+}
+
+static int request_encryption(bdaddr_t *src, bdaddr_t *dst)
+{
+       struct hci_conn_info_req *cr;
+       char addr[18];
+       int err, dd, dev_id;
+
+       ba2str(src, addr);
+       dev_id = hci_devid(addr);
+       if (dev_id < 0)
+               return dev_id;
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0)
+               return dd;
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr)
+               return -ENOMEM;
+
+       bacpy(&cr->bdaddr, dst);
+       cr->type = ACL_LINK;
+       err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr);
+       if (err < 0) {
+               free(cr);
+               hci_close_dev(dd);
+               return err;
+       }
+
+       err = hci_encrypt_link(dd, htobs(cr->conn_info->handle), 1, 25000);
+
+       free(cr);
+       hci_close_dev(dd);
+
+       return err;
+}
+
+static int create_device(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout)
+{
+       struct hidp_connadd_req req;
+       struct sockaddr_l2 addr;
+       socklen_t addrlen;
+       bdaddr_t src, dst;
+       char bda[18];
+       int err;
+
+       memset(&addr, 0, sizeof(addr));
+       addrlen = sizeof(addr);
+
+       if (getsockname(csk, (struct sockaddr *) &addr, &addrlen) < 0)
+               return -1;
+
+       bacpy(&src, &addr.l2_bdaddr);
+
+       memset(&addr, 0, sizeof(addr));
+       addrlen = sizeof(addr);
+
+       if (getpeername(csk, (struct sockaddr *) &addr, &addrlen) < 0)
+               return -1;
+
+       bacpy(&dst, &addr.l2_bdaddr);
+
+       memset(&req, 0, sizeof(req));
+       req.ctrl_sock = csk;
+       req.intr_sock = isk;
+       req.flags     = 0;
+       req.idle_to   = timeout * 60;
+
+       err = get_stored_device_info(&src, &dst, &req);
+       if (!err)
+               goto create;
+
+       if (!nocheck) {
+               ba2str(&dst, bda);
+               syslog(LOG_ERR, "Rejected connection from unknown device %s", bda);
+               /* Return no error to avoid run_server() complaining too */
+               return 0;
+       }
+
+       if (!nosdp) {
+               err = get_sdp_device_info(&src, &dst, &req);
+               if (err < 0)
+                       goto error;
+       } else {
+               struct l2cap_conninfo conn;
+               socklen_t size;
+               uint8_t class[3];
+
+               memset(&conn, 0, sizeof(conn));
+               size = sizeof(conn);
+               if (getsockopt(csk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &size) < 0)
+                       memset(class, 0, 3);
+               else
+                       memcpy(class, conn.dev_class, 3);
+
+               if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01))
+                       req.subclass = class[0];
+               else
+                       req.subclass = 0xc0;
+       }
+
+create:
+       if (subclass != 0x00)
+               req.subclass = subclass;
+
+       ba2str(&dst, bda);
+       syslog(LOG_INFO, "New HID device %s (%s)", bda, req.name);
+
+       if (encrypt && (req.subclass & 0x40)) {
+               err = request_authentication(&src, &dst);
+               if (err < 0) {
+                       syslog(LOG_ERR, "Authentication for %s failed", bda);
+                       goto error;
+               }
+
+               err = request_encryption(&src, &dst);
+               if (err < 0)
+                       syslog(LOG_ERR, "Encryption for %s failed", bda);
+       }
+
+       if (bootonly) {
+               req.rd_size = 0;
+               req.flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
+       }
+
+       err = ioctl(ctl, HIDPCONNADD, &req);
+
+error:
+       free(req.rd_data);
+
+       return err;
+}
+
+static void run_server(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout)
+{
+       struct pollfd p[2];
+       sigset_t sigs;
+       short events;
+       int err, ncsk, nisk;
+
+       sigfillset(&sigs);
+       sigdelset(&sigs, SIGCHLD);
+       sigdelset(&sigs, SIGPIPE);
+       sigdelset(&sigs, SIGTERM);
+       sigdelset(&sigs, SIGINT);
+       sigdelset(&sigs, SIGHUP);
+
+       p[0].fd = csk;
+       p[0].events = POLLIN | POLLERR | POLLHUP;
+
+       p[1].fd = isk;
+       p[1].events = POLLIN | POLLERR | POLLHUP;
+
+       while (!__io_canceled) {
+               p[0].revents = 0;
+               p[1].revents = 0;
+
+               if (ppoll(p, 2, NULL, &sigs) < 1)
+                       continue;
+
+               events = p[0].revents | p[1].revents;
+
+               if (events & POLLIN) {
+                       ncsk = l2cap_accept(csk, NULL);
+                       nisk = l2cap_accept(isk, NULL);
+
+                       err = create_device(ctl, ncsk, nisk, subclass, nosdp, nocheck, bootonly, encrypt, timeout);
+                       if (err < 0)
+                               syslog(LOG_ERR, "HID create error %d (%s)",
+                                               errno, strerror(errno));
+
+                       close(nisk);
+                       sleep(1);
+                       close(ncsk);
+               }
+       }
+}
+
+static char *hidp_state[] = {
+       "unknown",
+       "connected",
+       "open",
+       "bound",
+       "listening",
+       "connecting",
+       "connecting",
+       "config",
+       "disconnecting",
+       "closed"
+};
+
+static char *hidp_flagstostr(uint32_t flags)
+{
+       static char str[100];
+       str[0] = 0;
+
+       strcat(str, "[");
+
+       if (flags & (1 << HIDP_BOOT_PROTOCOL_MODE))
+               strcat(str, "boot-protocol");
+
+       strcat(str, "]");
+
+       return str;
+}
+
+static void do_show(int ctl)
+{
+       struct hidp_connlist_req req;
+       struct hidp_conninfo ci[16];
+       char addr[18];
+       unsigned int i;
+
+       req.cnum = 16;
+       req.ci   = ci;
+
+       if (ioctl(ctl, HIDPGETCONNLIST, &req) < 0) {
+               perror("Can't get connection list");
+               close(ctl);
+               exit(1);
+       }
+
+       for (i = 0; i < req.cnum; i++) {
+               ba2str(&ci[i].bdaddr, addr);
+               printf("%s %s [%04x:%04x] %s %s\n", addr, ci[i].name,
+                       ci[i].vendor, ci[i].product, hidp_state[ci[i].state],
+                       ci[i].flags ? hidp_flagstostr(ci[i].flags) : "");
+       }
+}
+
+static void do_connect(int ctl, bdaddr_t *src, bdaddr_t *dst, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout)
+{
+       struct hidp_connadd_req req;
+       uint16_t uuid = HID_SVCLASS_ID;
+       uint8_t channel = 0;
+       char name[256];
+       int csk, isk, err;
+
+       memset(&req, 0, sizeof(req));
+       name[0] = '\0';
+
+       err = get_sdp_device_info(src, dst, &req);
+       if (err < 0 && fakehid)
+               err = get_alternate_device_info(src, dst,
+                               &uuid, &channel, name, sizeof(name) - 1);
+
+       if (err < 0) {
+               perror("Can't get device information");
+               close(ctl);
+               exit(1);
+       }
+
+       switch (uuid) {
+       case HID_SVCLASS_ID:
+               goto connect;
+
+       case SERIAL_PORT_SVCLASS_ID:
+               if (subclass == 0x40 || !strcmp(name, "Cable Replacement")) {
+                       if (epox_presenter(src, dst, channel) < 0) {
+                               close(ctl);
+                               exit(1);
+                       }
+                       break;
+               }
+               if (subclass == 0x1f || !strcmp(name, "SPP slave")) {
+                       if (jthree_keyboard(src, dst, channel) < 0) {
+                               close(ctl);
+                               exit(1);
+                       }
+                       break;
+               }
+               if (subclass == 0x02 || !strcmp(name, "Serial Port")) {
+                       if (celluon_keyboard(src, dst, channel) < 0) {
+                               close(ctl);
+                               exit(1);
+                       }
+                       break;
+               }
+               break;
+
+       case HEADSET_SVCLASS_ID:
+       case HANDSFREE_SVCLASS_ID:
+               if (headset_presenter(src, dst, channel) < 0) {
+                       close(ctl);
+                       exit(1);
+               }
+               break;
+       }
+
+       return;
+
+connect:
+       csk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_CTRL);
+       if (csk < 0) {
+               perror("Can't create HID control channel");
+               close(ctl);
+               exit(1);
+       }
+
+       isk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_INTR);
+       if (isk < 0) {
+               perror("Can't create HID interrupt channel");
+               close(csk);
+               close(ctl);
+               exit(1);
+       }
+
+       err = create_device(ctl, csk, isk, subclass, 1, 1, bootonly, encrypt, timeout);
+       if (err < 0) {
+               fprintf(stderr, "HID create error %d (%s)\n",
+                                               errno, strerror(errno));
+               close(isk);
+               sleep(1);
+               close(csk);
+               close(ctl);
+               exit(1);
+       }
+}
+
+static void do_search(int ctl, bdaddr_t *bdaddr, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout)
+{
+       inquiry_info *info = NULL;
+       bdaddr_t src, dst;
+       int i, dev_id, num_rsp, length, flags;
+       char addr[18];
+       uint8_t class[3];
+
+       ba2str(bdaddr, addr);
+       dev_id = hci_devid(addr);
+       if (dev_id < 0) {
+               dev_id = hci_get_route(NULL);
+               hci_devba(dev_id, &src);
+       } else
+               bacpy(&src, bdaddr);
+
+       length  = 8;    /* ~10 seconds */
+       num_rsp = 0;
+       flags   = IREQ_CACHE_FLUSH;
+
+       printf("Searching ...\n");
+
+       num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags);
+
+       for (i = 0; i < num_rsp; i++) {
+               memcpy(class, (info+i)->dev_class, 3);
+               if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01)) {
+                       bacpy(&dst, &(info+i)->bdaddr);
+                       ba2str(&dst, addr);
+
+                       printf("\tConnecting to device %s\n", addr);
+                       do_connect(ctl, &src, &dst, subclass, fakehid, bootonly, encrypt, timeout);
+               }
+       }
+
+       if (!fakehid)
+               goto done;
+
+       for (i = 0; i < num_rsp; i++) {
+               memcpy(class, (info+i)->dev_class, 3);
+               if ((class[0] == 0x00 && class[2] == 0x00 &&
+                               (class[1] == 0x40 || class[1] == 0x1f)) ||
+                               (class[0] == 0x10 && class[1] == 0x02 && class[2] == 0x40)) {
+                       bacpy(&dst, &(info+i)->bdaddr);
+                       ba2str(&dst, addr);
+
+                       printf("\tConnecting to device %s\n", addr);
+                       do_connect(ctl, &src, &dst, subclass, 1, bootonly, 0, timeout);
+               }
+       }
+
+done:
+       bt_free(info);
+
+       if (!num_rsp) {
+               fprintf(stderr, "\tNo devices in range or visible\n");
+               close(ctl);
+               exit(1);
+       }
+}
+
+static void do_kill(int ctl, bdaddr_t *bdaddr, uint32_t flags)
+{
+       struct hidp_conndel_req req;
+       struct hidp_connlist_req cl;
+       struct hidp_conninfo ci[16];
+       unsigned int i;
+
+       if (!bacmp(bdaddr, BDADDR_ALL)) {
+               cl.cnum = 16;
+               cl.ci   = ci;
+
+               if (ioctl(ctl, HIDPGETCONNLIST, &cl) < 0) {
+                       perror("Can't get connection list");
+                       close(ctl);
+                       exit(1);
+               }
+
+               for (i = 0; i < cl.cnum; i++) {
+                       bacpy(&req.bdaddr, &ci[i].bdaddr);
+                       req.flags = flags;
+
+                       if (ioctl(ctl, HIDPCONNDEL, &req) < 0) {
+                               perror("Can't release connection");
+                               close(ctl);
+                               exit(1);
+                       }
+               }
+
+       } else {
+               bacpy(&req.bdaddr, bdaddr);
+               req.flags = flags;
+
+               if (ioctl(ctl, HIDPCONNDEL, &req) < 0) {
+                       perror("Can't release connection");
+                       close(ctl);
+                       exit(1);
+               }
+       }
+}
+
+static void usage(void)
+{
+       printf("hidd - Bluetooth HID daemon version %s\n\n", VERSION);
+
+       printf("Usage:\n"
+               "\thidd [options] [commands]\n"
+               "\n");
+
+       printf("Options:\n"
+               "\t-i <hciX|bdaddr>     Local HCI device or BD Address\n"
+               "\t-t <timeout>         Set idle timeout (in minutes)\n"
+               "\t-b <subclass>        Overwrite the boot mode subclass\n"
+               "\t-n, --nodaemon       Don't fork daemon to background\n"
+               "\t-h, --help           Display help\n"
+               "\n");
+
+       printf("Commands:\n"
+               "\t--server             Start HID server\n"
+               "\t--search             Search for HID devices\n"
+               "\t--connect <bdaddr>   Connect remote HID device\n"
+               "\t--unplug <bdaddr>    Unplug the HID connection\n"
+               "\t--kill <bdaddr>      Terminate HID connection\n"
+               "\t--killall            Terminate all connections\n"
+               "\t--show               List current HID connections\n"
+               "\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "nodaemon",   0, 0, 'n' },
+       { "subclass",   1, 0, 'b' },
+       { "timeout",    1, 0, 't' },
+       { "device",     1, 0, 'i' },
+       { "master",     0, 0, 'M' },
+       { "encrypt",    0, 0, 'E' },
+       { "nosdp",      0, 0, 'D' },
+       { "nocheck",    0, 0, 'Z' },
+       { "bootonly",   0, 0, 'B' },
+       { "hidonly",    0, 0, 'H' },
+       { "show",       0, 0, 'l' },
+       { "list",       0, 0, 'l' },
+       { "server",     0, 0, 'd' },
+       { "listen",     0, 0, 'd' },
+       { "search",     0, 0, 's' },
+       { "create",     1, 0, 'c' },
+       { "connect",    1, 0, 'c' },
+       { "disconnect", 1, 0, 'k' },
+       { "terminate",  1, 0, 'k' },
+       { "release",    1, 0, 'k' },
+       { "kill",       1, 0, 'k' },
+       { "killall",    0, 0, 'K' },
+       { "unplug",     1, 0, 'u' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       struct sigaction sa;
+       bdaddr_t bdaddr, dev;
+       uint32_t flags = 0;
+       uint8_t subclass = 0x00;
+       char addr[18];
+       int log_option = LOG_NDELAY | LOG_PID;
+       int opt, ctl, csk, isk;
+       int mode = SHOW, detach = 1, nosdp = 0, nocheck = 0, bootonly = 0;
+       int fakehid = 1, encrypt = 0, timeout = 30, lm = 0;
+
+       bacpy(&bdaddr, BDADDR_ANY);
+
+       while ((opt = getopt_long(argc, argv, "+i:nt:b:MEDZBHldsc:k:Ku:h", main_options, NULL)) != -1) {
+               switch(opt) {
+               case 'i':
+                       if (!strncasecmp(optarg, "hci", 3))
+                               hci_devba(atoi(optarg + 3), &bdaddr);
+                       else
+                               str2ba(optarg, &bdaddr);
+                       break;
+               case 'n':
+                       detach = 0;
+                       break;
+               case 't':
+                       timeout = atoi(optarg);
+                       break;
+               case 'b':
+                       if (!strncasecmp(optarg, "0x", 2))
+                               subclass = (uint8_t) strtol(optarg, NULL, 16);
+                       else
+                               subclass = atoi(optarg);
+                       break;
+               case 'M':
+                       lm |= L2CAP_LM_MASTER;
+                       break;
+               case 'E':
+                       encrypt = 1;
+                       break;
+               case 'D':
+                       nosdp = 1;
+                       break;
+               case 'Z':
+                       nocheck = 1;
+                       break;
+               case 'B':
+                       bootonly = 1;
+                       break;
+               case 'H':
+                       fakehid = 0;
+                       break;
+               case 'l':
+                       mode = SHOW;
+                       break;
+               case 'd':
+                       mode = SERVER;
+                       break;
+               case 's':
+                       mode = SEARCH;
+                       break;
+               case 'c':
+                       str2ba(optarg, &dev);
+                       mode = CONNECT;
+                       break;
+               case 'k':
+                       str2ba(optarg, &dev);
+                       mode = KILL;
+                       break;
+               case 'K':
+                       bacpy(&dev, BDADDR_ALL);
+                       mode = KILL;
+                       break;
+               case 'u':
+                       str2ba(optarg, &dev);
+                       flags = (1 << HIDP_VIRTUAL_CABLE_UNPLUG);
+                       mode = KILL;
+                       break;
+               case 'h':
+                       usage();
+                       exit(0);
+               default:
+                       exit(0);
+               }
+       }
+
+       ba2str(&bdaddr, addr);
+
+       ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+       if (ctl < 0) {
+               perror("Can't open HIDP control socket");
+               exit(1);
+       }
+
+       switch (mode) {
+       case SERVER:
+               csk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_CTRL, lm, 10);
+               if (csk < 0) {
+                       perror("Can't listen on HID control channel");
+                       close(ctl);
+                       exit(1);
+               }
+
+               isk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_INTR, lm, 10);
+               if (isk < 0) {
+                       perror("Can't listen on HID interrupt channel");
+                       close(ctl);
+                       close(csk);
+                       exit(1);
+               }
+               break;
+
+       case SEARCH:
+               do_search(ctl, &bdaddr, subclass, fakehid, bootonly, encrypt, timeout);
+               close(ctl);
+               exit(0);
+
+       case CONNECT:
+               do_connect(ctl, &bdaddr, &dev, subclass, fakehid, bootonly, encrypt, timeout);
+               close(ctl);
+               exit(0);
+
+       case KILL:
+               do_kill(ctl, &dev, flags);
+               close(ctl);
+               exit(0);
+
+       default:
+               do_show(ctl);
+               close(ctl);
+               exit(0);
+       }
+
+        if (detach) {
+               if (daemon(0, 0)) {
+                       perror("Can't start daemon");
+                       exit(1);
+               }
+       } else
+               log_option |= LOG_PERROR;
+
+       openlog("hidd", log_option, LOG_DAEMON);
+
+       if (bacmp(&bdaddr, BDADDR_ANY))
+               syslog(LOG_INFO, "Bluetooth HID daemon (%s)", addr);
+       else
+               syslog(LOG_INFO, "Bluetooth HID daemon");
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags = SA_NOCLDSTOP;
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       run_server(ctl, csk, isk, subclass, nosdp, nocheck, bootonly, encrypt, timeout);
+
+       syslog(LOG_INFO, "Exit");
+
+       close(csk);
+       close(isk);
+       close(ctl);
+
+       return 0;
+}
diff --git a/compat/hidd.h b/compat/hidd.h
new file mode 100644 (file)
index 0000000..0536967
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define L2CAP_PSM_HIDP_CTRL 0x11
+#define L2CAP_PSM_HIDP_INTR 0x13
+
+int epox_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
+int headset_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
+int jthree_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
+int celluon_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel);
diff --git a/compat/lib.h b/compat/lib.h
new file mode 100644 (file)
index 0000000..3b3aeb5
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifndef min
+#define min(a,b)    ( (a)<(b) ? (a):(b) )
+#endif
+
+/* IO cancelation */
+extern volatile sig_atomic_t __io_canceled;
+
+static inline void io_init(void)
+{
+       __io_canceled = 0;
+}
+
+static inline void io_cancel(void)
+{
+       __io_canceled = 1;
+}
+
+/* Read exactly len bytes (Signal safe)*/
+static inline int read_n(int fd, char *buf, int len)
+{
+       register int t = 0, w;
+
+       while (!__io_canceled && len > 0) {
+               if ((w = read(fd, buf, len)) < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       return -1;
+               }
+               if (!w)
+                       return 0;
+               len -= w;
+               buf += w;
+               t += w;
+       }
+
+       return t;
+}
+
+/* Write exactly len bytes (Signal safe)*/
+static inline int write_n(int fd, char *buf, int len)
+{
+       register int t = 0, w;
+
+       while (!__io_canceled && len > 0) {
+               if ((w = write(fd, buf, len)) < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       return -1;
+               }
+               if (!w)
+                       return 0;
+               len -= w;
+               buf += w;
+               t += w;
+       }
+
+       return t;
+}
diff --git a/compat/msdun.c b/compat/msdun.c
new file mode 100644 (file)
index 0000000..ae88c0c
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include "lib.h"
+#include "dund.h"
+
+#define MS_PPP      2
+#define MS_SUCCESS  1
+#define MS_FAILED  -1
+#define MS_TIMEOUT -2
+
+static sigjmp_buf jmp;
+static int        retry;
+static int        timeout;
+
+static void sig_alarm(int sig)
+{
+       siglongjmp(jmp, MS_TIMEOUT);
+}
+
+static int w4_str(int fd, char *str)
+{
+       char buf[40];
+       unsigned len = 0;
+       int r;
+
+       while (1) {
+               r = read(fd, buf + len, sizeof(buf) - len - 1);
+               if (r < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       break;
+               }
+               if (!r)
+                       break;
+
+               len += r;
+
+               if (len < strlen(str))
+                       continue;
+               buf[len] = 0;
+
+               if (strstr(buf, str))
+                       return MS_SUCCESS;
+
+               /* Detect PPP */
+               if (strchr(buf, '~'))
+                       return MS_PPP;
+       }
+       return MS_FAILED;
+}
+
+static int ms_server(int fd)
+{
+       switch (w4_str(fd, "CLIENT")) {
+       case MS_SUCCESS:
+               write_n(fd, "CLIENTSERVER", 12);
+       case MS_PPP:
+               return MS_SUCCESS;
+       default:
+               return MS_FAILED;
+       }
+}
+
+static int ms_client(int fd)
+{
+       write_n(fd, "CLIENT", 6);
+       return w4_str(fd, "CLIENTSERVER");
+}
+
+int ms_dun(int fd, int server, int timeo)
+{
+       sig_t osig;
+
+       retry    = 4;
+       timeout  = timeo;
+
+       if (!server)
+               timeout /= retry;
+
+       osig = signal(SIGALRM, sig_alarm);
+
+       while (1) {
+               int r = sigsetjmp(jmp, 1);
+               if (r) {
+                       if (r == MS_TIMEOUT && !server && --retry)
+                               continue;
+
+                       alarm(0);
+                       signal(SIGALRM, osig);
+
+                       switch (r) {
+                       case MS_SUCCESS:
+                       case MS_PPP:
+                               errno = 0;
+                               return 0;
+
+                       case MS_FAILED:
+                               errno = EPROTO;
+                               break;
+
+                       case MS_TIMEOUT:
+                               errno = ETIMEDOUT;
+                               break;
+                       }
+                       return -1;
+               }
+
+               alarm(timeout);
+
+               if (server)
+                       r = ms_server(fd);
+               else
+                       r = ms_client(fd);
+
+               siglongjmp(jmp, r);
+       }
+}
diff --git a/compat/pand.1 b/compat/pand.1
new file mode 100644 (file)
index 0000000..4603b8b
--- /dev/null
@@ -0,0 +1,77 @@
+.\" DO NOT MODIFY THIS FILE!  It was generated by help2man 1.29.
+.TH BlueZ "1" "February 2003" "PAN daemon" "User Commands"
+.SH NAME
+pand \- BlueZ Bluetooth PAN daemon
+.SH DESCRIPTION
+The pand PAN daemon allows your computer to connect to ethernet
+networks using Bluetooth.
+.SH SYNPOSIS
+pand <options>
+.SH OPTIONS
+.TP
+\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR
+Show active PAN connections
+.TP
+\fB\-\-listen\fR \fB\-s\fR
+Listen for PAN connections
+.TP
+\fB\-\-connect\fR \fB\-c\fR <bdaddr>
+Create PAN connection
+.TP
+\fB\-\-search\fR \fB\-Q[duration]\fR
+Search and connect
+.TP
+\fB\-\-kill\fR \fB\-k\fR <bdaddr>
+Kill PAN connection
+.TP
+\fB\-\-killall\fR \fB\-K\fR
+Kill all PAN connections
+.TP
+\fB\-\-role\fR \fB\-r\fR <role>
+Local PAN role (PANU, NAP, GN)
+.TP
+\fB\-\-service\fR \fB\-d\fR <role>
+Remote PAN service (PANU, NAP, GN)
+.TP
+\fB\-\-ethernet\fR \fB\-e\fR <name>
+Network interface name
+.TP
+\fB\-\-device\fR \fB\-i\fR <bdaddr>
+Source bdaddr
+.TP
+\fB\-\-nosdp\fR \fB\-D\fR
+Disable SDP
+.TP
+\fB\-\-encrypt\fR \fB\-E\fR
+Enable encryption
+.TP
+\fB\-\-secure\fR \fB\-S\fR
+Secure connection
+.TP
+\fB\-\-master\fR \fB\-M\fR
+Become the master of a piconet
+.TP
+\fB\-\-nodetach\fR \fB\-n\fR
+Do not become a daemon
+.TP
+\fB\-\-persist\fR \fB\-p[interval]\fR
+Persist mode
+.TP
+\fB\-\-cache\fR \fB\-C[valid]\fR
+Cache addresses
+.TP
+\fB\-\-pidfile\fR \fB\-P <pidfile>\fR
+Create PID file
+.TP
+\fB\-\-devup\fR \fB\-u <script>\fR
+Script to run when interface comes up
+.TP
+\fB\-\-devdown\fR \fB\-o <script>\fR
+Script to run when interface comes down
+.TP
+\fB\-\-autozap\fR \fB\-z\fR
+Disconnect automatically on exit
+
+.SH SCRIPTS
+The devup/devdown script will be called with bluetooth device as first argument
+and bluetooth destination address as second argument.
diff --git a/compat/pand.c b/compat/pand.c
new file mode 100644 (file)
index 0000000..b82650e
--- /dev/null
@@ -0,0 +1,811 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/hidp.h>
+
+#include "sdp.h"
+#include "pand.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static uint16_t role    = BNEP_SVC_PANU;       /* Local role (ie service) */
+static uint16_t service = BNEP_SVC_NAP;                /* Remote service */
+
+static int detach = 1;
+static int persist;
+static int use_sdp = 1;
+static int use_cache;
+static int link_mode = 0;
+static int cleanup;
+static int search_duration = 10;
+
+static struct {
+       int      valid;
+       char     dst[40];
+       bdaddr_t bdaddr;
+} cache;
+
+static char netdev[16] = "bnep%d";
+static char *pidfile = NULL;
+static char *devupcmd = NULL;
+static char *devdowncmd = NULL;
+
+static bdaddr_t src_addr = *BDADDR_ANY;
+static int src_dev = -1;
+
+static volatile int terminate;
+
+static void do_kill(char *dst);
+
+enum {
+       NONE,
+       SHOW,
+       LISTEN,
+       CONNECT,
+       KILL
+} modes;
+
+struct script_arg {
+       char    dev[20];
+       char    dst[20];
+       int     sk;
+       int     nsk;
+};
+
+static void run_script(char *script, char *dev, char *dst, int sk, int nsk)
+{
+       char *argv[4];
+       struct sigaction sa;
+
+       if (!script)
+               return;
+
+       if (access(script, R_OK | X_OK))
+               return;
+
+       if (fork())
+               return;
+
+       if (sk >= 0)
+               close(sk);
+
+       if (nsk >= 0)
+               close(nsk);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = SIG_DFL;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       argv[0] = script;
+       argv[1] = dev;
+       argv[2] = dst;
+       argv[3] = NULL;
+
+       execv(script, argv);
+
+       exit(1);
+}
+
+/* Wait for disconnect or error condition on the socket */
+static int w4_hup(int sk, struct script_arg *down_cmd)
+{
+       struct pollfd pf;
+       sigset_t sigs;
+       int n;
+
+       sigfillset(&sigs);
+       sigdelset(&sigs, SIGCHLD);
+       sigdelset(&sigs, SIGPIPE);
+       sigdelset(&sigs, SIGTERM);
+       sigdelset(&sigs, SIGINT);
+       sigdelset(&sigs, SIGHUP);
+
+       while (!terminate) {
+               pf.fd = sk;
+               pf.events = POLLERR | POLLHUP;
+
+               n = ppoll(&pf, 1, NULL, &sigs);
+
+               if (n < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+
+                       syslog(LOG_ERR, "Poll failed. %s(%d)",
+                                               strerror(errno), errno);
+
+                       return 1;
+               }
+
+               if (n) {
+                       int err = 0;
+                       socklen_t olen = sizeof(err);
+
+                       getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &olen);
+
+                       syslog(LOG_INFO, "%s disconnected%s%s", netdev,
+                               err ? " : " : "", err ? strerror(err) : "");
+
+                       if (down_cmd)
+                               run_script(devdowncmd,
+                                               down_cmd->dev, down_cmd->dst,
+                                               down_cmd->sk, down_cmd->nsk);
+
+                       close(sk);
+
+                       return 0;
+               }
+       }
+
+       return 0;
+}
+
+static int do_listen(void)
+{
+       struct l2cap_options l2o;
+       struct sockaddr_l2 l2a;
+       socklen_t olen;
+       int sk, lm;
+
+       if (use_sdp)
+               bnep_sdp_register(&src_addr, role);
+
+       /* Create L2CAP socket and bind it to PSM BNEP */
+       sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+       if (sk < 0) {
+               syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)",
+                                               strerror(errno), errno);
+               return -1;
+       }
+
+       memset(&l2a, 0, sizeof(l2a));
+       l2a.l2_family = AF_BLUETOOTH;
+       bacpy(&l2a.l2_bdaddr, &src_addr);
+       l2a.l2_psm = htobs(BNEP_PSM);
+
+       if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a))) {
+               syslog(LOG_ERR, "Bind failed. %s(%d)",
+                                               strerror(errno), errno);
+               return -1;
+       }
+
+       /* Setup L2CAP options according to BNEP spec */
+       memset(&l2o, 0, sizeof(l2o));
+       olen = sizeof(l2o);
+       if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen) < 0) {
+               syslog(LOG_ERR, "Failed to get L2CAP options. %s(%d)",
+                                               strerror(errno), errno);
+               return -1;
+       }
+
+       l2o.imtu = l2o.omtu = BNEP_MTU;
+       if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) {
+               syslog(LOG_ERR, "Failed to set L2CAP options. %s(%d)",
+                                               strerror(errno), errno);
+               return -1;
+       }
+
+       /* Set link mode */
+       lm = link_mode;
+       if (lm && setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)) < 0) {
+               syslog(LOG_ERR, "Failed to set link mode. %s(%d)",
+                                               strerror(errno), errno);
+               return -1;
+       }
+
+       listen(sk, 10);
+
+       while (!terminate) {
+               socklen_t alen = sizeof(l2a);
+               char devname[16];
+               int nsk;
+
+               nsk = accept(sk, (struct sockaddr *) &l2a, &alen);
+               if (nsk < 0) {
+                       syslog(LOG_ERR, "Accept failed. %s(%d)",
+                                               strerror(errno), errno);
+                       continue;
+               }
+
+               switch (fork()) {
+               case 0:
+                       break;
+               case -1:
+                       syslog(LOG_ERR, "Fork failed. %s(%d)",
+                                               strerror(errno), errno);
+               default:
+                       close(nsk);
+                       continue;
+               }
+
+               strncpy(devname, netdev, 16);
+               devname[15] = '\0';
+
+               if (!bnep_accept_connection(nsk, role, devname)) {
+                       char str[40];
+                       struct script_arg down_cmd;
+
+                       ba2str(&l2a.l2_bdaddr, str);
+
+                       syslog(LOG_INFO, "New connection from %s at %s",
+                                                               str, devname);
+
+                       run_script(devupcmd, devname, str, sk, nsk);
+
+                       memset(&down_cmd, 0, sizeof(struct script_arg));
+                       strncpy(down_cmd.dev, devname, strlen(devname) + 1);
+                       strncpy(down_cmd.dst, str, strlen(str) + 1);
+                       down_cmd.sk = sk;
+                       down_cmd.nsk = nsk;
+                       w4_hup(nsk, &down_cmd);
+               } else {
+                       syslog(LOG_ERR, "Connection failed. %s(%d)",
+                                               strerror(errno), errno);
+               }
+
+               close(nsk);
+               exit(0);
+       }
+
+       if (use_sdp)
+               bnep_sdp_unregister();
+
+       return 0;
+}
+
+/* Connect and initiate BNEP session
+ * Returns:
+ *   -1 - critical error (exit persist mode)
+ *   1  - non critical error
+ *   0  - success
+ */
+static int create_connection(char *dst, bdaddr_t *bdaddr)
+{
+       struct l2cap_options l2o;
+       struct sockaddr_l2 l2a;
+       socklen_t olen;
+       int sk, r = 0;
+       struct script_arg down_cmd;
+
+       syslog(LOG_INFO, "Connecting to %s", dst);
+
+       sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+       if (sk < 0) {
+               syslog(LOG_ERR, "Cannot create L2CAP socket. %s(%d)",
+                                               strerror(errno), errno);
+               return -1;
+       }
+
+       /* Setup L2CAP options according to BNEP spec */
+       memset(&l2o, 0, sizeof(l2o));
+       olen = sizeof(l2o);
+       getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &olen);
+       l2o.imtu = l2o.omtu = BNEP_MTU;
+       setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o));
+
+       memset(&l2a, 0, sizeof(l2a));
+       l2a.l2_family = AF_BLUETOOTH;
+       bacpy(&l2a.l2_bdaddr, &src_addr);
+
+       if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)))
+               syslog(LOG_ERR, "Bind failed. %s(%d)",
+                                               strerror(errno), errno);
+
+       memset(&l2a, 0, sizeof(l2a));
+       l2a.l2_family = AF_BLUETOOTH;
+       bacpy(&l2a.l2_bdaddr, bdaddr);
+       l2a.l2_psm = htobs(BNEP_PSM);
+
+       if (!connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) &&
+                       !bnep_create_connection(sk, role, service, netdev)) {
+
+               syslog(LOG_INFO, "%s connected", netdev);
+
+               run_script(devupcmd, netdev, dst, sk, -1);
+
+               if (persist || devdowncmd) {
+                               memset(&down_cmd, 0, sizeof(struct script_arg));
+                               strncpy(down_cmd.dev, netdev, strlen(netdev) + 1);
+                               strncpy(down_cmd.dst, dst, strlen(dst) + 1);
+                               down_cmd.sk = sk;
+                               down_cmd.nsk = -1;
+                               w4_hup(sk, &down_cmd);
+
+                       if (terminate && cleanup) {
+                               syslog(LOG_INFO, "Disconnecting from %s.", dst);
+                               do_kill(dst);
+                       }
+               }
+
+               r = 0;
+       } else {
+               syslog(LOG_ERR, "Connect to %s failed. %s(%d)",
+                                               dst, strerror(errno), errno);
+               r = 1;
+       }
+
+       close(sk);
+
+       if (use_cache) {
+               if (!r) {
+                       /* Succesesful connection, validate cache */
+                       strcpy(cache.dst, dst);
+                       bacpy(&cache.bdaddr, bdaddr);
+                       cache.valid = use_cache;
+               } else
+                       cache.valid--;
+       }
+
+       return r;
+}
+
+/* Search and connect
+ * Returns:
+ *   -1 - critical error (exit persist mode)
+ *   1  - non critical error
+ *   0  - success
+ */
+static int do_connect(void)
+{
+       inquiry_info *ii;
+       int reconnect = 0;
+       int i, n, r = 0;
+
+       do {
+               if (reconnect)
+                       sleep(persist);
+               reconnect = 1;
+
+               if (cache.valid > 0) {
+                       /* Use cached bdaddr */
+                       r = create_connection(cache.dst, &cache.bdaddr);
+                       if (r < 0) {
+                               terminate = 1;
+                               break;
+                       }
+                       continue;
+               }
+
+               syslog(LOG_INFO, "Inquiring");
+
+               /* FIXME: Should we use non general LAP here ? */
+
+               ii = NULL;
+               n  = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0);
+               if (n < 0) {
+                       syslog(LOG_ERR, "Inquiry failed. %s(%d)",
+                                               strerror(errno), errno);
+                       continue;
+               }
+
+               for (i = 0; i < n; i++) {
+                       char dst[40];
+                       ba2str(&ii[i].bdaddr, dst);
+
+                       if (use_sdp) {
+                               syslog(LOG_INFO, "Searching for %s on %s",
+                                               bnep_svc2str(service), dst);
+
+                               if (bnep_sdp_search(&src_addr, &ii[i].bdaddr, service) <= 0)
+                                       continue;
+                       }
+
+                       r = create_connection(dst, &ii[i].bdaddr);
+                       if (r < 0) {
+                               terminate = 1;
+                               break;
+                       }
+               }
+               bt_free(ii);
+       } while (!terminate && persist);
+
+       return r;
+}
+
+static void do_show(void)
+{
+       bnep_show_connections();
+}
+
+static void do_kill(char *dst)
+{
+       if (dst) {
+               bdaddr_t *ba = strtoba(dst);
+               bnep_kill_connection((void *) ba);
+               free(ba);
+       } else {
+               bnep_kill_all_connections();
+       }
+}
+
+static void sig_hup(int sig)
+{
+       return;
+}
+
+static void sig_term(int sig)
+{
+       terminate = 1;
+}
+
+static int write_pidfile(void)
+{
+       int fd;
+       FILE *f;
+       pid_t pid;
+
+       do {
+               fd = open(pidfile, O_WRONLY|O_TRUNC|O_CREAT|O_EXCL, 0644);
+               if (fd == -1) {
+                       /* Try to open the file for read. */
+                       fd = open(pidfile, O_RDONLY);
+                       if (fd < 0) {
+                               syslog(LOG_ERR, "Could not read old pidfile: %s(%d)",
+                                                       strerror(errno), errno);
+                               return -1;
+                       }
+
+                       /* We're already running; send a SIGHUP (we presume that they
+                        * are calling ifup for a reason, so they probably want to
+                        * rescan) and then exit cleanly and let things go on in the
+                        * background.  Muck with the filename so that we don't go
+                        * deleting the pid file for the already-running instance.
+                        */
+                       f = fdopen(fd, "r");
+                       if (!f) {
+                               syslog(LOG_ERR, "Could not fdopen old pidfile: %s(%d)",
+                                                       strerror(errno), errno);
+                               close(fd);
+                               return -1;
+                       }
+
+                       pid = 0;
+                       if (fscanf(f, "%d", &pid) != 1)
+                               pid = 0;
+                       fclose(f);
+
+                       if (pid) {
+                               /* Try to kill it. */
+                               if (kill(pid, SIGHUP) == -1) {
+                                       /* No such pid; remove the bogus pid file. */
+                                       syslog(LOG_INFO, "Removing stale pidfile");
+                                       unlink(pidfile);
+                                       fd = -1;
+                               } else {
+                                       /* Got it.  Don't mess with the pid file on
+                                        * our way out. */
+                                       syslog(LOG_INFO, "Signalling existing process %d and exiting\n", pid);
+                                       pidfile = NULL;
+                                       return -1;
+                               }
+                       }
+               }
+       } while(fd == -1);
+
+       f = fdopen(fd, "w");
+       if (!f) {
+               syslog(LOG_ERR, "Could not fdopen new pidfile: %s(%d)",
+                                               strerror(errno), errno);
+               close(fd);
+               unlink(pidfile);
+               return -1;
+       }
+
+       fprintf(f, "%d\n", getpid());
+       fclose(f);
+
+       return 0;
+}
+
+static struct option main_lopts[] = {
+       { "help",     0, 0, 'h' },
+       { "listen",   0, 0, 's' },
+       { "connect",  1, 0, 'c' },
+       { "search",   2, 0, 'Q' },
+       { "kill",     1, 0, 'k' },
+       { "killall",  0, 0, 'K' },
+       { "role",     1, 0, 'r' },
+       { "service",  1, 0, 'd' },
+       { "ethernet", 1, 0, 'e' },
+       { "device",   1, 0, 'i' },
+       { "nosdp",    0, 0, 'D' },
+       { "list",     0, 0, 'l' },
+       { "show",     0, 0, 'l' },
+       { "nodetach", 0, 0, 'n' },
+       { "persist",  2, 0, 'p' },
+       { "auth",     0, 0, 'A' },
+       { "encrypt",  0, 0, 'E' },
+       { "secure",   0, 0, 'S' },
+       { "master",   0, 0, 'M' },
+       { "cache",    0, 0, 'C' },
+       { "pidfile",  1, 0, 'P' },
+       { "devup",    1, 0, 'u' },
+       { "devdown",  1, 0, 'o' },
+       { "autozap",  0, 0, 'z' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *main_sopts = "hsc:k:Kr:d:e:i:lnp::DQ::AESMC::P:u:o:z";
+
+static const char *main_help =
+       "Bluetooth PAN daemon version %s\n"
+       "Usage:\n"
+       "\tpand <options>\n"
+       "Options:\n"
+       "\t--show --list -l          Show active PAN connections\n"
+       "\t--listen -s               Listen for PAN connections\n"
+       "\t--connect -c <bdaddr>     Create PAN connection\n"
+       "\t--autozap -z              Disconnect automatically on exit\n"
+       "\t--search -Q[duration]     Search and connect\n"
+       "\t--kill -k <bdaddr>        Kill PAN connection\n"
+       "\t--killall -K              Kill all PAN connections\n"
+       "\t--role -r <role>          Local PAN role (PANU, NAP, GN)\n"
+       "\t--service -d <role>       Remote PAN service (PANU, NAP, GN)\n"
+       "\t--ethernet -e <name>      Network interface name\n"
+       "\t--device -i <bdaddr>      Source bdaddr\n"
+       "\t--nosdp -D                Disable SDP\n"
+       "\t--auth -A                 Enable authentication\n"
+       "\t--encrypt -E              Enable encryption\n"
+       "\t--secure -S               Secure connection\n"
+       "\t--master -M               Become the master of a piconet\n"
+       "\t--nodetach -n             Do not become a daemon\n"
+       "\t--persist -p[interval]    Persist mode\n"
+       "\t--cache -C[valid]         Cache addresses\n"
+       "\t--pidfile -P <pidfile>    Create PID file\n"
+       "\t--devup -u <script>       Script to run when interface comes up\n"
+       "\t--devdown -o <script>     Script to run when interface comes down\n";
+
+int main(int argc, char *argv[])
+{
+       char *dst = NULL, *src = NULL;
+       struct sigaction sa;
+       int mode = NONE;
+       int opt;
+
+       while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) {
+               switch(opt) {
+               case 'l':
+                       mode = SHOW;
+                       detach = 0;
+                       break;
+
+               case 's':
+                       mode = LISTEN;
+                       break;
+
+               case 'c':
+                       mode = CONNECT;
+                       dst  = strdup(optarg);
+                       break;
+
+               case 'Q':
+                       mode = CONNECT;
+                       if (optarg)
+                               search_duration = atoi(optarg);
+                       break;
+
+               case 'k':
+                       mode = KILL;
+                       detach = 0;
+                       dst  = strdup(optarg);
+                       break;
+
+               case 'K':
+                       mode = KILL;
+                       detach = 0;
+                       break;
+
+               case 'i':
+                       src = strdup(optarg);
+                       break;
+
+               case 'r':
+                       bnep_str2svc(optarg, &role);
+                       break;
+
+               case 'd':
+                       bnep_str2svc(optarg, &service);
+                       break;
+
+               case 'D':
+                       use_sdp = 0;
+                       break;
+
+               case 'A':
+                       link_mode |= L2CAP_LM_AUTH;
+                       break;
+
+               case 'E':
+                       link_mode |= L2CAP_LM_ENCRYPT;
+                       break;
+
+               case 'S':
+                       link_mode |= L2CAP_LM_SECURE;
+                       break;
+
+               case 'M':
+                       link_mode |= L2CAP_LM_MASTER;
+                       break;
+
+               case 'e':
+                       strncpy(netdev, optarg, 16);
+                       netdev[15] = '\0';
+                       break;
+
+               case 'n':
+                       detach = 0;
+                       break;
+
+               case 'p':
+                       if (optarg)
+                               persist = atoi(optarg);
+                       else
+                               persist = 5;
+                       break;
+
+               case 'C':
+                       if (optarg)
+                               use_cache = atoi(optarg);
+                       else
+                               use_cache = 2;
+                       break;
+
+               case 'P':
+                       pidfile = strdup(optarg);
+                       break;
+
+               case 'u':
+                       devupcmd = strdup(optarg);
+                       break;
+
+               case 'o':
+                       devdowncmd = strdup(optarg);
+                       break;
+
+               case 'z':
+                       cleanup = 1;
+                       break;
+
+               case 'h':
+               default:
+                       printf(main_help, VERSION);
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (bnep_init()) {
+               free(dst);
+               return -1;
+       }
+
+       /* Check non daemon modes first */
+       switch (mode) {
+       case SHOW:
+               do_show();
+               free(dst);
+               return 0;
+
+       case KILL:
+               do_kill(dst);
+               free(dst);
+               return 0;
+
+       case NONE:
+               printf(main_help, VERSION);
+               free(dst);
+               return 0;
+       }
+
+       /* Initialize signals */
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       if (detach && daemon(0, 0)) {
+               perror("Can't start daemon");
+               exit(1);
+       }
+
+       openlog("pand", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+       syslog(LOG_INFO, "Bluetooth PAN daemon version %s", VERSION);
+
+       if (src) {
+               src_dev = hci_devid(src);
+               if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) {
+                       syslog(LOG_ERR, "Invalid source. %s(%d)",
+                                               strerror(errno), errno);
+                       free(dst);
+                       return -1;
+               }
+       }
+
+       if (pidfile && write_pidfile()) {
+               free(dst);
+               return -1;
+       }
+
+       if (dst) {
+               /* Disable cache invalidation */
+               use_cache = 0;
+
+               strncpy(cache.dst, dst, sizeof(cache.dst) - 1);
+               str2ba(dst, &cache.bdaddr);
+               cache.valid = 1;
+               free(dst);
+       }
+
+       switch (mode) {
+       case CONNECT:
+               do_connect();
+               break;
+
+       case LISTEN:
+               do_listen();
+               break;
+       }
+
+       if (pidfile)
+               unlink(pidfile);
+
+       return 0;
+}
diff --git a/compat/pand.h b/compat/pand.h
new file mode 100644 (file)
index 0000000..08b5bdc
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int bnep_init(void);
+int bnep_cleanup(void);
+
+int bnep_str2svc(char *svc, uint16_t *uuid);
+char *bnep_svc2str(uint16_t uuid);
+
+int bnep_show_connections(void);
+int bnep_kill_connection(uint8_t *dst);
+int bnep_kill_all_connections(void);
+
+int bnep_accept_connection(int sk, uint16_t role, char *dev);
+int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev);
diff --git a/compat/sdp.c b/compat/sdp.c
new file mode 100644 (file)
index 0000000..9ad8333
--- /dev/null
@@ -0,0 +1,706 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/hidp.h>
+#include <bluetooth/bnep.h>
+
+#include "textfile.h"
+#include "sdp.h"
+
+static sdp_record_t *record = NULL;
+static sdp_session_t *session = NULL;
+
+static void epox_endian_quirk(unsigned char *data, int size)
+{
+       /* USAGE_PAGE (Keyboard)        05 07
+        * USAGE_MINIMUM (0)            19 00
+        * USAGE_MAXIMUM (65280)        2A 00 FF   <= must be FF 00
+        * LOGICAL_MINIMUM (0)          15 00
+        * LOGICAL_MAXIMUM (65280)      26 00 FF   <= must be FF 00
+        */
+       unsigned char pattern[] = { 0x05, 0x07, 0x19, 0x00, 0x2a, 0x00, 0xff,
+                                               0x15, 0x00, 0x26, 0x00, 0xff };
+       unsigned int i;
+
+       if (!data)
+               return;
+
+       for (i = 0; i < size - sizeof(pattern); i++) {
+               if (!memcmp(data + i, pattern, sizeof(pattern))) {
+                       data[i + 5] = 0xff;
+                       data[i + 6] = 0x00;
+                       data[i + 10] = 0xff;
+                       data[i + 11] = 0x00;
+               }
+       }
+}
+
+static int store_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req)
+{
+       char filename[PATH_MAX + 1], addr[18], *str, *desc;
+       int i, err, size;
+
+       ba2str(src, addr);
+       create_name(filename, PATH_MAX, STORAGEDIR, addr, "hidd");
+
+       size = 15 + 3 + 3 + 5 + (req->rd_size * 2) + 1 + 9 + strlen(req->name) + 2;
+       str = malloc(size);
+       if (!str)
+               return -ENOMEM;
+
+       desc = malloc((req->rd_size * 2) + 1);
+       if (!desc) {
+               free(str);
+               return -ENOMEM;
+       }
+
+       memset(desc, 0, (req->rd_size * 2) + 1);
+       for (i = 0; i < req->rd_size; i++)
+               sprintf(desc + (i * 2), "%2.2X", req->rd_data[i]);
+
+       snprintf(str, size - 1, "%04X:%04X:%04X %02X %02X %04X %s %08X %s",
+                       req->vendor, req->product, req->version,
+                       req->subclass, req->country, req->parser, desc,
+                       req->flags, req->name);
+
+       free(desc);
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(dst, addr);
+       err = textfile_put(filename, addr, str);
+
+       free(str);
+
+       return err;
+}
+
+int get_stored_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req)
+{
+       char filename[PATH_MAX + 1], addr[18], tmp[3], *str, *desc;
+       unsigned int vendor, product, version, subclass, country, parser, pos;
+       int i;
+
+       desc = malloc(4096);
+       if (!desc)
+               return -ENOMEM;
+
+       memset(desc, 0, 4096);
+
+       ba2str(src, addr);
+       create_name(filename, PATH_MAX, STORAGEDIR, addr, "hidd");
+
+       ba2str(dst, addr);
+       str = textfile_get(filename, addr);
+       if (!str) {
+               free(desc);
+               return -EIO;
+       }
+
+       sscanf(str, "%04X:%04X:%04X %02X %02X %04X %4095s %08X %n",
+                       &vendor, &product, &version, &subclass, &country,
+                       &parser, desc, &req->flags, &pos);
+
+
+       req->vendor   = vendor;
+       req->product  = product;
+       req->version  = version;
+       req->subclass = subclass;
+       req->country  = country;
+       req->parser   = parser;
+
+       snprintf(req->name, 128, "%s", str + pos);
+
+       free(str);
+       req->rd_size = strlen(desc) / 2;
+       req->rd_data = malloc(req->rd_size);
+       if (!req->rd_data) {
+               free(desc);
+               return -ENOMEM;
+       }
+
+       memset(tmp, 0, sizeof(tmp));
+       for (i = 0; i < req->rd_size; i++) {
+               memcpy(tmp, desc + (i * 2), 2);
+               req->rd_data[i] = (uint8_t) strtol(tmp, NULL, 16);
+       }
+
+       free(desc);
+
+       return 0;
+}
+
+int get_sdp_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req)
+{
+       struct sockaddr_l2 addr;
+       socklen_t addrlen;
+       bdaddr_t bdaddr;
+       uint32_t range = 0x0000ffff;
+       sdp_session_t *s;
+       sdp_list_t *search, *attrid, *pnp_rsp, *hid_rsp;
+       sdp_record_t *rec;
+       sdp_data_t *pdlist, *pdlist2;
+       uuid_t svclass;
+       int err;
+
+       s = sdp_connect(src, dst, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE);
+       if (!s)
+               return -1;
+
+       sdp_uuid16_create(&svclass, PNP_INFO_SVCLASS_ID);
+       search = sdp_list_append(NULL, &svclass);
+       attrid = sdp_list_append(NULL, &range);
+
+       err = sdp_service_search_attr_req(s, search,
+                                       SDP_ATTR_REQ_RANGE, attrid, &pnp_rsp);
+
+       sdp_list_free(search, NULL);
+       sdp_list_free(attrid, NULL);
+
+       sdp_uuid16_create(&svclass, HID_SVCLASS_ID);
+       search = sdp_list_append(NULL, &svclass);
+       attrid = sdp_list_append(NULL, &range);
+
+       err = sdp_service_search_attr_req(s, search,
+                                       SDP_ATTR_REQ_RANGE, attrid, &hid_rsp);
+
+       sdp_list_free(search, NULL);
+       sdp_list_free(attrid, NULL);
+
+       memset(&addr, 0, sizeof(addr));
+       addrlen = sizeof(addr);
+
+       if (getsockname(s->sock, (struct sockaddr *) &addr, &addrlen) < 0)
+               bacpy(&bdaddr, src);
+       else
+               bacpy(&bdaddr, &addr.l2_bdaddr);
+
+       sdp_close(s);
+
+       if (err || !hid_rsp)
+               return -1;
+
+       if (pnp_rsp) {
+               rec = (sdp_record_t *) pnp_rsp->data;
+
+               pdlist = sdp_data_get(rec, 0x0201);
+               req->vendor = pdlist ? pdlist->val.uint16 : 0x0000;
+
+               pdlist = sdp_data_get(rec, 0x0202);
+               req->product = pdlist ? pdlist->val.uint16 : 0x0000;
+
+               pdlist = sdp_data_get(rec, 0x0203);
+               req->version = pdlist ? pdlist->val.uint16 : 0x0000;
+
+               sdp_record_free(rec);
+       }
+
+       rec = (sdp_record_t *) hid_rsp->data;
+
+       pdlist2 = sdp_data_get(rec, 0x0100);
+       if (pdlist2)
+               strncpy(req->name, pdlist2->val.str, sizeof(req->name) - 1);
+       else {
+               pdlist = sdp_data_get(rec, 0x0101);
+               pdlist2 = sdp_data_get(rec, 0x0102);
+               if (pdlist) {
+                       if (pdlist2) {
+                               if (strncmp(pdlist->val.str, pdlist2->val.str, 5)) {
+                                       strncpy(req->name, pdlist2->val.str, sizeof(req->name) - 1);
+                                       strcat(req->name, " ");
+                               }
+                               strncat(req->name, pdlist->val.str,
+                                               sizeof(req->name) - strlen(req->name));
+                       } else
+                               strncpy(req->name, pdlist->val.str, sizeof(req->name) - 1);
+               }
+       }
+
+       pdlist = sdp_data_get(rec, 0x0201);
+       req->parser = pdlist ? pdlist->val.uint16 : 0x0100;
+
+       pdlist = sdp_data_get(rec, 0x0202);
+       req->subclass = pdlist ? pdlist->val.uint8 : 0;
+
+       pdlist = sdp_data_get(rec, 0x0203);
+       req->country = pdlist ? pdlist->val.uint8 : 0;
+
+       pdlist = sdp_data_get(rec, 0x0206);
+       if (pdlist) {
+               pdlist = pdlist->val.dataseq;
+               pdlist = pdlist->val.dataseq;
+               pdlist = pdlist->next;
+
+               req->rd_data = malloc(pdlist->unitSize);
+               if (req->rd_data) {
+                       memcpy(req->rd_data, (unsigned char *) pdlist->val.str, pdlist->unitSize);
+                       req->rd_size = pdlist->unitSize;
+                       epox_endian_quirk(req->rd_data, req->rd_size);
+               }
+       }
+
+       sdp_record_free(rec);
+
+       if (bacmp(&bdaddr, BDADDR_ANY))
+               store_device_info(&bdaddr, dst, req);
+
+       return 0;
+}
+
+int get_alternate_device_info(const bdaddr_t *src, const bdaddr_t *dst, uint16_t *uuid, uint8_t *channel, char *name, size_t len)
+{
+       uint16_t attr1 = SDP_ATTR_PROTO_DESC_LIST;
+       uint16_t attr2 = SDP_ATTR_SVCNAME_PRIMARY;
+       sdp_session_t *s;
+       sdp_list_t *search, *attrid, *rsp;
+       uuid_t svclass;
+       int err;
+
+       s = sdp_connect(src, dst, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE);
+       if (!s)
+               return -1;
+
+       sdp_uuid16_create(&svclass, HEADSET_SVCLASS_ID);
+       search = sdp_list_append(NULL, &svclass);
+       attrid = sdp_list_append(NULL, &attr1);
+       attrid = sdp_list_append(attrid, &attr2);
+
+       err = sdp_service_search_attr_req(s, search,
+                                       SDP_ATTR_REQ_INDIVIDUAL, attrid, &rsp);
+
+       sdp_list_free(search, NULL);
+       sdp_list_free(attrid, NULL);
+
+       if (err <= 0) {
+               sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID);
+               search = sdp_list_append(NULL, &svclass);
+               attrid = sdp_list_append(NULL, &attr1);
+               attrid = sdp_list_append(attrid, &attr2);
+
+               err = sdp_service_search_attr_req(s, search,
+                                       SDP_ATTR_REQ_INDIVIDUAL, attrid, &rsp);
+
+               sdp_list_free(search, NULL);
+               sdp_list_free(attrid, NULL);
+
+               if (err < 0) {
+                       sdp_close(s);
+                       return err;
+               }
+
+               if (uuid)
+                       *uuid = SERIAL_PORT_SVCLASS_ID;
+       } else {
+               if (uuid)
+                       *uuid = HEADSET_SVCLASS_ID;
+       }
+
+       sdp_close(s);
+
+       for (; rsp; rsp = rsp->next) {
+               sdp_record_t *rec = (sdp_record_t *) rsp->data;
+               sdp_list_t *protos;
+
+               sdp_get_service_name(rec, name, len);
+
+               if (!sdp_get_access_protos(rec, &protos)) {
+                       uint8_t ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+                       if (ch > 0) {
+                               if (channel)
+                                       *channel = ch;
+                               return 0;
+                       }
+               }
+
+               sdp_record_free(rec);
+       }
+
+       return -EIO;
+}
+
+void bnep_sdp_unregister(void)
+{
+       if (record && sdp_record_unregister(session, record))
+               syslog(LOG_ERR, "Service record unregistration failed.");
+
+       sdp_close(session);
+}
+
+int bnep_sdp_register(bdaddr_t *device, uint16_t role)
+{
+       sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+       uuid_t root_uuid, pan, l2cap, bnep;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *proto[2];
+       sdp_data_t *v, *p;
+       uint16_t psm = 15, version = 0x0100;
+       uint16_t security_desc = 0;
+       uint16_t net_access_type = 0xfffe;
+       uint32_t max_net_access_rate = 0;
+       char *name = "BlueZ PAN";
+       char *desc = "BlueZ PAN Service";
+       int status;
+
+       session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+       if (!session) {
+               syslog(LOG_ERR, "Failed to connect to the local SDP server. %s(%d)",
+                                                       strerror(errno), errno);
+               return -1;
+       }
+
+       record = sdp_record_alloc();
+       if (!record) {
+               syslog(LOG_ERR, "Failed to allocate service record %s(%d)",
+                                                       strerror(errno), errno);
+               sdp_close(session);
+               return -1;
+       }
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(record, root);
+       sdp_list_free(root, 0);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(NULL, &l2cap);
+       p = sdp_data_alloc(SDP_UINT16, &psm);
+       proto[0] = sdp_list_append(proto[0], p);
+       apseq    = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&bnep, BNEP_UUID);
+       proto[1] = sdp_list_append(NULL, &bnep);
+       v = sdp_data_alloc(SDP_UINT16, &version);
+       proto[1] = sdp_list_append(proto[1], v);
+
+       /* Supported protocols */
+       {
+               uint16_t ptype[4] = {
+                       0x0800,  /* IPv4 */
+                       0x0806,  /* ARP */
+               };
+               sdp_data_t *head, *pseq;
+               int p;
+
+               for (p = 0, head = NULL; p < 2; p++) {
+                       sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+                       if (head)
+                               sdp_seq_append(head, data);
+                       else
+                               head = data;
+               }
+               pseq = sdp_data_alloc(SDP_SEQ16, head);
+               proto[1] = sdp_list_append(proto[1], pseq);
+       }
+
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_add_lang_attr(record);
+
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(aproto, NULL);
+       sdp_data_free(p);
+       sdp_data_free(v);
+       sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security_desc);
+
+       switch (role) {
+       case BNEP_SVC_NAP:
+               sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
+               svclass = sdp_list_append(NULL, &pan);
+               sdp_set_service_classes(record, svclass);
+
+               sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+               profile[0].version = 0x0100;
+               pfseq = sdp_list_append(NULL, &profile[0]);
+               sdp_set_profile_descs(record, pfseq);
+
+               sdp_set_info_attr(record, "Network Access Point", name, desc);
+
+               sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &net_access_type);
+               sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE, SDP_UINT32, &max_net_access_rate);
+               break;
+
+       case BNEP_SVC_GN:
+               sdp_uuid16_create(&pan, GN_SVCLASS_ID);
+               svclass = sdp_list_append(NULL, &pan);
+               sdp_set_service_classes(record, svclass);
+
+               sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+               profile[0].version = 0x0100;
+               pfseq = sdp_list_append(NULL, &profile[0]);
+               sdp_set_profile_descs(record, pfseq);
+
+               sdp_set_info_attr(record, "Group Network Service", name, desc);
+               break;
+
+       case BNEP_SVC_PANU:
+               sdp_uuid16_create(&pan, PANU_SVCLASS_ID);
+               svclass = sdp_list_append(NULL, &pan);
+               sdp_set_service_classes(record, svclass);
+               sdp_list_free(svclass, 0);
+
+               sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+               profile[0].version = 0x0100;
+               pfseq = sdp_list_append(NULL, &profile[0]);
+               sdp_set_profile_descs(record, pfseq);
+               sdp_list_free(pfseq, 0);
+
+               sdp_set_info_attr(record, "PAN User", name, desc);
+               break;
+       }
+
+       status = sdp_device_record_register(session, device, record, 0);
+       if (status) {
+               syslog(LOG_ERR, "SDP registration failed.");
+               sdp_record_free(record); record = NULL;
+               sdp_close(session);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Search for PAN service.
+ * Returns 1 if service is found and 0 otherwise. */
+int bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service)
+{
+       sdp_list_t *srch, *rsp = NULL;
+       sdp_session_t *s;
+       uuid_t svclass;
+       int err;
+
+       switch (service) {
+       case BNEP_SVC_PANU:
+               sdp_uuid16_create(&svclass, PANU_SVCLASS_ID);
+               break;
+       case BNEP_SVC_NAP:
+               sdp_uuid16_create(&svclass, NAP_SVCLASS_ID);
+               break;
+       case BNEP_SVC_GN:
+               sdp_uuid16_create(&svclass, GN_SVCLASS_ID);
+               break;
+       }
+
+       srch = sdp_list_append(NULL, &svclass);
+
+       s = sdp_connect(src, dst, 0);
+       if (!s) {
+               syslog(LOG_ERR, "Failed to connect to the SDP server. %s(%d)",
+                                                       strerror(errno), errno);
+               return 0;
+       }
+
+       err = sdp_service_search_req(s, srch, 1, &rsp);
+       sdp_close(s);
+
+       /* Assume that search is successeful
+        * if at least one record is found */
+       if (!err && sdp_list_len(rsp))
+               return 1;
+
+       return 0;
+}
+
+static unsigned char async_uuid[] = {  0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62,
+                                       0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C };
+
+void dun_sdp_unregister(void)
+{
+       if (record && sdp_record_unregister(session, record))
+               syslog(LOG_ERR, "Service record unregistration failed.");
+       sdp_close(session);
+}
+
+int dun_sdp_register(bdaddr_t *device, uint8_t channel, int type)
+{
+       sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+       uuid_t root_uuid, l2cap, rfcomm, dun;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *proto[2];
+       int status;
+
+       session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+       if (!session) {
+               syslog(LOG_ERR, "Failed to connect to the local SDP server. %s(%d)",
+                               strerror(errno), errno);
+               return -1;
+       }
+
+       record = sdp_record_alloc();
+       if (!record) {
+               syslog(LOG_ERR, "Failed to alloc service record");
+               return -1;
+       }
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(record, root);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(NULL, &l2cap);
+       apseq    = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+       proto[1] = sdp_list_append(NULL, &rfcomm);
+       proto[1] = sdp_list_append(proto[1], sdp_data_alloc(SDP_UINT8, &channel));
+       apseq    = sdp_list_append(apseq, proto[1]);
+
+       aproto   = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       switch (type) {
+       case MROUTER:
+               sdp_uuid16_create(&dun, SERIAL_PORT_SVCLASS_ID);
+               break;
+       case ACTIVESYNC:
+               sdp_uuid128_create(&dun, (void *) async_uuid);
+               break;
+       case DIALUP:
+               sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
+               break;
+       default:
+               sdp_uuid16_create(&dun, LAN_ACCESS_SVCLASS_ID);
+               break;
+       }
+
+       svclass = sdp_list_append(NULL, &dun);
+       sdp_set_service_classes(record, svclass);
+
+       switch (type) {
+       case LANACCESS:
+               sdp_uuid16_create(&profile[0].uuid, LAN_ACCESS_PROFILE_ID);
+               profile[0].version = 0x0100;
+               pfseq = sdp_list_append(NULL, &profile[0]);
+               sdp_set_profile_descs(record, pfseq);
+               break;
+       case DIALUP:
+               sdp_uuid16_create(&profile[0].uuid, DIALUP_NET_PROFILE_ID);
+               profile[0].version = 0x0100;
+               pfseq = sdp_list_append(NULL, &profile[0]);
+               sdp_set_profile_descs(record, pfseq);
+               break;
+       }
+
+       switch (type) {
+       case MROUTER:
+               sdp_set_info_attr(record, "mRouter", NULL, NULL);
+               break;
+       case ACTIVESYNC:
+               sdp_set_info_attr(record, "ActiveSync", NULL, NULL);
+               break;
+       case DIALUP:
+               sdp_set_info_attr(record, "Dialup Networking", NULL, NULL);
+               break;
+       default:
+               sdp_set_info_attr(record, "LAN Access Point", NULL, NULL);
+               break;
+       }
+
+       status = sdp_device_record_register(session, device, record, 0);
+       if (status) {
+               syslog(LOG_ERR, "SDP registration failed.");
+               sdp_record_free(record);
+               record = NULL;
+               return -1;
+       }
+       return 0;
+}
+
+int dun_sdp_search(bdaddr_t *src, bdaddr_t *dst, int *channel, int type)
+{
+       sdp_session_t *s;
+       sdp_list_t *srch, *attrs, *rsp;
+       uuid_t svclass;
+       uint16_t attr;
+       int err;
+
+       s = sdp_connect(src, dst, 0);
+       if (!s) {
+               syslog(LOG_ERR, "Failed to connect to the SDP server. %s(%d)",
+                               strerror(errno), errno);
+               return -1;
+       }
+
+       switch (type) {
+       case MROUTER:
+               sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID);
+               break;
+       case ACTIVESYNC:
+               sdp_uuid128_create(&svclass, (void *) async_uuid);
+               break;
+       case DIALUP:
+               sdp_uuid16_create(&svclass, DIALUP_NET_SVCLASS_ID);
+               break;
+       default:
+               sdp_uuid16_create(&svclass, LAN_ACCESS_SVCLASS_ID);
+               break;
+       }
+
+       srch  = sdp_list_append(NULL, &svclass);
+
+       attr  = SDP_ATTR_PROTO_DESC_LIST;
+       attrs = sdp_list_append(NULL, &attr);
+
+       err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+
+       sdp_close(s);
+
+       if (err)
+               return 0;
+
+       for(; rsp; rsp = rsp->next) {
+               sdp_record_t *rec = (sdp_record_t *) rsp->data;
+               sdp_list_t *protos;
+
+               if (!sdp_get_access_protos(rec, &protos)) {
+                       int ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+                       if (ch > 0) {
+                               *channel = ch;
+                               return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
diff --git a/compat/sdp.h b/compat/sdp.h
new file mode 100644 (file)
index 0000000..6ca975f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define LANACCESS      0
+#define MROUTER                1
+#define ACTIVESYNC     2
+#define DIALUP         3
+
+int get_stored_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req);
+int get_sdp_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req);
+int get_alternate_device_info(const bdaddr_t *src, const bdaddr_t *dst, uint16_t *uuid, uint8_t *channel, char *name, size_t len);
+
+int bnep_sdp_search(bdaddr_t *src, bdaddr_t *dst, uint16_t service);
+int bnep_sdp_register(bdaddr_t *device, uint16_t role);
+void bnep_sdp_unregister(void);
+
+int dun_sdp_search(bdaddr_t *src, bdaddr_t *dst, int *channel, int type);
+int dun_sdp_register(bdaddr_t *device, uint8_t channel, int type);
+void dun_sdp_unregister(void);
diff --git a/compile b/compile
new file mode 100755 (executable)
index 0000000..b1f4749
--- /dev/null
+++ b/compile
@@ -0,0 +1,310 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand '-c -o'.
+
+scriptversion=2012-01-04.17; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009, 2010, 2012 Free
+# Software Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+nl='
+'
+
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent tools from complaining about whitespace usage.
+IFS=" ""       $nl"
+
+file_conv=
+
+# func_file_conv build_file lazy
+# Convert a $build file to $host form and store it in $file
+# Currently only supports Windows hosts. If the determined conversion
+# type is listed in (the comma separated) LAZY, no conversion will
+# take place.
+func_file_conv ()
+{
+  file=$1
+  case $file in
+    / | /[!/]*) # absolute file, and not a UNC file
+      if test -z "$file_conv"; then
+       # lazily determine how to convert abs files
+       case `uname -s` in
+         MINGW*)
+           file_conv=mingw
+           ;;
+         CYGWIN*)
+           file_conv=cygwin
+           ;;
+         *)
+           file_conv=wine
+           ;;
+       esac
+      fi
+      case $file_conv/,$2, in
+       *,$file_conv,*)
+         ;;
+       mingw/*)
+         file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
+         ;;
+       cygwin/*)
+         file=`cygpath -m "$file" || echo "$file"`
+         ;;
+       wine/*)
+         file=`winepath -w "$file" || echo "$file"`
+         ;;
+      esac
+      ;;
+  esac
+}
+
+# func_cl_wrapper cl arg...
+# Adjust compile command to suit cl
+func_cl_wrapper ()
+{
+  # Assume a capable shell
+  lib_path=
+  shared=:
+  linker_opts=
+  for arg
+  do
+    if test -n "$eat"; then
+      eat=
+    else
+      case $1 in
+       -o)
+         # configure might choose to run compile as 'compile cc -o foo foo.c'.
+         eat=1
+         case $2 in
+           *.o | *.[oO][bB][jJ])
+             func_file_conv "$2"
+             set x "$@" -Fo"$file"
+             shift
+             ;;
+           *)
+             func_file_conv "$2"
+             set x "$@" -Fe"$file"
+             shift
+             ;;
+         esac
+         ;;
+       -I*)
+         func_file_conv "${1#-I}" mingw
+         set x "$@" -I"$file"
+         shift
+         ;;
+       -l*)
+         lib=${1#-l}
+         found=no
+         save_IFS=$IFS
+         IFS=';'
+         for dir in $lib_path $LIB
+         do
+           IFS=$save_IFS
+           if $shared && test -f "$dir/$lib.dll.lib"; then
+             found=yes
+             set x "$@" "$dir/$lib.dll.lib"
+             break
+           fi
+           if test -f "$dir/$lib.lib"; then
+             found=yes
+             set x "$@" "$dir/$lib.lib"
+             break
+           fi
+         done
+         IFS=$save_IFS
+
+         test "$found" != yes && set x "$@" "$lib.lib"
+         shift
+         ;;
+       -L*)
+         func_file_conv "${1#-L}"
+         if test -z "$lib_path"; then
+           lib_path=$file
+         else
+           lib_path="$lib_path;$file"
+         fi
+         linker_opts="$linker_opts -LIBPATH:$file"
+         ;;
+       -static)
+         shared=false
+         ;;
+       -Wl,*)
+         arg=${1#-Wl,}
+         save_ifs="$IFS"; IFS=','
+         for flag in $arg; do
+           IFS="$save_ifs"
+           linker_opts="$linker_opts $flag"
+         done
+         IFS="$save_ifs"
+         ;;
+       -Xlinker)
+         eat=1
+         linker_opts="$linker_opts $2"
+         ;;
+       -*)
+         set x "$@" "$1"
+         shift
+         ;;
+       *.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
+         func_file_conv "$1"
+         set x "$@" -Tp"$file"
+         shift
+         ;;
+       *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
+         func_file_conv "$1" mingw
+         set x "$@" "$file"
+         shift
+         ;;
+       *)
+         set x "$@" "$1"
+         shift
+         ;;
+      esac
+    fi
+    shift
+  done
+  if test -n "$linker_opts"; then
+    linker_opts="-link$linker_opts"
+  fi
+  exec "$@" $linker_opts
+  exit 1
+}
+
+eat=
+
+case $1 in
+  '')
+     echo "$0: No command.  Try '$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand '-c -o'.
+Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file 'INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "compile $scriptversion"
+    exit $?
+    ;;
+  cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
+    func_cl_wrapper "$@"      # Doesn't return...
+    ;;
+esac
+
+ofile=
+cfile=
+
+for arg
+do
+  if test -n "$eat"; then
+    eat=
+  else
+    case $1 in
+      -o)
+       # configure might choose to run compile as 'compile cc -o foo foo.c'.
+       # So we strip '-o arg' only if arg is an object.
+       eat=1
+       case $2 in
+         *.o | *.obj)
+           ofile=$2
+           ;;
+         *)
+           set x "$@" -o "$2"
+           shift
+           ;;
+       esac
+       ;;
+      *.c)
+       cfile=$1
+       set x "$@" "$1"
+       shift
+       ;;
+      *)
+       set x "$@" "$1"
+       shift
+       ;;
+    esac
+  fi
+  shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+  # If no '-o' option was seen then we might have been invoked from a
+  # pattern rule where we don't need one.  That is ok -- this is a
+  # normal compilation that the losing compiler can handle.  If no
+  # '.c' file was seen then we are probably linking.  That is also
+  # ok.
+  exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use '[/\\:.-]' here to ensure that we don't use the same name
+# that we are using for the .o file.  Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
+while true; do
+  if mkdir "$lockdir" >/dev/null 2>&1; then
+    break
+  fi
+  sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+  test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+  test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/config.guess b/config.guess
new file mode 100755 (executable)
index 0000000..d622a44
--- /dev/null
@@ -0,0 +1,1530 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+#   2011, 2012 Free Software Foundation, Inc.
+
+timestamp='2012-02-10'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner.  Please send patches (context
+# diff format) to <config-patches@gnu.org> and include a ChangeLog
+# entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+       for c in cc gcc c89 c99 ; do
+         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+            CC_FOR_BUILD="$c"; break ;
+         fi ;
+       done ;
+       if test x"$CC_FOR_BUILD" = x ; then
+         CC_FOR_BUILD=no_compiler_found ;
+       fi
+       ;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+       # NetBSD (nbsd) targets should (where applicable) match one or
+       # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+       # switched to ELF, *-*-netbsd* would select the old
+       # object file format.  This provides both forward
+       # compatibility and a consistent mechanism for selecting the
+       # object file format.
+       #
+       # Note: NetBSD doesn't particularly care about the vendor
+       # portion of the name.  We always set it to "unknown".
+       sysctl="sysctl -n hw.machine_arch"
+       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+       case "${UNAME_MACHINE_ARCH}" in
+           armeb) machine=armeb-unknown ;;
+           arm*) machine=arm-unknown ;;
+           sh3el) machine=shl-unknown ;;
+           sh3eb) machine=sh-unknown ;;
+           sh5el) machine=sh5le-unknown ;;
+           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+       esac
+       # The Operating System including object format, if it has switched
+       # to ELF recently, or will in the future.
+       case "${UNAME_MACHINE_ARCH}" in
+           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+               eval $set_cc_for_build
+               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+                       | grep -q __ELF__
+               then
+                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+                   # Return netbsd for either.  FIX?
+                   os=netbsd
+               else
+                   os=netbsdelf
+               fi
+               ;;
+           *)
+               os=netbsd
+               ;;
+       esac
+       # The OS release
+       # Debian GNU/NetBSD machines have a different userland, and
+       # thus, need a distinct triplet. However, they do not need
+       # kernel version information, so it can be replaced with a
+       # suitable tag, in the style of linux-gnu.
+       case "${UNAME_VERSION}" in
+           Debian*)
+               release='-gnu'
+               ;;
+           *)
+               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+               ;;
+       esac
+       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+       # contains redundant information, the shorter form:
+       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+       echo "${machine}-${os}${release}"
+       exit ;;
+    *:OpenBSD:*:*)
+       UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+       echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
+       exit ;;
+    *:ekkoBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+       exit ;;
+    *:SolidBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+       exit ;;
+    macppc:MirBSD:*:*)
+       echo powerpc-unknown-mirbsd${UNAME_RELEASE}
+       exit ;;
+    *:MirBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+       exit ;;
+    alpha:OSF1:*:*)
+       case $UNAME_RELEASE in
+       *4.0)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+               ;;
+       *5.*)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+               ;;
+       esac
+       # According to Compaq, /usr/sbin/psrinfo has been available on
+       # OSF/1 and Tru64 systems produced since 1995.  I hope that
+       # covers most systems running today.  This code pipes the CPU
+       # types through head -n 1, so we only detect the type of CPU 0.
+       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+       case "$ALPHA_CPU_TYPE" in
+           "EV4 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "EV4.5 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "LCA4 (21066/21068)")
+               UNAME_MACHINE="alpha" ;;
+           "EV5 (21164)")
+               UNAME_MACHINE="alphaev5" ;;
+           "EV5.6 (21164A)")
+               UNAME_MACHINE="alphaev56" ;;
+           "EV5.6 (21164PC)")
+               UNAME_MACHINE="alphapca56" ;;
+           "EV5.7 (21164PC)")
+               UNAME_MACHINE="alphapca57" ;;
+           "EV6 (21264)")
+               UNAME_MACHINE="alphaev6" ;;
+           "EV6.7 (21264A)")
+               UNAME_MACHINE="alphaev67" ;;
+           "EV6.8CB (21264C)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8AL (21264B)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8CX (21264D)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.9A (21264/EV69A)")
+               UNAME_MACHINE="alphaev69" ;;
+           "EV7 (21364)")
+               UNAME_MACHINE="alphaev7" ;;
+           "EV7.9 (21364A)")
+               UNAME_MACHINE="alphaev79" ;;
+       esac
+       # A Pn.n version is a patched version.
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+       # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+       exitcode=$?
+       trap '' 0
+       exit $exitcode ;;
+    Alpha\ *:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # Should we change UNAME_MACHINE based on the output of uname instead
+       # of the specific Alpha model?
+       echo alpha-pc-interix
+       exit ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       echo m68k-unknown-sysv4
+       exit ;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-amigaos
+       exit ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-morphos
+       exit ;;
+    *:OS/390:*:*)
+       echo i370-ibm-openedition
+       exit ;;
+    *:z/VM:*:*)
+       echo s390-ibm-zvmoe
+       exit ;;
+    *:OS400:*:*)
+       echo powerpc-ibm-os400
+       exit ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit ;;
+    arm:riscos:*:*|arm:RISCOS:*:*)
+       echo arm-unknown-riscos
+       exit ;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+       echo hppa1.1-hitachi-hiuxmpp
+       exit ;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit ;;
+    NILE*:*:*:dcosx)
+       echo pyramid-pyramid-svr4
+       exit ;;
+    DRS?6000:unix:4.0:6*)
+       echo sparc-icl-nx6
+       exit ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+       case `/usr/bin/uname -p` in
+           sparc) echo sparc-icl-nx7; exit ;;
+       esac ;;
+    s390x:SunOS:*:*)
+       echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4H:SunOS:5.*:*)
+       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+       echo i386-pc-auroraux${UNAME_RELEASE}
+       exit ;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+       eval $set_cc_for_build
+       SUN_ARCH="i386"
+       # If there is a compiler, see if it is configured for 64-bit objects.
+       # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+       # This test works for both compilers.
+       if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+           if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+               (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+               grep IS_64BIT_ARCH >/dev/null
+           then
+               SUN_ARCH="x86_64"
+           fi
+       fi
+       echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+       case "`/bin/arch`" in
+           sun3)
+               echo m68k-sun-sunos${UNAME_RELEASE}
+               ;;
+           sun4)
+               echo sparc-sun-sunos${UNAME_RELEASE}
+               ;;
+       esac
+       exit ;;
+    aushp:SunOS:*:*)
+       echo sparc-auspex-sunos${UNAME_RELEASE}
+       exit ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+       exit ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+       echo m68k-milan-mint${UNAME_RELEASE}
+       exit ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+       echo m68k-hades-mint${UNAME_RELEASE}
+       exit ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+       echo m68k-unknown-mint${UNAME_RELEASE}
+       exit ;;
+    m68k:machten:*:*)
+       echo m68k-apple-machten${UNAME_RELEASE}
+       exit ;;
+    powerpc:machten:*:*)
+       echo powerpc-apple-machten${UNAME_RELEASE}
+       exit ;;
+    RISC*:Mach:*:*)
+       echo mips-dec-mach_bsd4.3
+       exit ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       echo clipper-intergraph-clix${UNAME_RELEASE}
+       exit ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c &&
+         dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+         SYSTEM_NAME=`$dummy $dummyarg` &&
+           { echo "$SYSTEM_NAME"; exit; }
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit ;;
+    Motorola:PowerMAX_OS:*:*)
+       echo powerpc-motorola-powermax
+       exit ;;
+    Motorola:*:4.3:PL8-*)
+       echo powerpc-harris-powermax
+       exit ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+       echo powerpc-harris-powermax
+       exit ;;
+    Night_Hawk:Power_UNIX:*:*)
+       echo powerpc-harris-powerunix
+       exit ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit ;;
+    AViiON:dgux:*:*)
+       # DG/UX returns AViiON for all architectures
+       UNAME_PROCESSOR=`/usr/bin/uname -p`
+       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+       then
+           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+              [ ${TARGET_BINARY_INTERFACE}x = x ]
+           then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+           else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+           fi
+       else
+           echo i586-dg-dgux${UNAME_RELEASE}
+       fi
+       exit ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
+       exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+       echo i386-ibm-aix
+       exit ;;
+    ia64:AIX:*:*)
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+       exit ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               eval $set_cc_for_build
+               sed 's/^                //' << EOF >$dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+               then
+                       echo "$SYSTEM_NAME"
+               else
+                       echo rs6000-ibm-aix3.2.5
+               fi
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit ;;
+    *:AIX:*:[4567])
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       exit ;;                             # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit ;;
+    9000/[34678]??:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/[678][0-9][0-9])
+               if [ -x /usr/bin/getconf ]; then
+                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                   sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                   case "${sc_cpu_version}" in
+                     523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                     528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                     532)                      # CPU_PA_RISC2_0
+                       case "${sc_kernel_bits}" in
+                         32) HP_ARCH="hppa2.0n" ;;
+                         64) HP_ARCH="hppa2.0w" ;;
+                         '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+                       esac ;;
+                   esac
+               fi
+               if [ "${HP_ARCH}" = "" ]; then
+                   eval $set_cc_for_build
+                   sed 's/^            //' << EOF >$dummy.c
+
+               #define _HPUX_SOURCE
+               #include <stdlib.h>
+               #include <unistd.h>
+
+               int main ()
+               {
+               #if defined(_SC_KERNEL_BITS)
+                   long bits = sysconf(_SC_KERNEL_BITS);
+               #endif
+                   long cpu  = sysconf (_SC_CPU_VERSION);
+
+                   switch (cpu)
+                       {
+                       case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+                       case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+                       case CPU_PA_RISC2_0:
+               #if defined(_SC_KERNEL_BITS)
+                           switch (bits)
+                               {
+                               case 64: puts ("hppa2.0w"); break;
+                               case 32: puts ("hppa2.0n"); break;
+                               default: puts ("hppa2.0"); break;
+                               } break;
+               #else  /* !defined(_SC_KERNEL_BITS) */
+                           puts ("hppa2.0"); break;
+               #endif
+                       default: puts ("hppa1.0"); break;
+                       }
+                   exit (0);
+               }
+EOF
+                   (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+                   test -z "$HP_ARCH" && HP_ARCH=hppa
+               fi ;;
+       esac
+       if [ ${HP_ARCH} = "hppa2.0w" ]
+       then
+           eval $set_cc_for_build
+
+           # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+           # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+           # generating 64-bit code.  GNU and HP use different nomenclature:
+           #
+           # $ CC_FOR_BUILD=cc ./config.guess
+           # => hppa2.0w-hp-hpux11.23
+           # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+           # => hppa64-hp-hpux11.23
+
+           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+               grep -q __LP64__
+           then
+               HP_ARCH="hppa2.0w"
+           else
+               HP_ARCH="hppa64"
+           fi
+       fi
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit ;;
+    ia64:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ia64-hp-hpux${HPUX_REV}
+       exit ;;
+    3050*:HI-UX:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+               { echo "$SYSTEM_NAME"; exit; }
+       echo unknown-hitachi-hiuxwe2
+       exit ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+       echo hppa1.0-hp-mpeix
+       exit ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit ;;
+    i*86:OSF1:*:*)
+       if [ -x /usr/sbin/sysversion ] ; then
+           echo ${UNAME_MACHINE}-unknown-osf1mk
+       else
+           echo ${UNAME_MACHINE}-unknown-osf1
+       fi
+       exit ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+       exit ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+       exit ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+       exit ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+       exit ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+             -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*TS:*:*:*)
+       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*T3E:*:*:*)
+       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    CRAY*SV1:*:*:*)
+       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    *:UNICOS/mp:*:*)
+       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+       FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+       FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+       echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+       exit ;;
+    5000:UNIX_System_V:4.*:*)
+       FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+       FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+       echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+       exit ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       exit ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit ;;
+    *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit ;;
+    *:FreeBSD:*:*)
+       UNAME_PROCESSOR=`/usr/bin/uname -p`
+       case ${UNAME_PROCESSOR} in
+           amd64)
+               echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+           *)
+               echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+       esac
+       exit ;;
+    i*:CYGWIN*:*)
+       echo ${UNAME_MACHINE}-pc-cygwin
+       exit ;;
+    *:MINGW*:*)
+       echo ${UNAME_MACHINE}-pc-mingw32
+       exit ;;
+    i*:MSYS*:*)
+       echo ${UNAME_MACHINE}-pc-msys
+       exit ;;
+    i*:windows32*:*)
+       # uname -m includes "-pc" on this system.
+       echo ${UNAME_MACHINE}-mingw32
+       exit ;;
+    i*:PW*:*)
+       echo ${UNAME_MACHINE}-pc-pw32
+       exit ;;
+    *:Interix*:*)
+       case ${UNAME_MACHINE} in
+           x86)
+               echo i586-pc-interix${UNAME_RELEASE}
+               exit ;;
+           authenticamd | genuineintel | EM64T)
+               echo x86_64-unknown-interix${UNAME_RELEASE}
+               exit ;;
+           IA64)
+               echo ia64-unknown-interix${UNAME_RELEASE}
+               exit ;;
+       esac ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+       echo i${UNAME_MACHINE}-pc-mks
+       exit ;;
+    8664:Windows_NT:*)
+       echo x86_64-pc-mks
+       exit ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+       # UNAME_MACHINE based on the output of uname instead of i386?
+       echo i586-pc-interix
+       exit ;;
+    i*:UWIN*:*)
+       echo ${UNAME_MACHINE}-pc-uwin
+       exit ;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+       echo x86_64-unknown-cygwin
+       exit ;;
+    p*:CYGWIN*:*)
+       echo powerpcle-unknown-cygwin
+       exit ;;
+    prep*:SunOS:5.*:*)
+       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit ;;
+    *:GNU:*:*)
+       # the GNU system
+       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit ;;
+    *:GNU/*:*:*)
+       # other systems with GNU libc and userland
+       echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+       exit ;;
+    i*86:Minix:*:*)
+       echo ${UNAME_MACHINE}-pc-minix
+       exit ;;
+    aarch64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    aarch64_be:Linux:*:*)
+       UNAME_MACHINE=aarch64_be
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    alpha:Linux:*:*)
+       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+         EV5)   UNAME_MACHINE=alphaev5 ;;
+         EV56)  UNAME_MACHINE=alphaev56 ;;
+         PCA56) UNAME_MACHINE=alphapca56 ;;
+         PCA57) UNAME_MACHINE=alphapca56 ;;
+         EV6)   UNAME_MACHINE=alphaev6 ;;
+         EV67)  UNAME_MACHINE=alphaev67 ;;
+         EV68*) UNAME_MACHINE=alphaev68 ;;
+       esac
+       objdump --private-headers /bin/sh | grep -q ld.so.1
+       if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+       echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+       exit ;;
+    arm*:Linux:*:*)
+       eval $set_cc_for_build
+       if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+           | grep -q __ARM_EABI__
+       then
+           echo ${UNAME_MACHINE}-unknown-linux-gnu
+       else
+           if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+               | grep -q __ARM_PCS_VFP
+           then
+               echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+           else
+               echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+           fi
+       fi
+       exit ;;
+    avr32*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    cris:Linux:*:*)
+       echo ${UNAME_MACHINE}-axis-linux-gnu
+       exit ;;
+    crisv32:Linux:*:*)
+       echo ${UNAME_MACHINE}-axis-linux-gnu
+       exit ;;
+    frv:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    hexagon:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    i*86:Linux:*:*)
+       LIBC=gnu
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #ifdef __dietlibc__
+       LIBC=dietlibc
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+       echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+       exit ;;
+    ia64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    m32r*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    m68*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef ${UNAME_MACHINE}
+       #undef ${UNAME_MACHINE}el
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=${UNAME_MACHINE}el
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=${UNAME_MACHINE}
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+       ;;
+    or32:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    padre:Linux:*:*)
+       echo sparc-unknown-linux-gnu
+       exit ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+       echo hppa64-unknown-linux-gnu
+       exit ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+       # Look for CPU level
+       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+         PA7*) echo hppa1.1-unknown-linux-gnu ;;
+         PA8*) echo hppa2.0-unknown-linux-gnu ;;
+         *)    echo hppa-unknown-linux-gnu ;;
+       esac
+       exit ;;
+    ppc64:Linux:*:*)
+       echo powerpc64-unknown-linux-gnu
+       exit ;;
+    ppc:Linux:*:*)
+       echo powerpc-unknown-linux-gnu
+       exit ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+       echo ${UNAME_MACHINE}-ibm-linux
+       exit ;;
+    sh64*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    sh*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    tile*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    vax:Linux:*:*)
+       echo ${UNAME_MACHINE}-dec-linux-gnu
+       exit ;;
+    x86_64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    xtensa*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit ;;
+    i*86:DYNIX/ptx:4*:*)
+       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+       # earlier versions are messed up and put the nodename in both
+       # sysname and nodename.
+       echo i386-sequent-sysv4
+       exit ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+       # Unixware is an offshoot of SVR4, but it has its own version
+       # number series starting with 2...
+       # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+       # Use sysv4.2uw... so that sysv4* matches it.
+       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       exit ;;
+    i*86:OS/2:*:*)
+       # If we were able to find `uname', then EMX Unix compatibility
+       # is probably installed.
+       echo ${UNAME_MACHINE}-pc-os2-emx
+       exit ;;
+    i*86:XTS-300:*:STOP)
+       echo ${UNAME_MACHINE}-unknown-stop
+       exit ;;
+    i*86:atheos:*:*)
+       echo ${UNAME_MACHINE}-unknown-atheos
+       exit ;;
+    i*86:syllable:*:*)
+       echo ${UNAME_MACHINE}-pc-syllable
+       exit ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+       echo i386-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    i*86:*DOS:*:*)
+       echo ${UNAME_MACHINE}-pc-msdosdjgpp
+       exit ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+       fi
+       exit ;;
+    i*86:*:5:[678]*)
+       # UnixWare 7.x, OpenUNIX and OpenServer 6.
+       case `/bin/uname -X | grep "^Machine"` in
+           *486*)           UNAME_MACHINE=i486 ;;
+           *Pentium)        UNAME_MACHINE=i586 ;;
+           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+       esac
+       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       exit ;;
+    i*86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-pc-sysv32
+       fi
+       exit ;;
+    pc:*:*:*)
+       # Left here for compatibility:
+       # uname -m prints for DJGPP always 'pc', but it prints nothing about
+       # the processor, so we play safe by assuming i586.
+       # Note: whatever this is, it MUST be the same as what config.sub
+       # prints for the "djgpp" host, or else GDB configury will decide that
+       # this is a cross-build.
+       echo i586-pc-msdosdjgpp
+       exit ;;
+    Intel:Mach:3*:*)
+       echo i386-pc-mach3
+       exit ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+       echo m68k-convergent-sysv
+       exit ;;
+    M680?0:D-NIX:5.3:*)
+       echo m68k-diab-dnix
+       exit ;;
+    M68*:*:R3V[5678]*:*)
+       test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+       OS_REL='.3'
+       test -r /etc/.relid \
+           && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+           && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+           && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+       /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+           && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit ;;
+    TSUNAMI:LynxOS:2.*:*)
+       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    rs6000:LynxOS:2.*:*)
+       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+       echo powerpc-unknown-lynxos${UNAME_RELEASE}
+       exit ;;
+    SM[BE]S:UNIX_SV:*:*)
+       echo mips-dde-sysv${UNAME_RELEASE}
+       exit ;;
+    RM*:ReliantUNIX-*:*:*)
+       echo mips-sni-sysv4
+       exit ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit ;;
+    PENTIUM:*:4.0*:*)  # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                       # says <Richard.M.Bartel@ccMail.Census.GOV>
+       echo i586-unisys-sysv4
+       exit ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       echo hppa1.1-stratus-sysv4
+       exit ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       echo i860-stratus-sysv4
+       exit ;;
+    i*86:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo ${UNAME_MACHINE}-stratus-vos
+       exit ;;
+    *:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo hppa1.1-stratus-vos
+       exit ;;
+    mc68*:A/UX:*:*)
+       echo m68k-apple-aux${UNAME_RELEASE}
+       exit ;;
+    news*:NEWS-OS:6*:*)
+       echo mips-sony-newsos6
+       exit ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+       if [ -d /usr/nec ]; then
+               echo mips-nec-sysv${UNAME_RELEASE}
+       else
+               echo mips-unknown-sysv${UNAME_RELEASE}
+       fi
+       exit ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       echo powerpc-be-beos
+       exit ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       echo powerpc-apple-beos
+       exit ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       echo i586-pc-beos
+       exit ;;
+    BePC:Haiku:*:*)    # Haiku running on Intel PC compatible.
+       echo i586-pc-haiku
+       exit ;;
+    SX-4:SUPER-UX:*:*)
+       echo sx4-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-5:SUPER-UX:*:*)
+       echo sx5-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-6:SUPER-UX:*:*)
+       echo sx6-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-7:SUPER-UX:*:*)
+       echo sx7-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-8:SUPER-UX:*:*)
+       echo sx8-nec-superux${UNAME_RELEASE}
+       exit ;;
+    SX-8R:SUPER-UX:*:*)
+       echo sx8r-nec-superux${UNAME_RELEASE}
+       exit ;;
+    Power*:Rhapsody:*:*)
+       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       exit ;;
+    *:Rhapsody:*:*)
+       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       exit ;;
+    *:Darwin:*:*)
+       UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+       case $UNAME_PROCESSOR in
+           i386)
+               eval $set_cc_for_build
+               if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+                 if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+                     (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+                     grep IS_64BIT_ARCH >/dev/null
+                 then
+                     UNAME_PROCESSOR="x86_64"
+                 fi
+               fi ;;
+           unknown) UNAME_PROCESSOR=powerpc ;;
+       esac
+       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+       exit ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+       UNAME_PROCESSOR=`uname -p`
+       if test "$UNAME_PROCESSOR" = "x86"; then
+               UNAME_PROCESSOR=i386
+               UNAME_MACHINE=pc
+       fi
+       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+       exit ;;
+    *:QNX:*:4*)
+       echo i386-pc-qnx
+       exit ;;
+    NEO-?:NONSTOP_KERNEL:*:*)
+       echo neo-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    NSE-?:NONSTOP_KERNEL:*:*)
+       echo nse-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    NSR-?:NONSTOP_KERNEL:*:*)
+       echo nsr-tandem-nsk${UNAME_RELEASE}
+       exit ;;
+    *:NonStop-UX:*:*)
+       echo mips-compaq-nonstopux
+       exit ;;
+    BS2000:POSIX*:*:*)
+       echo bs2000-siemens-sysv
+       exit ;;
+    DS/*:UNIX_System_V:*:*)
+       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+       exit ;;
+    *:Plan9:*:*)
+       # "uname -m" is not consistent, so use $cputype instead. 386
+       # is converted to i386 for consistency with other x86
+       # operating systems.
+       if test "$cputype" = "386"; then
+           UNAME_MACHINE=i386
+       else
+           UNAME_MACHINE="$cputype"
+       fi
+       echo ${UNAME_MACHINE}-unknown-plan9
+       exit ;;
+    *:TOPS-10:*:*)
+       echo pdp10-unknown-tops10
+       exit ;;
+    *:TENEX:*:*)
+       echo pdp10-unknown-tenex
+       exit ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+       echo pdp10-dec-tops20
+       exit ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+       echo pdp10-xkl-tops20
+       exit ;;
+    *:TOPS-20:*:*)
+       echo pdp10-unknown-tops20
+       exit ;;
+    *:ITS:*:*)
+       echo pdp10-unknown-its
+       exit ;;
+    SEI:*:*:SEIUX)
+       echo mips-sei-seiux${UNAME_RELEASE}
+       exit ;;
+    *:DragonFly:*:*)
+       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit ;;
+    *:*VMS:*:*)
+       UNAME_MACHINE=`(uname -p) 2>/dev/null`
+       case "${UNAME_MACHINE}" in
+           A*) echo alpha-dec-vms ; exit ;;
+           I*) echo ia64-dec-vms ; exit ;;
+           V*) echo vax-dec-vms ; exit ;;
+       esac ;;
+    *:XENIX:*:SysV)
+       echo i386-pc-xenix
+       exit ;;
+    i*86:skyos:*:*)
+       echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+       exit ;;
+    i*86:rdos:*:*)
+       echo ${UNAME_MACHINE}-pc-rdos
+       exit ;;
+    i*86:AROS:*:*)
+       echo ${UNAME_MACHINE}-pc-aros
+       exit ;;
+    x86_64:VMkernel:*:*)
+       echo ${UNAME_MACHINE}-unknown-esx
+       exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+       "4"
+#else
+       ""
+#endif
+       ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+       { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit ;;
+    c34*)
+       echo c34-convex-bsd
+       exit ;;
+    c38*)
+       echo c38-convex-bsd
+       exit ;;
+    c4*)
+       echo c4-convex-bsd
+       exit ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+  http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..ef188ba
--- /dev/null
@@ -0,0 +1,93 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Directory for the configuration files */
+#undef CONFIGDIR
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have USB library. */
+#undef HAVE_LIBUSB
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have <sys/inotify.h>. */
+#undef HAVE_SYS_INOTIFY_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
+/* Define to 1 if you need the ppoll() function. */
+#undef NEED_PPOLL
+
+/* Define to 1 if you need the usb_get_busses() function. */
+#undef NEED_USB_GET_BUSSES
+
+/* Define to 1 if you need the usb_interrupt_read() function. */
+#undef NEED_USB_INTERRUPT_READ
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
+/* Define the OUI file path */
+#undef OUIFILE
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Directory for the storage files */
+#undef STORAGEDIR
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
+   `char[]'. */
+#undef YYTEXT_POINTER
diff --git a/config.sub b/config.sub
new file mode 100755 (executable)
index 0000000..c894da4
--- /dev/null
@@ -0,0 +1,1773 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+#   2011, 2012 Free Software Foundation, Inc.
+
+timestamp='2012-02-10'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted GNU ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+  linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+  knetbsd*-gnu* | netbsd*-gnu* | \
+  kopensolaris*-gnu* | \
+  storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  android-linux)
+    os=-linux-android
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple | -axis | -knuth | -cray | -microblaze)
+               os=
+               basic_machine=$1
+               ;;
+       -bluegene*)
+               os=-cnk
+               ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -chorusos*)
+               os=-chorusos
+               basic_machine=$1
+               ;;
+       -chorusrdb)
+               os=-chorusrdb
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco6)
+               os=-sco5v6
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco5)
+               os=-sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco5v6*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       1750a | 580 \
+       | a29k \
+       | aarch64 | aarch64_be \
+       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+       | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+       | am33_2.0 \
+       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+        | be32 | be64 \
+       | bfin \
+       | c4x | clipper \
+       | d10v | d30v | dlx | dsp16xx \
+       | epiphany \
+       | fido | fr30 | frv \
+       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+       | hexagon \
+       | i370 | i860 | i960 | ia64 \
+       | ip2k | iq2000 \
+       | le32 | le64 \
+       | lm32 \
+       | m32c | m32r | m32rle | m68000 | m68k | m88k \
+       | maxq | mb | microblaze | mcore | mep | metag \
+       | mips | mipsbe | mipseb | mipsel | mipsle \
+       | mips16 \
+       | mips64 | mips64el \
+       | mips64octeon | mips64octeonel \
+       | mips64orion | mips64orionel \
+       | mips64r5900 | mips64r5900el \
+       | mips64vr | mips64vrel \
+       | mips64vr4100 | mips64vr4100el \
+       | mips64vr4300 | mips64vr4300el \
+       | mips64vr5000 | mips64vr5000el \
+       | mips64vr5900 | mips64vr5900el \
+       | mipsisa32 | mipsisa32el \
+       | mipsisa32r2 | mipsisa32r2el \
+       | mipsisa64 | mipsisa64el \
+       | mipsisa64r2 | mipsisa64r2el \
+       | mipsisa64sb1 | mipsisa64sb1el \
+       | mipsisa64sr71k | mipsisa64sr71kel \
+       | mipstx39 | mipstx39el \
+       | mn10200 | mn10300 \
+       | moxie \
+       | mt \
+       | msp430 \
+       | nds32 | nds32le | nds32be \
+       | nios | nios2 \
+       | ns16k | ns32k \
+       | open8 \
+       | or32 \
+       | pdp10 | pdp11 | pj | pjl \
+       | powerpc | powerpc64 | powerpc64le | powerpcle \
+       | pyramid \
+       | rl78 | rx \
+       | score \
+       | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+       | sh64 | sh64le \
+       | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+       | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+       | spu \
+       | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+       | ubicom32 \
+       | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+       | we32k \
+       | x86 | xc16x | xstormy16 | xtensa \
+       | z8k | z80)
+               basic_machine=$basic_machine-unknown
+               ;;
+       c54x)
+               basic_machine=tic54x-unknown
+               ;;
+       c55x)
+               basic_machine=tic55x-unknown
+               ;;
+       c6x)
+               basic_machine=tic6x-unknown
+               ;;
+       m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+               ;;
+       ms1)
+               basic_machine=mt-unknown
+               ;;
+
+       strongarm | thumb | xscale)
+               basic_machine=arm-unknown
+               ;;
+       xgate)
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       xscaleeb)
+               basic_machine=armeb-unknown
+               ;;
+
+       xscaleel)
+               basic_machine=armel-unknown
+               ;;
+
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       580-* \
+       | a29k-* \
+       | aarch64-* | aarch64_be-* \
+       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+       | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+       | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+       | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+       | avr-* | avr32-* \
+       | be32-* | be64-* \
+       | bfin-* | bs2000-* \
+       | c[123]* | c30-* | [cjt]90-* | c4x-* \
+       | clipper-* | craynv-* | cydra-* \
+       | d10v-* | d30v-* | dlx-* \
+       | elxsi-* \
+       | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+       | h8300-* | h8500-* \
+       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+       | hexagon-* \
+       | i*86-* | i860-* | i960-* | ia64-* \
+       | ip2k-* | iq2000-* \
+       | le32-* | le64-* \
+       | lm32-* \
+       | m32c-* | m32r-* | m32rle-* \
+       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+       | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
+       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+       | mips16-* \
+       | mips64-* | mips64el-* \
+       | mips64octeon-* | mips64octeonel-* \
+       | mips64orion-* | mips64orionel-* \
+       | mips64r5900-* | mips64r5900el-* \
+       | mips64vr-* | mips64vrel-* \
+       | mips64vr4100-* | mips64vr4100el-* \
+       | mips64vr4300-* | mips64vr4300el-* \
+       | mips64vr5000-* | mips64vr5000el-* \
+       | mips64vr5900-* | mips64vr5900el-* \
+       | mipsisa32-* | mipsisa32el-* \
+       | mipsisa32r2-* | mipsisa32r2el-* \
+       | mipsisa64-* | mipsisa64el-* \
+       | mipsisa64r2-* | mipsisa64r2el-* \
+       | mipsisa64sb1-* | mipsisa64sb1el-* \
+       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+       | mipstx39-* | mipstx39el-* \
+       | mmix-* \
+       | mt-* \
+       | msp430-* \
+       | nds32-* | nds32le-* | nds32be-* \
+       | nios-* | nios2-* \
+       | none-* | np1-* | ns16k-* | ns32k-* \
+       | open8-* \
+       | orion-* \
+       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+       | pyramid-* \
+       | rl78-* | romp-* | rs6000-* | rx-* \
+       | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+       | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+       | sparclite-* \
+       | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+       | tahoe-* \
+       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+       | tile*-* \
+       | tron-* \
+       | ubicom32-* \
+       | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+       | vax-* \
+       | we32k-* \
+       | x86-* | x86_64-* | xc16x-* | xps100-* \
+       | xstormy16-* | xtensa*-* \
+       | ymp-* \
+       | z8k-* | z80-*)
+               ;;
+       # Recognize the basic CPU types without company name, with glob match.
+       xtensa*)
+               basic_machine=$basic_machine-unknown
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       386bsd)
+               basic_machine=i386-unknown
+               os=-bsd
+               ;;
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       abacus)
+               basic_machine=abacus-unknown
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amd64)
+               basic_machine=x86_64-pc
+               ;;
+       amd64-*)
+               basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-unknown
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-unknown
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-unknown
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
+       aros)
+               basic_machine=i386-pc
+               os=-aros
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       blackfin)
+               basic_machine=bfin-unknown
+               os=-linux
+               ;;
+       blackfin-*)
+               basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       bluegene*)
+               basic_machine=powerpc-ibm
+               os=-cnk
+               ;;
+       c54x-*)
+               basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       c55x-*)
+               basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       c6x-*)
+               basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       c90)
+               basic_machine=c90-cray
+               os=-unicos
+               ;;
+       cegcc)
+               basic_machine=arm-unknown
+               os=-cegcc
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | j90)
+               basic_machine=j90-cray
+               os=-unicos
+               ;;
+       craynv)
+               basic_machine=craynv-cray
+               os=-unicosmp
+               ;;
+       cr16 | cr16-*)
+               basic_machine=cr16-unknown
+               os=-elf
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       crisv32 | crisv32-* | etraxfs*)
+               basic_machine=crisv32-axis
+               ;;
+       cris | cris-* | etrax*)
+               basic_machine=cris-axis
+               ;;
+       crx)
+               basic_machine=crx-unknown
+               os=-elf
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       decsystem10* | dec10*)
+               basic_machine=pdp10-dec
+               os=-tops10
+               ;;
+       decsystem20* | dec20*)
+               basic_machine=pdp10-dec
+               os=-tops20
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       dicos)
+               basic_machine=i686-pc
+               os=-dicos
+               ;;
+       djgpp)
+               basic_machine=i586-pc
+               os=-msdosdjgpp
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       go32)
+               basic_machine=i386-pc
+               os=-go32
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
+               ;;
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               ;;
+       i*86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i*86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i*86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i*86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m68knommu)
+               basic_machine=m68k-unknown
+               os=-linux
+               ;;
+       m68knommu-*)
+               basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+       microblaze)
+               basic_machine=microblaze-xilinx
+               ;;
+       mingw32)
+               basic_machine=i386-pc
+               os=-mingw32
+               ;;
+       mingw32ce)
+               basic_machine=arm-unknown
+               os=-mingw32ce
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       morphos)
+               basic_machine=powerpc-unknown
+               os=-morphos
+               ;;
+       msdos)
+               basic_machine=i386-pc
+               os=-msdos
+               ;;
+       ms1-*)
+               basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+               ;;
+       msys)
+               basic_machine=i386-pc
+               os=-msys
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+       nacl)
+               basic_machine=le32-unknown
+               os=-nacl
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
+       netwinder)
+               basic_machine=armv4l-rebel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       nonstopux)
+               basic_machine=mips-compaq
+               os=-nonstopux
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       neo-tandem)
+               basic_machine=neo-tandem
+               ;;
+       nse-tandem)
+               basic_machine=nse-tandem
+               ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       openrisc | openrisc-*)
+               basic_machine=or32-unknown
+               ;;
+       os400)
+               basic_machine=powerpc-ibm
+               os=-os400
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       parisc)
+               basic_machine=hppa-unknown
+               os=-linux
+               ;;
+       parisc-*)
+               basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+               os=-linux
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+       pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pc98)
+               basic_machine=i386-pc
+               ;;
+       pc98-*)
+               basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium | p5 | k5 | k6 | nexgen | viac3)
+               basic_machine=i586-pc
+               ;;
+       pentiumpro | p6 | 6x86 | athlon | athlon_*)
+               basic_machine=i686-pc
+               ;;
+       pentiumii | pentium2 | pentiumiii | pentium3)
+               basic_machine=i686-pc
+               ;;
+       pentium4)
+               basic_machine=i786-pc
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium4-*)
+               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=power-ibm
+               ;;
+       ppc | ppcbe)    basic_machine=powerpc-unknown
+               ;;
+       ppc-* | ppcbe-*)
+               basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+               basic_machine=powerpc64le-unknown
+               ;;
+       ppc64le-* | powerpc64little-*)
+               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       pw32)
+               basic_machine=i586-unknown
+               os=-pw32
+               ;;
+       rdos)
+               basic_machine=i386-pc
+               os=-rdos
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       s390 | s390-*)
+               basic_machine=s390-ibm
+               ;;
+       s390x | s390x-*)
+               basic_machine=s390x-ibm
+               ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       sb1)
+               basic_machine=mipsisa64sb1-unknown
+               ;;
+       sb1el)
+               basic_machine=mipsisa64sb1el-unknown
+               ;;
+       sde)
+               basic_machine=mipsisa32-sde
+               os=-elf
+               ;;
+       sei)
+               basic_machine=mips-sei
+               os=-seiux
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sh5el)
+               basic_machine=sh5le-unknown
+               ;;
+       sh64)
+               basic_machine=sh64-unknown
+               ;;
+       sparclite-wrs | simso-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
+       strongarm-* | thumb-*)
+               basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       t3e)
+               basic_machine=alphaev5-cray
+               os=-unicos
+               ;;
+       t90)
+               basic_machine=t90-cray
+               os=-unicos
+               ;;
+       tile*)
+               basic_machine=$basic_machine-unknown
+               os=-linux-gnu
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       toad1)
+               basic_machine=pdp10-xkl
+               os=-tops20
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       tpf)
+               basic_machine=s390x-ibm
+               os=-tpf
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+       xbox)
+               basic_machine=i686-pc
+               os=-mingw32
+               ;;
+       xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       xscale-* | xscalee[bl]-*)
+               basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+               ;;
+       ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
+       z80-*-coff)
+               basic_machine=z80-unknown
+               os=-sim
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       mmix)
+               basic_machine=mmix-knuth
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp10)
+               # there are many clones, so DEC is not a safe bet
+               basic_machine=pdp10-unknown
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+               basic_machine=sh-unknown
+               ;;
+       sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+               basic_machine=sparc-sun
+               ;;
+       cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       *-unknown)
+               # Make sure to match an already-canonicalized machine name.
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+       # First match some system type aliases
+       # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+       -auroraux)
+               os=-auroraux
+               ;;
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+             | -sym* | -kopensolaris* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* | -aros* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+             | -openbsd* | -solidbsd* \
+             | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+             | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -chorusos* | -chorusrdb* | -cegcc* \
+             | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -linux-android* \
+             | -linux-newlib* | -linux-uclibc* \
+             | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+             | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+             | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+             | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i*86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto-qnx*)
+               ;;
+       -nto*)
+               os=`echo $os | sed -e 's|nto|nto-qnx|'`
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
+       -linux-dietlibc)
+               os=-linux-dietlibc
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -opened*)
+               os=-openedition
+               ;;
+       -os400*)
+               os=-os400
+               ;;
+       -wince*)
+               os=-wince
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -atheos*)
+               os=-atheos
+               ;;
+       -syllable*)
+               os=-syllable
+               ;;
+       -386bsd)
+               os=-bsd
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -nova*)
+               os=-rtmk-nova
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       -nsk*)
+               os=-nsk
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+       -tpf*)
+               os=-tpf
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+               os=-mint
+               ;;
+       -aros*)
+               os=-aros
+               ;;
+       -kaos*)
+               os=-kaos
+               ;;
+       -zvmoe)
+               os=-zvmoe
+               ;;
+       -dicos*)
+               os=-dicos
+               ;;
+       -nacl*)
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+       score-*)
+               os=-elf
+               ;;
+       spu-*)
+               os=-elf
+               ;;
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-rebel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+       c4x-* | tic4x-*)
+               os=-coff
+               ;;
+       tic54x-*)
+               os=-coff
+               ;;
+       tic55x-*)
+               os=-coff
+               ;;
+       tic6x-*)
+               os=-coff
+               ;;
+       # This must come before the *-dec entry.
+       pdp10-*)
+               os=-tops20
+               ;;
+       pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+       mep-*)
+               os=-elf
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
+       or32-*)
+               os=-coff
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-haiku)
+               os=-haiku
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-knuth)
+               os=-mmixware
+               ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+       *-gould)
+               os=-sysv
+               ;;
+       *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+       *-sgi)
+               os=-irix
+               ;;
+       *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=-uxpv
+               ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -cnk*|-aix*)
+                               vendor=ibm
+                               ;;
+                       -beos*)
+                               vendor=be
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs* | -opened*)
+                               vendor=ibm
+                               ;;
+                       -os400*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -tpf*)
+                               vendor=ibm
+                               ;;
+                       -vxsim* | -vxworks* | -windiss*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       -vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..22a9b13
--- /dev/null
+++ b/configure
@@ -0,0 +1,16114 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for bluez 4.101.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+
+  test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || (
+    ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+    ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+    ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+    PATH=/empty FPATH=/empty; export PATH FPATH
+    test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\
+      || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+        /*)
+          for as_base in sh bash ksh sh5; do
+            # Try only shells that exist, to save several forks.
+            as_shell=$as_dir/$as_base
+            if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+                   { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+                  if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+          done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+             { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='bluez'
+PACKAGE_TARNAME='bluez'
+PACKAGE_VERSION='4.101'
+PACKAGE_STRING='bluez 4.101'
+PACKAGE_BUGREPORT=''
+PACKAGE_URL=''
+
+ac_default_prefix=/usr/local
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIBOBJS
+SYSTEMD_FALSE
+SYSTEMD_TRUE
+SYSTEMD_UNITDIR
+GATTMODULES_FALSE
+GATTMODULES_TRUE
+WIIMOTEPLUGIN_FALSE
+WIIMOTEPLUGIN_TRUE
+DBUSOOBPLUGIN_FALSE
+DBUSOOBPLUGIN_TRUE
+MAEMO6PLUGIN_FALSE
+MAEMO6PLUGIN_TRUE
+DATAFILES_FALSE
+DATAFILES_TRUE
+DFUTOOL_FALSE
+DFUTOOL_TRUE
+HID2HCI_FALSE
+HID2HCI_TRUE
+PCMCIA_FALSE
+PCMCIA_TRUE
+BCCMD_FALSE
+BCCMD_TRUE
+TOOLS_FALSE
+TOOLS_TRUE
+TEST_FALSE
+TEST_TRUE
+CUPS_FALSE
+CUPS_TRUE
+DUND_FALSE
+DUND_TRUE
+PAND_FALSE
+PAND_TRUE
+HIDD_FALSE
+HIDD_TRUE
+PNATPLUGIN_FALSE
+PNATPLUGIN_TRUE
+READLINE_FALSE
+READLINE_TRUE
+HAL_FALSE
+HAL_TRUE
+MCAP_FALSE
+MCAP_TRUE
+HEALTHPLUGIN_FALSE
+HEALTHPLUGIN_TRUE
+SERVICEPLUGIN_FALSE
+SERVICEPLUGIN_TRUE
+SAPPLUGIN_FALSE
+SAPPLUGIN_TRUE
+NETWORKPLUGIN_FALSE
+NETWORKPLUGIN_TRUE
+SERIALPLUGIN_FALSE
+SERIALPLUGIN_TRUE
+INPUTPLUGIN_FALSE
+INPUTPLUGIN_TRUE
+AUDIOPLUGIN_FALSE
+AUDIOPLUGIN_TRUE
+GSTREAMER_FALSE
+GSTREAMER_TRUE
+ALSA_FALSE
+ALSA_TRUE
+SBC_FALSE
+SBC_TRUE
+USB_FALSE
+USB_TRUE
+SNDFILE_FALSE
+SNDFILE_TRUE
+MISC_LDFLAGS
+MISC_CFLAGS
+TELEPHONY_DRIVER
+SAP_DRIVER
+CHECK_LIBS
+CHECK_CFLAGS
+READLINE_LIBS
+SNDFILE_LIBS
+SNDFILE_CFLAGS
+UDEV_LIBS
+UDEV_CFLAGS
+USB_LIBS
+USB_CFLAGS
+GSTREAMER_PLUGINSDIR
+GSTREAMER_LIBS
+GSTREAMER_CFLAGS
+ALSA_LIBS
+ALSA_CFLAGS
+GLIB_LIBS
+GLIB_CFLAGS
+DBUS_LIBS
+DBUS_CFLAGS
+CPP
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+MANIFEST_TOOL
+RANLIB
+ac_ct_AR
+AR
+DLLTOOL
+OBJDUMP
+LN_S
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+LIBTOOL
+LEXLIB
+LEX_OUTPUT_ROOT
+LEX
+YFLAGS
+YACC
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+am__nodep
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+WARNING_CFLAGS
+UDEV_DIR
+STORAGEDIR
+CONFIGDIR
+PKG_CONFIG_LIBDIR
+PKG_CONFIG_PATH
+PKG_CONFIG
+MAINT
+MAINTAINER_MODE_FALSE
+MAINTAINER_MODE_TRUE
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+AM_DEFAULT_V
+AM_V
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_silent_rules
+enable_maintainer_mode
+enable_dependency_tracking
+enable_static
+enable_shared
+with_pic
+enable_fast_install
+with_gnu_ld
+with_sysroot
+enable_libtool_lock
+with_ouifile
+enable_optimization
+enable_fortify
+enable_pie
+enable_network
+enable_sap
+with_sap
+enable_serial
+enable_input
+enable_audio
+enable_service
+enable_health
+enable_pnat
+enable_gstreamer
+enable_alsa
+enable_usb
+enable_tools
+enable_bccmd
+enable_pcmcia
+enable_hid2hci
+enable_dfutool
+enable_hidd
+enable_pand
+enable_dund
+enable_cups
+enable_test
+enable_datafiles
+enable_debug
+with_telephony
+enable_maemo6
+enable_dbusoob
+enable_wiimote
+enable_hal
+enable_gatt
+with_systemdunitdir
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+PKG_CONFIG
+PKG_CONFIG_PATH
+PKG_CONFIG_LIBDIR
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+YACC
+YFLAGS
+CPP
+DBUS_CFLAGS
+DBUS_LIBS
+GLIB_CFLAGS
+GLIB_LIBS
+ALSA_CFLAGS
+ALSA_LIBS
+GSTREAMER_CFLAGS
+GSTREAMER_LIBS
+USB_CFLAGS
+USB_LIBS
+UDEV_CFLAGS
+UDEV_LIBS
+SNDFILE_CFLAGS
+SNDFILE_LIBS
+CHECK_CFLAGS
+CHECK_LIBS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_myself" : 'X\(//\)[^/]' \| \
+        X"$as_myself" : 'X\(//\)$' \| \
+        X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures bluez 4.101 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/bluez]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of bluez 4.101:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-silent-rules          less verbose build output (undo: `make V=1')
+  --disable-silent-rules         verbose build output (undo: `make V=0')
+  --enable-maintainer-mode  enable make rules and dependencies not useful
+                         (and sometimes confusing) to the casual installer
+  --disable-dependency-tracking  speeds up one-time build
+  --enable-dependency-tracking   do not reject slow dependency extractors
+  --enable-static[=PKGS]  build static libraries [default=no]
+  --enable-shared[=PKGS]  build shared libraries [default=yes]
+  --enable-fast-install[=PKGS]
+                          optimize for fast installation [default=yes]
+  --disable-libtool-lock  avoid locking (might break parallel builds)
+  --disable-optimization  disable code optimization
+  --disable-fortify       disable compile time buffer checks
+  --disable-pie           disable position independent executables flag
+  --disable-network       disable network plugin
+  --enable-sap            enable sap plugin
+  --disable-serial        disable serial plugin
+  --disable-input         disable input plugin
+  --disable-audio         disable audio plugin
+  --disable-service       disable service plugin
+  --enable-health         enable health plugin
+  --enable-pnat           enable pnat plugin
+  --enable-gstreamer      enable GStreamer support
+  --enable-alsa           enable ALSA support
+  --enable-usb            enable USB support
+  --enable-tools          install Bluetooth utilities
+  --enable-bccmd          install BCCMD interface utility
+  --enable-pcmcia         install PCMCIA serial script
+  --enable-hid2hci        install HID mode switching utility
+  --enable-dfutool        install DFU firmware upgrade utility
+  --enable-hidd           install HID daemon
+  --enable-pand           install PAN daemon
+  --enable-dund           install DUN daemon
+  --enable-cups           install CUPS backend support
+  --enable-test           install test programs
+  --enable-datafiles      install Bluetooth configuration and data files
+  --enable-debug          enable compiling with debugging information
+  --enable-maemo6         compile with maemo6 plugin
+  --enable-dbusoob        compile with D-Bus OOB plugin
+  --enable-wiimote        compile with Wii Remote plugin
+  --enable-hal            Use HAL to determine adapter class
+  --enable-gatt           enable gatt module
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-pic[=PKGS]       try to use only PIC/non-PIC objects [default=use
+                          both]
+  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
+  --with-sysroot=DIR Search for dependent libraries within DIR
+                        (or the compiler's sysroot if not specified).
+  --with-ouifile=PATH     Path to the oui.txt file [auto]
+  --with-sap=DRIVER       select SAP driver
+  --with-telephony=DRIVER select telephony driver
+  --with-systemdunitdir=DIR
+                          path to systemd system service directory
+
+Some influential environment variables:
+  PKG_CONFIG  path to pkg-config utility
+  PKG_CONFIG_PATH
+              directories to add to pkg-config's search path
+  PKG_CONFIG_LIBDIR
+              path overriding pkg-config's built-in search path
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  YACC        The `Yet Another Compiler Compiler' implementation to use.
+              Defaults to the first program found out of: `bison -y', `byacc',
+              `yacc'.
+  YFLAGS      The list of arguments that will be passed by default to $YACC.
+              This script will default YFLAGS to the empty string to avoid a
+              default value of `-d' given by some make applications.
+  CPP         C preprocessor
+  DBUS_CFLAGS C compiler flags for DBUS, overriding pkg-config
+  DBUS_LIBS   linker flags for DBUS, overriding pkg-config
+  GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config
+  GLIB_LIBS   linker flags for GLIB, overriding pkg-config
+  ALSA_CFLAGS C compiler flags for ALSA, overriding pkg-config
+  ALSA_LIBS   linker flags for ALSA, overriding pkg-config
+  GSTREAMER_CFLAGS
+              C compiler flags for GSTREAMER, overriding pkg-config
+  GSTREAMER_LIBS
+              linker flags for GSTREAMER, overriding pkg-config
+  USB_CFLAGS  C compiler flags for USB, overriding pkg-config
+  USB_LIBS    linker flags for USB, overriding pkg-config
+  UDEV_CFLAGS C compiler flags for UDEV, overriding pkg-config
+  UDEV_LIBS   linker flags for UDEV, overriding pkg-config
+  SNDFILE_CFLAGS
+              C compiler flags for SNDFILE, overriding pkg-config
+  SNDFILE_LIBS
+              linker flags for SNDFILE, overriding pkg-config
+  CHECK_CFLAGS
+              C compiler flags for CHECK, overriding pkg-config
+  CHECK_LIBS  linker flags for CHECK, overriding pkg-config
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+bluez configure 4.101
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: program exited with status $ac_status" >&5
+       $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if eval \${$3+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+  # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_header_compiler=yes
+else
+  ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  ac_header_preproc=yes
+else
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+  yes:no: )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+  no:yes:* )
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2:     check for missing prerequisite headers?" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+    ;;
+esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by bluez $as_me 4.101, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       $as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       # differences in whitespace do not lead to failure.
+       ac_old_val_w=`echo x $ac_old_val`
+       ac_new_val_w=`echo x $ac_new_val`
+       if test "$ac_old_val_w" != "$ac_new_val_w"; then
+         { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+         ac_cache_corrupted=:
+       else
+         { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+         eval $ac_var=\$ac_old_val
+       fi
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+am__api_version='1.11'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+  if test -f "$ac_dir/install-sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f "$ac_dir/install.sh"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f "$ac_dir/shtool"; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
+
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+  ./ | .// | /[cC]/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+       if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+         if test $ac_prog = install &&
+           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         elif test $ac_prog = install &&
+           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # program-specific install script used by HP pwplus--don't use.
+           :
+         else
+           rm -rf conftest.one conftest.two conftest.dir
+           echo one > conftest.one
+           echo two > conftest.two
+           mkdir conftest.dir
+           if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+             test -s conftest.one && test -s conftest.two &&
+             test -s conftest.dir/conftest.one &&
+             test -s conftest.dir/conftest.two
+           then
+             ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+             break 3
+           fi
+         fi
+       fi
+      done
+    done
+    ;;
+esac
+
+  done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    INSTALL=$ac_install_sh
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name.  Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+  *[\\\"\#\$\&\'\`$am_lf]*)
+    as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+  *[\\\"\#\$\&\'\`$am_lf\ \    ]*)
+    as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+   if test "$*" = "X"; then
+      # -L didn't work.
+      set X `ls -t "$srcdir/configure" conftest.file`
+   fi
+   rm -f conftest.file
+   if test "$*" != "X $srcdir/configure conftest.file" \
+      && test "$*" != "X conftest.file $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      as_fn_error $? "ls -t appears to fail.  Make sure there is not a broken
+alias in your environment" "$LINENO" 5
+   fi
+
+   test "$2" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+test "$program_prefix" != NONE &&
+  program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+if test x"${MISSING+set}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\    *)
+    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+  *)
+    MISSING="\${SHELL} $am_aux_dir/missing" ;;
+  esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh}" != xset; then
+  case $am_aux_dir in
+  *\ * | *\    *)
+    install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+  *)
+    install_sh="\${SHELL} $am_aux_dir/install-sh"
+  esac
+fi
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'.  However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+  if ${ac_cv_path_mkdir+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in mkdir gmkdir; do
+        for ac_exec_ext in '' $ac_executable_extensions; do
+          as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
+          case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+            'mkdir (GNU coreutils) '* | \
+            'mkdir (coreutils) '* | \
+            'mkdir (fileutils) '4.1*)
+              ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+              break 3;;
+          esac
+        done
+       done
+  done
+IFS=$as_save_IFS
+
+fi
+
+  test -d ./--version && rmdir ./--version
+  if test "${ac_cv_path_mkdir+set}" = set; then
+    MKDIR_P="$ac_cv_path_mkdir -p"
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for MKDIR_P within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    MKDIR_P="$ac_install_sh -d"
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+  [\\/$]* | ?:[\\/]*) ;;
+  */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+       @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+  *@@@%%%=?*=@@@%%%*)
+    eval ac_cv_prog_make_${ac_make}_set=yes;;
+  *)
+    eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+  SET_MAKE=
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+  # is not polluted with repeated "-I."
+  am__isrc=' -I$(srcdir)'
+  # test to see if srcdir already configured
+  if test -f $srcdir/config.status; then
+    as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+  fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='bluez'
+ VERSION='4.101'
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.  Yes, it's still used
+# in the wild :-(  We should find a proper way to deprecate it ...
+AMTAR='$${TAR-tar}'
+
+am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
+
+
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+  enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in
+yes) AM_DEFAULT_VERBOSITY=0;;
+no)  AM_DEFAULT_VERBOSITY=1;;
+*)   AM_DEFAULT_VERBOSITY=0;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+       @$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+  am_cv_make_support_nested_variables=yes
+else
+  am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+    AM_V='$(V)'
+  AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+  AM_V=$AM_DEFAULT_VERBOSITY
+  AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
+$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
+    # Check whether --enable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then :
+  enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
+else
+  USE_MAINTAINER_MODE=no
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5
+$as_echo "$USE_MAINTAINER_MODE" >&6; }
+   if test $USE_MAINTAINER_MODE = yes; then
+  MAINTAINER_MODE_TRUE=
+  MAINTAINER_MODE_FALSE='#'
+else
+  MAINTAINER_MODE_TRUE='#'
+  MAINTAINER_MODE_FALSE=
+fi
+
+  MAINT=$MAINTAINER_MODE_TRUE
+
+
+
+
+
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+       if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+  ac_pt_PKG_CONFIG=$PKG_CONFIG
+  # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $ac_pt_PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_pt_PKG_CONFIG" = x; then
+    PKG_CONFIG=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    PKG_CONFIG=$ac_pt_PKG_CONFIG
+  fi
+else
+  PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+       _pkg_min_version=0.9.0
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+       if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       else
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+               PKG_CONFIG=""
+       fi
+fi
+
+
+
+
+       if (test "${prefix}" = "NONE"); then
+                               if (test "$sysconfdir" = '${prefix}/etc'); then
+                       sysconfdir='/etc'
+
+               fi
+
+                               if (test "$localstatedir" = '${prefix}/var'); then
+                       localstatedir='/var'
+
+               fi
+
+                               if (test "$libexecdir" = '${exec_prefix}/libexec'); then
+                       libexecdir='/lib'
+
+               fi
+
+                               if (test "$mandir" = '${prefix}/man'); then
+                       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
+
+
+cat >>confdefs.h <<_ACEOF
+#define CONFIGDIR "${configdir}"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define STORAGEDIR "${storagedir}"
+_ACEOF
+
+
+       CONFIGDIR="${configdir}"
+
+       STORAGEDIR="${storagedir}"
+
+
+       UDEV_DIR="`$PKG_CONFIG --variable=udevdir udev`"
+       if (test -z "${UDEV_DIR}"); then
+               UDEV_DIR="/lib/udev"
+       fi
+
+
+
+
+       with_cflags=""
+       if (test "$USE_MAINTAINER_MODE" = "yes"); then
+               with_cflags="$with_cflags -Wall -Werror -Wextra"
+               with_cflags="$with_cflags -Wno-unused-parameter"
+               with_cflags="$with_cflags -Wno-missing-field-initializers"
+               with_cflags="$with_cflags -Wdeclaration-after-statement"
+               with_cflags="$with_cflags -Wmissing-declarations"
+               with_cflags="$with_cflags -Wredundant-decls"
+               with_cflags="$with_cflags -Wcast-align"
+               with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
+       fi
+
+       WARNING_CFLAGS=$with_cflags
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+       if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+       @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+  am__include=include
+  am__quote=
+  _am_result=GNU
+  ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   case `$am_make -s -f confmf 2> /dev/null` in #(
+   *the\ am__doit\ target*)
+     am__include=.include
+     am__quote="\""
+     _am_result=BSD
+     ;;
+   esac
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+  enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+  am__nodep='_no'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+  AMDEP_TRUE=
+  AMDEP_FALSE='#'
+else
+  AMDEP_TRUE='#'
+  AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC"   am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CC_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  am__universal=false
+  case " $depcc " in #(
+     *\ -arch\ *\ -arch\ *) am__universal=true ;;
+     esac
+
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+      # Solaris 8's {/usr,}/bin/sh.
+      touch sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.  Also, some Intel
+    # versions had trouble with output in subdirs
+    am__obj=sub/conftest.${OBJEXT-o}
+    am__minus_obj="-o $am__obj"
+    case $depmode in
+    gcc)
+      # This depmode causes a compiler race in universal mode.
+      test "$am__universal" = false || continue
+      ;;
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+       continue
+      else
+       break
+      fi
+      ;;
+    msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+      # This compiler won't grok `-c -o', but also, the minuso test has
+      # not run yet.  These depmodes are late enough in the game, and
+      # so weak that their functioning should not be impacted.
+      am__obj=conftest.${OBJEXT-o}
+      am__minus_obj=
+      ;;
+    none) break ;;
+    esac
+    if depmode=$depmode \
+       source=sub/conftest.c object=$am__obj \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CC_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+  am__fastdepCC_TRUE=
+  am__fastdepCC_FALSE='#'
+else
+  am__fastdepCC_TRUE='#'
+  am__fastdepCC_FALSE=
+fi
+
+
+if test "x$CC" != xcc; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC and cc understand -c and -o together" >&5
+$as_echo_n "checking whether $CC and cc understand -c and -o together... " >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether cc understands -c and -o together" >&5
+$as_echo_n "checking whether cc understands -c and -o together... " >&6; }
+fi
+set dummy $CC; ac_cc=`$as_echo "$2" |
+                     sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'`
+if eval \${ac_cv_prog_cc_${ac_cc}_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+# Make sure it works both with $CC and with simple cc.
+# We do the test twice because some compilers refuse to overwrite an
+# existing .o file with -o, though they will create one.
+ac_try='$CC -c conftest.$ac_ext -o conftest2.$ac_objext >&5'
+rm -f conftest2.*
+if { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } &&
+   test -f conftest2.$ac_objext && { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; };
+then
+  eval ac_cv_prog_cc_${ac_cc}_c_o=yes
+  if test "x$CC" != xcc; then
+    # Test first that cc exists at all.
+    if { ac_try='cc -c conftest.$ac_ext >&5'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+      ac_try='cc -c conftest.$ac_ext -o conftest2.$ac_objext >&5'
+      rm -f conftest2.*
+      if { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } &&
+        test -f conftest2.$ac_objext && { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; };
+      then
+       # cc works too.
+       :
+      else
+       # cc exists but doesn't like -o.
+       eval ac_cv_prog_cc_${ac_cc}_c_o=no
+      fi
+    fi
+  fi
+else
+  eval ac_cv_prog_cc_${ac_cc}_c_o=no
+fi
+rm -f core conftest*
+
+fi
+if eval test \$ac_cv_prog_cc_${ac_cc}_c_o = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+$as_echo "#define NO_MINUS_C_MINUS_O 1" >>confdefs.h
+
+fi
+
+# FIXME: we rely on the cache variable name because
+# there is no other way.
+set dummy $CC
+am_cc=`echo $2 | sed 's/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/'`
+eval am_t=\$ac_cv_prog_cc_${am_cc}_c_o
+if test "$am_t" != yes; then
+   # Losing compiler, so override with the script.
+   # FIXME: It is wrong to rewrite CC.
+   # But if we don't then we get into trouble of one sort or another.
+   # A longer-term fix would be to have automake use am__CC in this case,
+   # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+   CC="$am_aux_dir/compile $CC"
+fi
+
+
+
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${CC-cc} accepts -fPIE" >&5
+$as_echo_n "checking whether ${CC-cc} accepts -fPIE... " >&6; }
+if ${ac_cv_prog_cc_pie+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+               echo 'void f(){}' > conftest.c
+               if test -z "`${CC-cc} -fPIE -pie -c conftest.c 2>&1`"; then
+                       ac_cv_prog_cc_pie=yes
+               else
+                       ac_cv_prog_cc_pie=no
+               fi
+               rm -rf conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_pie" >&5
+$as_echo "$ac_cv_prog_cc_pie" >&6; }
+
+
+for ac_prog in 'bison -y' byacc
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_YACC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$YACC"; then
+  ac_cv_prog_YACC="$YACC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_YACC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+YACC=$ac_cv_prog_YACC
+if test -n "$YACC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $YACC" >&5
+$as_echo "$YACC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$YACC" && break
+done
+test -n "$YACC" || YACC="yacc"
+
+
+for ac_prog in flex lex
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LEX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$LEX"; then
+  ac_cv_prog_LEX="$LEX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_LEX="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+LEX=$ac_cv_prog_LEX
+if test -n "$LEX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LEX" >&5
+$as_echo "$LEX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$LEX" && break
+done
+test -n "$LEX" || LEX=":"
+
+if test "x$LEX" != "x:"; then
+  cat >conftest.l <<_ACEOF
+%%
+a { ECHO; }
+b { REJECT; }
+c { yymore (); }
+d { yyless (1); }
+e { /* IRIX 6.5 flex 2.5.4 underquotes its yyless argument.  */
+    yyless ((input () != 0)); }
+f { unput (yytext[0]); }
+. { BEGIN INITIAL; }
+%%
+#ifdef YYTEXT_POINTER
+extern char *yytext;
+#endif
+int
+main (void)
+{
+  return ! yylex () + ! yywrap ();
+}
+_ACEOF
+{ { ac_try="$LEX conftest.l"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$LEX conftest.l") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking lex output file root" >&5
+$as_echo_n "checking lex output file root... " >&6; }
+if ${ac_cv_prog_lex_root+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+if test -f lex.yy.c; then
+  ac_cv_prog_lex_root=lex.yy
+elif test -f lexyy.c; then
+  ac_cv_prog_lex_root=lexyy
+else
+  as_fn_error $? "cannot find output from $LEX; giving up" "$LINENO" 5
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_lex_root" >&5
+$as_echo "$ac_cv_prog_lex_root" >&6; }
+LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root
+
+if test -z "${LEXLIB+set}"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking lex library" >&5
+$as_echo_n "checking lex library... " >&6; }
+if ${ac_cv_lib_lex+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    ac_save_LIBS=$LIBS
+    ac_cv_lib_lex='none needed'
+    for ac_lib in '' -lfl -ll; do
+      LIBS="$ac_lib $ac_save_LIBS"
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+`cat $LEX_OUTPUT_ROOT.c`
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_lex=$ac_lib
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+      test "$ac_cv_lib_lex" != 'none needed' && break
+    done
+    LIBS=$ac_save_LIBS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lex" >&5
+$as_echo "$ac_cv_lib_lex" >&6; }
+  test "$ac_cv_lib_lex" != 'none needed' && LEXLIB=$ac_cv_lib_lex
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether yytext is a pointer" >&5
+$as_echo_n "checking whether yytext is a pointer... " >&6; }
+if ${ac_cv_prog_lex_yytext_pointer+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  # POSIX says lex can declare yytext either as a pointer or an array; the
+# default is implementation-dependent.  Figure out which it is, since
+# not all implementations provide the %pointer and %array declarations.
+ac_cv_prog_lex_yytext_pointer=no
+ac_save_LIBS=$LIBS
+LIBS="$LEXLIB $ac_save_LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+  #define YYTEXT_POINTER 1
+`cat $LEX_OUTPUT_ROOT.c`
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_prog_lex_yytext_pointer=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_save_LIBS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_lex_yytext_pointer" >&5
+$as_echo "$ac_cv_prog_lex_yytext_pointer" >&6; }
+if test $ac_cv_prog_lex_yytext_pointer = yes; then
+
+$as_echo "#define YYTEXT_POINTER 1" >>confdefs.h
+
+fi
+rm -f conftest.l $LEX_OUTPUT_ROOT.c
+
+fi
+if test "$LEX" = :; then
+  LEX=${am_missing_run}flex
+fi
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+  [\\/$]* | ?:[\\/]*) ;;
+  */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+
+
+
+
+# Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+  enableval=$enable_static; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_static=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_static=no
+fi
+
+
+
+
+
+
+
+
+
+case `pwd` in
+  *\ * | *\    *)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.4.2'
+macro_revision='1.3337'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+  as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+  as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+  as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+    as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
+$as_echo_n "checking how to print strings... " >&6; }
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+   test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='printf %s\n'
+else
+  # Use this function as a fallback that always works.
+  func_fallback_echo ()
+  {
+    eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+  }
+  ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO ""
+}
+
+case "$ECHO" in
+  printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5
+$as_echo "printf" >&6; } ;;
+  print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
+$as_echo "print -r" >&6; } ;;
+  *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5
+$as_echo "cat" >&6; } ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+            ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+     for ac_i in 1 2 3 4 5 6 7; do
+       ac_script="$ac_script$as_nl$ac_script"
+     done
+     echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+     { ac_script=; unset ac_script;}
+     if test -z "$SED"; then
+  ac_path_SED_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_SED" || continue
+# Check for GNU ac_path_SED and select it if it is found.
+  # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+  ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo '' >> "conftest.nl"
+    "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_SED_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_SED="$ac_path_SED"
+      ac_path_SED_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_SED_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_SED"; then
+    as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+  fi
+else
+  ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+  rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in grep ggrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in egrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if ${ac_cv_path_FGREP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+   then ac_cv_path_FGREP="$GREP -F"
+   else
+     if test -z "$FGREP"; then
+  ac_path_FGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_prog in fgrep; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_FGREP" || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+  # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+  ac_count=0
+  $as_echo_n 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    $as_echo 'FGREP' >> "conftest.nl"
+    "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_FGREP="$ac_path_FGREP"
+      ac_path_FGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_FGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_FGREP"; then
+    as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_FGREP=$FGREP
+fi
+
+   fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+  withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+  with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [\\/]* | ?:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+       ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -z "$LD"; then
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+       test "$with_gnu_ld" != no && break
+       ;;
+      *)
+       test "$with_gnu_ld" != yes && break
+       ;;
+      esac
+    fi
+  done
+  IFS="$lt_save_ifs"
+else
+  lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if ${lt_cv_path_NM+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM="$NM"
+else
+  lt_nm_to_check="${ac_tool_prefix}nm"
+  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+    lt_nm_to_check="$lt_nm_to_check nm"
+  fi
+  for lt_tmp_nm in $lt_nm_to_check; do
+    lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+      IFS="$lt_save_ifs"
+      test -z "$ac_dir" && ac_dir=.
+      tmp_nm="$ac_dir/$lt_tmp_nm"
+      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+       # Check to see if the nm accepts a BSD-compat flag.
+       # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+       #   nm: unknown option "B" ignored
+       # Tru64's nm complains that /dev/null is an invalid object file
+       case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+       */dev/null* | *'Invalid file or object type'*)
+         lt_cv_path_NM="$tmp_nm -B"
+         break
+         ;;
+       *)
+         case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+         */dev/null*)
+           lt_cv_path_NM="$tmp_nm -p"
+           break
+           ;;
+         *)
+           lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+           continue # so that we can try to find one that supports BSD flags
+           ;;
+         esac
+         ;;
+       esac
+      fi
+    done
+    IFS="$lt_save_ifs"
+  done
+  : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test "$lt_cv_path_NM" != "no"; then
+  NM="$lt_cv_path_NM"
+else
+  # Didn't find any BSD compatible name lister, look for dumpbin.
+  if test -n "$DUMPBIN"; then :
+    # Let the user override the test.
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in dumpbin "link -dump"
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DUMPBIN+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DUMPBIN"; then
+  ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$DUMPBIN" && break
+  done
+fi
+if test -z "$DUMPBIN"; then
+  ac_ct_DUMPBIN=$DUMPBIN
+  for ac_prog in dumpbin "link -dump"
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DUMPBIN"; then
+  ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_DUMPBIN" && break
+done
+
+  if test "x$ac_ct_DUMPBIN" = x; then
+    DUMPBIN=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DUMPBIN=$ac_ct_DUMPBIN
+  fi
+fi
+
+    case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in
+    *COFF*)
+      DUMPBIN="$DUMPBIN -symbols"
+      ;;
+    *)
+      DUMPBIN=:
+      ;;
+    esac
+  fi
+
+  if test "$DUMPBIN" != ":"; then
+    NM="$DUMPBIN"
+  fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if ${lt_cv_nm_interface+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_nm_interface="BSD nm"
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5)
+  (eval "$ac_compile" 2>conftest.err)
+  cat conftest.err >&5
+  (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+  cat conftest.err >&5
+  (eval echo "\"\$as_me:$LINENO: output\"" >&5)
+  cat conftest.out >&5
+  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+    lt_cv_nm_interface="MS dumpbin"
+  fi
+  rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if ${lt_cv_sys_max_cmd_len+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+    i=0
+  teststring="ABCD"
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw* | cegcc*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  mint*)
+    # On MiNT this can take a long time and run out of memory.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+    # This has been around since 386BSD, at least.  Likely further.
+    if test -x /sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+    elif test -x /usr/sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+    else
+      lt_cv_sys_max_cmd_len=65536      # usable default for all BSDs
+    fi
+    # And add a safety zone
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    ;;
+
+  interix*)
+    # We know the value 262144 and hardcode it with a safety zone (like BSD)
+    lt_cv_sys_max_cmd_len=196608
+    ;;
+
+  os2*)
+    # The test takes a long time on OS/2.
+    lt_cv_sys_max_cmd_len=8192
+    ;;
+
+  osf*)
+    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+    # nice to cause kernel panics so lets avoid the loop below.
+    # First set a reasonable default.
+    lt_cv_sys_max_cmd_len=16384
+    #
+    if test -x /sbin/sysconfig; then
+      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+        *1*) lt_cv_sys_max_cmd_len=-1 ;;
+      esac
+    fi
+    ;;
+  sco3.2v5*)
+    lt_cv_sys_max_cmd_len=102400
+    ;;
+  sysv5* | sco5v6* | sysv4.2uw2*)
+    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+    if test -n "$kargmax"; then
+      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[         ]//'`
+    else
+      lt_cv_sys_max_cmd_len=32768
+    fi
+    ;;
+  *)
+    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+    if test -n "$lt_cv_sys_max_cmd_len"; then
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    else
+      # Make teststring a little bigger before we do anything with it.
+      # a 1K string should be a reasonable start.
+      for i in 1 2 3 4 5 6 7 8 ; do
+        teststring=$teststring$teststring
+      done
+      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+      # If test is not a shell built-in, we'll probably end up computing a
+      # maximum length that is only half of the actual maximum length, but
+      # we can't tell.
+      while { test "X"`env echo "$teststring$teststring" 2>/dev/null` \
+                = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+             test $i != 17 # 1/2 MB should be enough
+      do
+        i=`expr $i + 1`
+        teststring=$teststring$teststring
+      done
+      # Only check the string length outside the loop.
+      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+      teststring=
+      # Add a significant safety factor because C++ compilers can tack on
+      # massive amounts of additional arguments before passing them to the
+      # linker.  It appears as though 1/2 is a usable value.
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    fi
+    ;;
+  esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5
+$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+  test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \
+      = c,a/b,b/c, \
+    && eval 'test $(( 1 + 1 )) -eq 2 \
+    && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+  && xsi_shell=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5
+$as_echo "$xsi_shell" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5
+$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
+    >/dev/null 2>&1 \
+  && lt_shell_append=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5
+$as_echo "$lt_shell_append" >&6; }
+
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  lt_unset=unset
+else
+  lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+  lt_SP2NL='tr \040 \012'
+  lt_NL2SP='tr \015\012 \040\040'
+  ;;
+ *) # EBCDIC based system
+  lt_SP2NL='tr \100 \n'
+  lt_NL2SP='tr \r\n \100\100'
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5
+$as_echo_n "checking how to convert $build file names to $host format... " >&6; }
+if ${lt_cv_to_host_file_cmd+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+        ;;
+    esac
+    ;;
+  *-*-cygwin* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_noop
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+        ;;
+    esac
+    ;;
+  * ) # unhandled hosts (and "normal" native builds)
+    lt_cv_to_host_file_cmd=func_convert_file_noop
+    ;;
+esac
+
+fi
+
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5
+$as_echo "$lt_cv_to_host_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5
+$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; }
+if ${lt_cv_to_tool_file_cmd+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  #assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+        ;;
+    esac
+    ;;
+esac
+
+fi
+
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5
+$as_echo "$lt_cv_to_tool_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if ${lt_cv_ld_reload_flag+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    if test "$GCC" != yes; then
+      reload_cmds=false
+    fi
+    ;;
+  darwin*)
+    if test "$GCC" = yes; then
+      reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+    else
+      reload_cmds='$LD$reload_flag -o $output$reload_objs'
+    fi
+    ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OBJDUMP"; then
+  ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+  ac_ct_OBJDUMP=$OBJDUMP
+  # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OBJDUMP"; then
+  ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OBJDUMP="objdump"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OBJDUMP" = x; then
+    OBJDUMP="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OBJDUMP=$ac_ct_OBJDUMP
+  fi
+else
+  OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if ${lt_cv_deplibs_check_method+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi[45]*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump',
+  # unless we find 'file', for example because we are cross-compiling.
+  # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin.
+  if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then
+    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+    lt_cv_file_magic_cmd='func_win32_libid'
+  else
+    # Keep this pattern in sync with the one in func_win32_libid.
+    lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+    lt_cv_file_magic_cmd='$OBJDUMP -f'
+  fi
+  ;;
+
+cegcc*)
+  # use the weaker test based on 'objdump'. See mingw*.
+  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | dragonfly*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+haiku*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case $host_cpu in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+interix[3-9]*)
+  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+  lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd* | netbsdelf*-gnu)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+*nto* | *qnx*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+openbsd*)
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+rdos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.3*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  pc)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+tpf*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+  case $host_os in
+  mingw* | pw32*)
+    if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+      want_nocaseglob=yes
+    else
+      file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"`
+    fi
+    ;;
+  esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DLLTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DLLTOOL"; then
+  ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DLLTOOL=$ac_cv_prog_DLLTOOL
+if test -n "$DLLTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
+$as_echo "$DLLTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DLLTOOL"; then
+  ac_ct_DLLTOOL=$DLLTOOL
+  # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DLLTOOL"; then
+  ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DLLTOOL="dlltool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
+if test -n "$ac_ct_DLLTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
+$as_echo "$ac_ct_DLLTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_DLLTOOL" = x; then
+    DLLTOOL="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DLLTOOL=$ac_ct_DLLTOOL
+  fi
+else
+  DLLTOOL="$ac_cv_prog_DLLTOOL"
+fi
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5
+$as_echo_n "checking how to associate runtime and link libraries... " >&6; }
+if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+  # two different shell functions defined in ltmain.sh
+  # decide which to use based on capabilities of $DLLTOOL
+  case `$DLLTOOL --help 2>&1` in
+  *--identify-strict*)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+    ;;
+  *)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+    ;;
+  esac
+  ;;
+*)
+  # fallback: assume linklib IS sharedlib
+  lt_cv_sharedlib_from_linklib_cmd="$ECHO"
+  ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5
+$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; }
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in ar
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$AR" && break
+  done
+fi
+if test -z "$AR"; then
+  ac_ct_AR=$AR
+  for ac_prog in ar
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_AR"; then
+  ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_AR="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_AR" && break
+done
+
+  if test "x$ac_ct_AR" = x; then
+    AR="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    AR=$ac_ct_AR
+  fi
+fi
+
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5
+$as_echo_n "checking for archiver @FILE support... " >&6; }
+if ${lt_cv_ar_at_file+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ar_at_file=no
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  echo conftest.$ac_objext > conftest.lst
+      lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5'
+      { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+  (eval $lt_ar_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+      if test "$ac_status" -eq 0; then
+       # Ensure the archiver fails upon bogus file names.
+       rm -f conftest.$ac_objext libconftest.a
+       { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+  (eval $lt_ar_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+       if test "$ac_status" -ne 0; then
+          lt_cv_ar_at_file=@
+        fi
+      fi
+      rm -f conftest.* libconftest.a
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
+$as_echo "$lt_cv_ar_at_file" >&6; }
+
+if test "x$lt_cv_ar_at_file" = xno; then
+  archiver_list_spec=
+else
+  archiver_list_spec=$lt_cv_ar_at_file
+fi
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  openbsd*)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+    ;;
+  *)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+  darwin*)
+    lock_old_archive_extraction=yes ;;
+  *)
+    lock_old_archive_extraction=no ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[BCDT]'
+  ;;
+cygwin* | mingw* | pw32* | cegcc*)
+  symcode='[ABCDGISTW]'
+  ;;
+hpux*)
+  if test "$host_cpu" = ia64; then
+    symcode='[ABCDEGRST]'
+  fi
+  ;;
+irix* | nonstopux*)
+  symcode='[BCDEGRST]'
+  ;;
+osf*)
+  symcode='[BCDEGQRST]'
+  ;;
+solaris*)
+  symcode='[BDRT]'
+  ;;
+sco3.2v5*)
+  symcode='[DT]'
+  ;;
+sysv4.2uw2*)
+  symcode='[DT]'
+  ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+  symcode='[ABDT]'
+  ;;
+sysv4)
+  symcode='[DFNSTU]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/  {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/  {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/  {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/  {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+  symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+  # Write the raw and C identifiers.
+  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+    # Fake it for dumpbin and say T for any non-static function
+    # and D for any global variable.
+    # Also find C++ and __fastcall symbols from MSVC++,
+    # which start with @ or ?.
+    lt_cv_sys_global_symbol_pipe="$AWK '"\
+"     {last_section=section; section=\$ 3};"\
+"     /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+"     \$ 0!~/External *\|/{next};"\
+"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+"     {if(hide[section]) next};"\
+"     {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+"     {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+"     s[1]~/^[@?]/{print s[1], s[1]; next};"\
+"     s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+"     ' prfx=^$ac_symprfx"
+  else
+    lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[     ]\($symcode$symcode*\)[         ][      ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+  fi
+  lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
+  (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+       mv -f "$nlist"T "$nlist"
+      else
+       rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+       if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+         cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+/* DATA imports from DLLs on WIN32 con't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT_DLSYM_CONST
+#elif defined(__osf__)
+/* This system does not cope well with relocations in const data.  */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+         # Now generate the symbol file.
+         eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+         cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols.  */
+LT_DLSYM_CONST struct {
+  const char *name;
+  void       *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+  { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+         $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/  {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+         cat <<\_LT_EOF >> conftest.$ac_ext
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+         # Now try linking the two files.
+         mv conftest.$ac_objext conftstm.$ac_objext
+         lt_globsym_save_LIBS=$LIBS
+         lt_globsym_save_CFLAGS=$CFLAGS
+         LIBS="conftstm.$ac_objext"
+         CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+         if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s conftest${ac_exeext}; then
+           pipe_works=yes
+         fi
+         LIBS=$lt_globsym_save_LIBS
+         CFLAGS=$lt_globsym_save_CFLAGS
+       else
+         echo "cannot find nm_test_func in $nlist" >&5
+       fi
+      else
+       echo "cannot find nm_test_var in $nlist" >&5
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+    fi
+  else
+    echo "$progname: failed program was:" >&5
+    cat conftest.$ac_ext >&5
+  fi
+  rm -rf conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test "$pipe_works" = yes; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+  nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then
+  nm_file_list_spec='@'
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5
+$as_echo_n "checking for sysroot... " >&6; }
+
+# Check whether --with-sysroot was given.
+if test "${with_sysroot+set}" = set; then :
+  withval=$with_sysroot;
+else
+  with_sysroot=no
+fi
+
+
+lt_sysroot=
+case ${with_sysroot} in #(
+ yes)
+   if test "$GCC" = yes; then
+     lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+   fi
+   ;; #(
+ /*)
+   lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+   ;; #(
+ no|'')
+   ;; #(
+ *)
+   { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5
+$as_echo "${with_sysroot}" >&6; }
+   as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5
+   ;;
+esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5
+$as_echo "${lt_sysroot:-no}" >&6; }
+
+
+
+
+
+# Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then :
+  enableval=$enable_libtool_lock;
+fi
+
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `/usr/bin/file conftest.$ac_objext` in
+      *ELF-32*)
+       HPUX_IA64_MODE="32"
+       ;;
+      *ELF-64*)
+       HPUX_IA64_MODE="64"
+       ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out which ABI we are using.
+  echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    if test "$lt_cv_prog_gnu_ld" = yes; then
+      case `/usr/bin/file conftest.$ac_objext` in
+       *32-bit*)
+         LD="${LD-ld} -melf32bsmip"
+         ;;
+       *N32*)
+         LD="${LD-ld} -melf32bmipn32"
+         ;;
+       *64-bit*)
+         LD="${LD-ld} -melf64bmip"
+       ;;
+      esac
+    else
+      case `/usr/bin/file conftest.$ac_objext` in
+       *32-bit*)
+         LD="${LD-ld} -32"
+         ;;
+       *N32*)
+         LD="${LD-ld} -n32"
+         ;;
+       *64-bit*)
+         LD="${LD-ld} -64"
+         ;;
+      esac
+    fi
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `/usr/bin/file conftest.o` in
+      *32-bit*)
+       case $host in
+         x86_64-*kfreebsd*-gnu)
+           LD="${LD-ld} -m elf_i386_fbsd"
+           ;;
+         x86_64-*linux*)
+           LD="${LD-ld} -m elf_i386"
+           ;;
+         ppc64-*linux*|powerpc64-*linux*)
+           LD="${LD-ld} -m elf32ppclinux"
+           ;;
+         s390x-*linux*)
+           LD="${LD-ld} -m elf_s390"
+           ;;
+         sparc64-*linux*)
+           LD="${LD-ld} -m elf32_sparc"
+           ;;
+       esac
+       ;;
+      *64-bit*)
+       case $host in
+         x86_64-*kfreebsd*-gnu)
+           LD="${LD-ld} -m elf_x86_64_fbsd"
+           ;;
+         x86_64-*linux*)
+           LD="${LD-ld} -m elf_x86_64"
+           ;;
+         ppc*-*linux*|powerpc*-*linux*)
+           LD="${LD-ld} -m elf64ppc"
+           ;;
+         s390*-*linux*|s390*-*tpf*)
+           LD="${LD-ld} -m elf64_s390"
+           ;;
+         sparc*-*linux*)
+           LD="${LD-ld} -m elf64_sparc"
+           ;;
+       esac
+       ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -belf"
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if ${lt_cv_cc_needs_belf+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  lt_cv_cc_needs_belf=yes
+else
+  lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+     ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+  if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS="$SAVE_CFLAGS"
+  fi
+  ;;
+*-*solaris*)
+  # Find out which ABI we are using.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `/usr/bin/file conftest.o` in
+    *64-bit*)
+      case $lt_cv_prog_gnu_ld in
+      yes*)
+        case $host in
+        i?86-*-solaris*)
+          LD="${LD-ld} -m elf_x86_64"
+          ;;
+        sparc*-*-solaris*)
+          LD="${LD-ld} -m elf64_sparc"
+          ;;
+        esac
+        # GNU ld 2.21 introduced _sol2 emulations.  Use them if available.
+        if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+          LD="${LD-ld}_sol2"
+        fi
+        ;;
+      *)
+       if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+         LD="${LD-ld} -64"
+       fi
+       ;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+esac
+
+need_locks="$enable_libtool_lock"
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args.
+set dummy ${ac_tool_prefix}mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_MANIFEST_TOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$MANIFEST_TOOL"; then
+  ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL
+if test -n "$MANIFEST_TOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5
+$as_echo "$MANIFEST_TOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_MANIFEST_TOOL"; then
+  ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL
+  # Extract the first word of "mt", so it can be a program name with args.
+set dummy mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_MANIFEST_TOOL"; then
+  ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_MANIFEST_TOOL="mt"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL
+if test -n "$ac_ct_MANIFEST_TOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5
+$as_echo "$ac_ct_MANIFEST_TOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_MANIFEST_TOOL" = x; then
+    MANIFEST_TOOL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL
+  fi
+else
+  MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL"
+fi
+
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5
+$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
+if ${lt_cv_path_mainfest_tool+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_path_mainfest_tool=no
+  echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5
+  $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+  cat conftest.err >&5
+  if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+    lt_cv_path_mainfest_tool=yes
+  fi
+  rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
+$as_echo "$lt_cv_path_mainfest_tool" >&6; }
+if test "x$lt_cv_path_mainfest_tool" != xyes; then
+  MANIFEST_TOOL=:
+fi
+
+
+
+
+
+
+  case $host_os in
+    rhapsody* | darwin*)
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$DSYMUTIL"; then
+  ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+  ac_ct_DSYMUTIL=$DSYMUTIL
+  # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_DSYMUTIL"; then
+  ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_DSYMUTIL" = x; then
+    DSYMUTIL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DSYMUTIL=$ac_ct_DSYMUTIL
+  fi
+else
+  DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_NMEDIT+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$NMEDIT"; then
+  ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+  ac_ct_NMEDIT=$NMEDIT
+  # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_NMEDIT"; then
+  ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_NMEDIT="nmedit"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_NMEDIT" = x; then
+    NMEDIT=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    NMEDIT=$ac_ct_NMEDIT
+  fi
+else
+  NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LIPO+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$LIPO"; then
+  ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+  ac_ct_LIPO=$LIPO
+  # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_LIPO"; then
+  ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_LIPO="lipo"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_LIPO" = x; then
+    LIPO=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    LIPO=$ac_ct_LIPO
+  fi
+else
+  LIPO="$ac_cv_prog_LIPO"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OTOOL"; then
+  ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+  ac_ct_OTOOL=$OTOOL
+  # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OTOOL"; then
+  ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OTOOL="otool"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OTOOL" = x; then
+    OTOOL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OTOOL=$ac_ct_OTOOL
+  fi
+else
+  OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL64+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$OTOOL64"; then
+  ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+  ac_ct_OTOOL64=$OTOOL64
+  # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_OTOOL64"; then
+  ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OTOOL64="otool64"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_OTOOL64" = x; then
+    OTOOL64=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OTOOL64=$ac_ct_OTOOL64
+  fi
+else
+  OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if ${lt_cv_apple_cc_single_mod+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_apple_cc_single_mod=no
+      if test -z "${LT_MULTI_MODULE}"; then
+       # By default we will add the -single_module flag. You can override
+       # by either setting the environment variable LT_MULTI_MODULE
+       # non-empty at configure time, or by adding -multi_module to the
+       # link flags.
+       rm -rf libconftest.dylib*
+       echo "int foo(void){return 1;}" > conftest.c
+       echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+       $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+         -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+        _lt_result=$?
+       # If there is a non-empty error log, and "single_module"
+       # appears in it, assume the flag caused a linker warning
+        if test -s conftest.err && $GREP single_module conftest.err; then
+         cat conftest.err >&5
+       # Otherwise, if the output was created with a 0 exit code from
+       # the compiler, it worked.
+       elif test -f libconftest.dylib && test $_lt_result -eq 0; then
+         lt_cv_apple_cc_single_mod=yes
+       else
+         cat conftest.err >&5
+       fi
+       rm -rf libconftest.dylib*
+       rm -f conftest.*
+      fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_exported_symbols_list=no
+      save_LDFLAGS=$LDFLAGS
+      echo "_main" > conftest.sym
+      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  lt_cv_ld_exported_symbols_list=yes
+else
+  lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+       LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5
+$as_echo_n "checking for -force_load linker flag... " >&6; }
+if ${lt_cv_ld_force_load+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_ld_force_load=no
+      cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
+      $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
+      echo "$AR cru libconftest.a conftest.o" >&5
+      $AR cru libconftest.a conftest.o 2>&5
+      echo "$RANLIB libconftest.a" >&5
+      $RANLIB libconftest.a 2>&5
+      cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5
+      $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+      _lt_result=$?
+      if test -s conftest.err && $GREP force_load conftest.err; then
+       cat conftest.err >&5
+      elif test -f conftest && test $_lt_result -eq 0 && $GREP forced_load conftest >/dev/null 2>&1 ; then
+       lt_cv_ld_force_load=yes
+      else
+       cat conftest.err >&5
+      fi
+        rm -f conftest.err libconftest.a conftest conftest.c
+        rm -rf conftest.dSYM
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5
+$as_echo "$lt_cv_ld_force_load" >&6; }
+    case $host_os in
+    rhapsody* | darwin1.[012])
+      _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+    darwin1.*)
+      _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+    darwin*) # darwin 5.x on
+      # if running on 10.5 or later, the deployment target defaults
+      # to the OS version, if on x86, and 10.4, the deployment
+      # target defaults to 10.4. Don't you love it?
+      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+       10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+         _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+       10.[012]*)
+         _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+       10.*)
+         _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+      esac
+    ;;
+  esac
+    if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+      _lt_dar_single_mod='$single_module'
+    fi
+    if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+      _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+    else
+      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+    fi
+    if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then
+      _lt_dsymutil='~$DSYMUTIL $lib || :'
+    else
+      _lt_dsymutil=
+    fi
+    ;;
+  esac
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if ${ac_cv_prog_CPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_header_stdc=yes
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then :
+  :
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      return 2;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+  ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                 inttypes.h stdint.h unistd.h
+do :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in dlfcn.h
+do :
+  ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_DLFCN_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+# Set options
+
+
+
+        enable_dlopen=no
+
+
+  enable_win32_dll=no
+
+
+            # Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+  enableval=$enable_shared; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_shared=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then :
+  withval=$with_pic; lt_p=${PACKAGE-default}
+    case $withval in
+    yes|no) pic_mode=$withval ;;
+    *)
+      pic_mode=default
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for lt_pkg in $withval; do
+       IFS="$lt_save_ifs"
+       if test "X$lt_pkg" = "X$lt_p"; then
+         pic_mode=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  pic_mode=default
+fi
+
+
+test -z "$pic_mode" && pic_mode=default
+
+
+
+
+
+
+
+  # Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then :
+  enableval=$enable_fast_install; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+      for pkg in $enableval; do
+       IFS="$lt_save_ifs"
+       if test "X$pkg" = "X$p"; then
+         enable_fast_install=yes
+       fi
+      done
+      IFS="$lt_save_ifs"
+      ;;
+    esac
+else
+  enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if ${lt_cv_objdir+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test "X${COLLECT_NAMES+set}" != Xset; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+for cc_temp in $compiler""; do
+  case $cc_temp in
+    compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+    distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
+done
+cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/${ac_tool_prefix}file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+      if test -n "$file_magic_test_file"; then
+       case $deplibs_check_method in
+       "file_magic "*)
+         file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+         MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+         if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+           $EGREP "$file_magic_regex" > /dev/null; then
+           :
+         else
+           cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+         fi ;;
+       esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD="$MAGIC_CMD"
+  lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS="$lt_save_ifs"
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/file; then
+      lt_cv_path_MAGIC_CMD="$ac_dir/file"
+      if test -n "$file_magic_test_file"; then
+       case $deplibs_check_method in
+       "file_magic "*)
+         file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+         MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+         if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+           $EGREP "$file_magic_regex" > /dev/null; then
+           :
+         else
+           cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+         fi ;;
+       esac
+      fi
+      break
+    fi
+  done
+  IFS="$lt_save_ifs"
+  MAGIC_CMD="$lt_save_MAGIC_CMD"
+  ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  else
+    MAGIC_CMD=:
+  fi
+fi
+
+  fi
+  ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+  case $cc_basename in
+  nvcc*)
+    lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;
+  *)
+    lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;;
+  esac
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_rtti_exceptions=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="-fno-rtti -fno-exceptions"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_rtti_exceptions=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+    lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+    :
+fi
+
+fi
+
+
+
+
+
+
+  lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+
+  if test "$GCC" = yes; then
+    lt_prog_compiler_wl='-Wl,'
+    lt_prog_compiler_static='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test "$host_cpu" = ia64; then
+       # AIX 5 now supports IA64 processor
+       lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            lt_prog_compiler_pic='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the `-m68020' flag to GCC prevents building anything better,
+            # like `-m68040'.
+            lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic='-fno-common'
+      ;;
+
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      lt_prog_compiler_static=
+      ;;
+
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+       # +Z the default
+       ;;
+      *)
+       lt_prog_compiler_pic='-fPIC'
+       ;;
+      esac
+      ;;
+
+    interix[3-9]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      lt_prog_compiler_can_build_shared=no
+      enable_shared=no
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic='-fPIC -shared'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       lt_prog_compiler_pic=-Kconform_pic
+      fi
+      ;;
+
+    *)
+      lt_prog_compiler_pic='-fPIC'
+      ;;
+    esac
+
+    case $cc_basename in
+    nvcc*) # Cuda Compiler Driver 2.2
+      lt_prog_compiler_wl='-Xlinker '
+      if test -n "$lt_prog_compiler_pic"; then
+        lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic"
+      fi
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      lt_prog_compiler_wl='-Wl,'
+      if test "$host_cpu" = ia64; then
+       # AIX 5 now supports IA64 processor
+       lt_prog_compiler_static='-Bstatic'
+      else
+       lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case $host_cpu in
+      hppa*64*|ia64*)
+       # +Z the default
+       ;;
+      *)
+       lt_prog_compiler_pic='+Z'
+       ;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      lt_prog_compiler_static='${wl}-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC (with -KPIC) is the default.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    linux* | k*bsd*-gnu | kopensolaris*-gnu)
+      case $cc_basename in
+      # old Intel for x86_64 which still supported -KPIC.
+      ecc*)
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='-KPIC'
+       lt_prog_compiler_static='-static'
+        ;;
+      # icc used to be incompatible with GCC.
+      # ICC 10 doesn't accept -KPIC any more.
+      icc* | ifort*)
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='-fPIC'
+       lt_prog_compiler_static='-static'
+        ;;
+      # Lahey Fortran 8.1.
+      lf95*)
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='--shared'
+       lt_prog_compiler_static='--static'
+       ;;
+      nagfor*)
+       # NAG Fortran compiler
+       lt_prog_compiler_wl='-Wl,-Wl,,'
+       lt_prog_compiler_pic='-PIC'
+       lt_prog_compiler_static='-Bstatic'
+       ;;
+      pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+        # Portland Group compilers (*not* the Pentium gcc compiler,
+       # which looks to be a dead project)
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='-fpic'
+       lt_prog_compiler_static='-Bstatic'
+        ;;
+      ccc*)
+        lt_prog_compiler_wl='-Wl,'
+        # All Alpha code is PIC.
+        lt_prog_compiler_static='-non_shared'
+        ;;
+      xl* | bgxl* | bgf* | mpixl*)
+       # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+       lt_prog_compiler_wl='-Wl,'
+       lt_prog_compiler_pic='-qpic'
+       lt_prog_compiler_static='-qstaticlink'
+       ;;
+      *)
+       case `$CC -V 2>&1 | sed 5q` in
+       *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*)
+         # Sun Fortran 8.3 passes all unrecognized flags to the linker
+         lt_prog_compiler_pic='-KPIC'
+         lt_prog_compiler_static='-Bstatic'
+         lt_prog_compiler_wl=''
+         ;;
+       *Sun\ F* | *Sun*Fortran*)
+         lt_prog_compiler_pic='-KPIC'
+         lt_prog_compiler_static='-Bstatic'
+         lt_prog_compiler_wl='-Qoption ld '
+         ;;
+       *Sun\ C*)
+         # Sun C 5.9
+         lt_prog_compiler_pic='-KPIC'
+         lt_prog_compiler_static='-Bstatic'
+         lt_prog_compiler_wl='-Wl,'
+         ;;
+        *Intel*\ [CF]*Compiler*)
+         lt_prog_compiler_wl='-Wl,'
+         lt_prog_compiler_pic='-fPIC'
+         lt_prog_compiler_static='-static'
+         ;;
+       *Portland\ Group*)
+         lt_prog_compiler_wl='-Wl,'
+         lt_prog_compiler_pic='-fpic'
+         lt_prog_compiler_static='-Bstatic'
+         ;;
+       esac
+       ;;
+      esac
+      ;;
+
+    newsos6)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic='-fPIC -shared'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      lt_prog_compiler_wl='-Wl,'
+      # All OSF/1 code is PIC.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    rdos*)
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    solaris*)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      case $cc_basename in
+      f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+       lt_prog_compiler_wl='-Qoption ld ';;
+      *)
+       lt_prog_compiler_wl='-Wl,';;
+      esac
+      ;;
+
+    sunos4*)
+      lt_prog_compiler_wl='-Qoption ld '
+      lt_prog_compiler_pic='-PIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec ;then
+       lt_prog_compiler_pic='-Kconform_pic'
+       lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    unicos*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_can_build_shared=no
+      ;;
+
+    uts4*)
+      lt_prog_compiler_pic='-pic'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *)
+      lt_prog_compiler_can_build_shared=no
+      ;;
+    esac
+  fi
+
+case $host_os in
+  # For platforms which do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    lt_prog_compiler_pic=
+    ;;
+  *)
+    lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+    ;;
+esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+if ${lt_cv_prog_compiler_pic+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5
+$as_echo "$lt_cv_prog_compiler_pic" >&6; }
+lt_prog_compiler_pic=$lt_cv_prog_compiler_pic
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_pic_works=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$lt_prog_compiler_pic -DPIC"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_pic_works=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+    case $lt_prog_compiler_pic in
+     "" | " "*) ;;
+     *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+     esac
+else
+    lt_prog_compiler_pic=
+     lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_static_works=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         lt_cv_prog_compiler_static_works=yes
+       fi
+     else
+       lt_cv_prog_compiler_static_works=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+    :
+else
+    lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_c_o=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler_c_o=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+  # do not overwrite the value of need_locks provided by the user
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+  if test "$hard_links" = no; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+  runpath_var=
+  allow_undefined_flag=
+  always_export_symbols=no
+  archive_cmds=
+  archive_expsym_cmds=
+  compiler_needs_object=no
+  enable_shared_with_static_runtimes=no
+  export_dynamic_flag_spec=
+  export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  hardcode_automatic=no
+  hardcode_direct=no
+  hardcode_direct_absolute=no
+  hardcode_libdir_flag_spec=
+  hardcode_libdir_separator=
+  hardcode_minus_L=no
+  hardcode_shlibpath_var=unsupported
+  inherit_rpath=no
+  link_all_deplibs=unknown
+  module_cmds=
+  module_expsym_cmds=
+  old_archive_from_new_cmds=
+  old_archive_from_expsyms_cmds=
+  thread_safe_flag_spec=
+  whole_archive_flag_spec=
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  include_expsyms=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ` (' and `)$', so one must not match beginning or
+  # end of line.  Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+  # as well as any symbol that contains `d'.
+  exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  # Exclude shared library initialization/finalization symbols.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    link_all_deplibs=no
+    ;;
+  esac
+
+  ld_shlibs=yes
+
+  # On some targets, GNU ld is compatible enough with the native linker
+  # that we're better off using the native interface for both.
+  lt_use_gnu_ld_interface=no
+  if test "$with_gnu_ld" = yes; then
+    case $host_os in
+      aix*)
+       # The AIX port of GNU ld has always aspired to compatibility
+       # with the native linker.  However, as the warning in the GNU ld
+       # block says, versions before 2.19.5* couldn't really create working
+       # shared libraries, regardless of the interface used.
+       case `$LD -v 2>&1` in
+         *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+         *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;;
+         *\ \(GNU\ Binutils\)\ [3-9]*) ;;
+         *)
+           lt_use_gnu_ld_interface=yes
+           ;;
+       esac
+       ;;
+      *)
+       lt_use_gnu_ld_interface=yes
+       ;;
+    esac
+  fi
+
+  if test "$lt_use_gnu_ld_interface" = yes; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='${wl}'
+
+    # Set some defaults for GNU ld with shared library support. These
+    # are reset later if shared libraries are not supported. Putting them
+    # here allows them to be overridden if necessary.
+    runpath_var=LD_RUN_PATH
+    hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+    export_dynamic_flag_spec='${wl}--export-dynamic'
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+      whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+    else
+      whole_archive_flag_spec=
+    fi
+    supports_anon_versioning=no
+    case `$LD -v 2>&1` in
+      *GNU\ gold*) supports_anon_versioning=yes ;;
+      *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+      *\ 2.11.*) ;; # other 2.11 versions
+      *) supports_anon_versioning=yes ;;
+    esac
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix[3-9]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+       ld_shlibs=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            archive_expsym_cmds=''
+        ;;
+      m68k)
+            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_minus_L=yes
+        ;;
+      esac
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       allow_undefined_flag=unsupported
+       # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+       # support --undefined.  This deserves some investigation.  FIXME
+       archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      else
+       ld_shlibs=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+      # as there is no search path for DLLs.
+      hardcode_libdir_flag_spec='-L$libdir'
+      export_dynamic_flag_spec='${wl}--export-all-symbols'
+      allow_undefined_flag=unsupported
+      always_export_symbols=no
+      enable_shared_with_static_runtimes=yes
+      export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
+      exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
+
+      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+        archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+       # If the export-symbols file already is a .def file (1st line
+       # is EXPORTS), use it as is; otherwise, prepend...
+       archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+         cp $export_symbols $output_objdir/$soname.def;
+       else
+         echo EXPORTS > $output_objdir/$soname.def;
+         cat $export_symbols >> $output_objdir/$soname.def;
+       fi~
+       $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+      else
+       ld_shlibs=no
+      fi
+      ;;
+
+    haiku*)
+      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+      link_all_deplibs=yes
+      ;;
+
+    interix[3-9]*)
+      hardcode_direct=no
+      hardcode_shlibpath_var=no
+      hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+      export_dynamic_flag_spec='${wl}-E'
+      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+      # Instead, shared libraries are loaded at an image base (0x10000000 by
+      # default) and relocated if they conflict, which is a slow very memory
+      # consuming and fragmenting process.  To avoid this, we pick a random,
+      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+      archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      ;;
+
+    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+      tmp_diet=no
+      if test "$host_os" = linux-dietlibc; then
+       case $cc_basename in
+         diet\ *) tmp_diet=yes;;       # linux-dietlibc with static linking (!diet-dyn)
+       esac
+      fi
+      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+        && test "$tmp_diet" = no
+      then
+       tmp_addflag=' $pic_flag'
+       tmp_sharedflag='-shared'
+       case $cc_basename,$host_cpu in
+        pgcc*)                         # Portland Group C compiler
+         whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+         tmp_addflag=' $pic_flag'
+         ;;
+       pgf77* | pgf90* | pgf95* | pgfortran*)
+                                       # Portland Group f77 and f90 compilers
+         whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+         tmp_addflag=' $pic_flag -Mnomain' ;;
+       ecc*,ia64* | icc*,ia64*)        # Intel C compiler on ia64
+         tmp_addflag=' -i_dynamic' ;;
+       efc*,ia64* | ifort*,ia64*)      # Intel Fortran compiler on ia64
+         tmp_addflag=' -i_dynamic -nofor_main' ;;
+       ifc* | ifort*)                  # Intel Fortran compiler
+         tmp_addflag=' -nofor_main' ;;
+       lf95*)                          # Lahey Fortran 8.1
+         whole_archive_flag_spec=
+         tmp_sharedflag='--shared' ;;
+       xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+         tmp_sharedflag='-qmkshrobj'
+         tmp_addflag= ;;
+       nvcc*)  # Cuda Compiler Driver 2.2
+         whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+         compiler_needs_object=yes
+         ;;
+       esac
+       case `$CC -V 2>&1 | sed 5q` in
+       *Sun\ C*)                       # Sun C 5.9
+         whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+         compiler_needs_object=yes
+         tmp_sharedflag='-G' ;;
+       *Sun\ F*)                       # Sun Fortran 8.3
+         tmp_sharedflag='-G' ;;
+       esac
+       archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+        if test "x$supports_anon_versioning" = xyes; then
+          archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+           cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+           echo "local: *; };" >> $output_objdir/$libname.ver~
+           $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+        fi
+
+       case $cc_basename in
+       xlf* | bgf* | bgxlf* | mpixlf*)
+         # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+         whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+         hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+         archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+         if test "x$supports_anon_versioning" = xyes; then
+           archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+             cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+             echo "local: *; };" >> $output_objdir/$libname.ver~
+             $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+         fi
+         ;;
+       esac
+      else
+        ld_shlibs=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+       archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+       wlarc=
+      else
+       archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris*)
+      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+       ld_shlibs=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+       ld_shlibs=no
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+       ld_shlibs=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+       ;;
+       *)
+         # For security reasons, it is highly recommended that you always
+         # use absolute paths for naming shared libraries, and exclude the
+         # DT_RUNPATH tag from executables and libraries.  But doing so
+         # requires that you compile everything twice, which is a pain.
+         if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+           hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+           archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+           archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+         else
+           ld_shlibs=no
+         fi
+       ;;
+      esac
+      ;;
+
+    sunos4*)
+      archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+       archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+       ld_shlibs=no
+      fi
+      ;;
+    esac
+
+    if test "$ld_shlibs" = no; then
+      runpath_var=
+      hardcode_libdir_flag_spec=
+      export_dynamic_flag_spec=
+      whole_archive_flag_spec=
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      allow_undefined_flag=unsupported
+      always_export_symbols=yes
+      archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      hardcode_minus_L=yes
+      if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+       # Neither direct hardcoding nor static linking is supported with a
+       # broken collect2.
+       hardcode_direct=unsupported
+      fi
+      ;;
+
+    aix[4-9]*)
+      if test "$host_cpu" = ia64; then
+       # On IA64, the linker does run time linking by default, so we don't
+       # have to do anything special.
+       aix_use_runtimelinking=no
+       exp_sym_flag='-Bexport'
+       no_entry_flag=""
+      else
+       # If we're using GNU nm, then we don't want the "-C" option.
+       # -C means demangle to AIX nm, but means don't demangle with GNU nm
+       # Also, AIX nm treats weak defined symbols like other global
+       # defined symbols, whereas GNU nm marks them as "W".
+       if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+         export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+       else
+         export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+       fi
+       aix_use_runtimelinking=no
+
+       # Test if we are trying to use run time linking or normal
+       # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+       # need to do runtime linking.
+       case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+         for ld_flag in $LDFLAGS; do
+         if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+           aix_use_runtimelinking=yes
+           break
+         fi
+         done
+         ;;
+       esac
+
+       exp_sym_flag='-bexport'
+       no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      archive_cmds=''
+      hardcode_direct=yes
+      hardcode_direct_absolute=yes
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+      file_list_spec='${wl}-f,'
+
+      if test "$GCC" = yes; then
+       case $host_os in aix4.[012]|aix4.[012].*)
+       # We only want to do this on AIX 4.2 and lower, the check
+       # below for broken collect2 doesn't work under 4.3+
+         collect2name=`${CC} -print-prog-name=collect2`
+         if test -f "$collect2name" &&
+          strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+         then
+         # We have reworked collect2
+         :
+         else
+         # We have old collect2
+         hardcode_direct=unsupported
+         # It fails to find uninstalled libraries when the uninstalled
+         # path is not listed in the libpath.  Setting hardcode_minus_L
+         # to unsupported forces relinking
+         hardcode_minus_L=yes
+         hardcode_libdir_flag_spec='-L$libdir'
+         hardcode_libdir_separator=
+         fi
+         ;;
+       esac
+       shared_flag='-shared'
+       if test "$aix_use_runtimelinking" = yes; then
+         shared_flag="$shared_flag "'${wl}-G'
+       fi
+       link_all_deplibs=no
+      else
+       # not using gcc
+       if test "$host_cpu" = ia64; then
+       # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+       # chokes on -Wl,-G. The following line is correct:
+         shared_flag='-G'
+       else
+         if test "$aix_use_runtimelinking" = yes; then
+           shared_flag='${wl}-G'
+         else
+           shared_flag='${wl}-bM:SRE'
+         fi
+       fi
+      fi
+
+      export_dynamic_flag_spec='${wl}-bexpall'
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      always_export_symbols=yes
+      if test "$aix_use_runtimelinking" = yes; then
+       # Warning - without using the other runtime loading flags (-brtl),
+       # -berok will link without error, but may produce a broken library.
+       allow_undefined_flag='-berok'
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  if ${lt_cv_aix_libpath_+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+  lt_aix_libpath_sed='
+      /Import File Strings/,/^$/ {
+         /^0/ {
+             s/^0  *\([^ ]*\) *$/\1/
+             p
+         }
+      }'
+  lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_="/usr/lib:/lib"
+  fi
+
+fi
+
+  aix_libpath=$lt_cv_aix_libpath_
+fi
+
+        hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+        archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+      else
+       if test "$host_cpu" = ia64; then
+         hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+         allow_undefined_flag="-z nodefs"
+         archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+       else
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        if test "${lt_cv_aix_libpath+set}" = set; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  if ${lt_cv_aix_libpath_+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+  lt_aix_libpath_sed='
+      /Import File Strings/,/^$/ {
+         /^0/ {
+             s/^0  *\([^ ]*\) *$/\1/
+             p
+         }
+      }'
+  lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_="/usr/lib:/lib"
+  fi
+
+fi
+
+  aix_libpath=$lt_cv_aix_libpath_
+fi
+
+        hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+         # Warning - without using the other run time loading flags,
+         # -berok will link without error, but may produce a broken library.
+         no_undefined_flag=' ${wl}-bernotok'
+         allow_undefined_flag=' ${wl}-berok'
+         if test "$with_gnu_ld" = yes; then
+           # We only use this code for GNU lds that support --whole-archive.
+           whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+         else
+           # Exported symbols can be pulled into shared objects from archives
+           whole_archive_flag_spec='$convenience'
+         fi
+         archive_cmds_need_lc=yes
+         # This is similar to how AIX traditionally builds its shared libraries.
+         archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+       fi
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+            archive_expsym_cmds=''
+        ;;
+      m68k)
+            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_minus_L=yes
+        ;;
+      esac
+      ;;
+
+    bsdi[45]*)
+      export_dynamic_flag_spec=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      case $cc_basename in
+      cl*)
+       # Native MSVC
+       hardcode_libdir_flag_spec=' '
+       allow_undefined_flag=unsupported
+       always_export_symbols=yes
+       file_list_spec='@'
+       # Tell ltmain to make .lib files, not .a files.
+       libext=lib
+       # Tell ltmain to make .dll files, not .so files.
+       shrext_cmds=".dll"
+       # FIXME: Setting linknames here is a bad hack.
+       archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
+       archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+           sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
+         else
+           sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
+         fi~
+         $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+         linknames='
+       # The linker will not automatically build a static lib if we build a DLL.
+       # _LT_TAGVAR(old_archive_from_new_cmds, )='true'
+       enable_shared_with_static_runtimes=yes
+       exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+       export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+       # Don't use ranlib
+       old_postinstall_cmds='chmod 644 $oldlib'
+       postlink_cmds='lt_outputfile="@OUTPUT@"~
+         lt_tool_outputfile="@TOOL_OUTPUT@"~
+         case $lt_outputfile in
+           *.exe|*.EXE) ;;
+           *)
+             lt_outputfile="$lt_outputfile.exe"
+             lt_tool_outputfile="$lt_tool_outputfile.exe"
+             ;;
+         esac~
+         if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
+           $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+           $RM "$lt_outputfile.manifest";
+         fi'
+       ;;
+      *)
+       # Assume MSVC wrapper
+       hardcode_libdir_flag_spec=' '
+       allow_undefined_flag=unsupported
+       # Tell ltmain to make .lib files, not .a files.
+       libext=lib
+       # Tell ltmain to make .dll files, not .so files.
+       shrext_cmds=".dll"
+       # FIXME: Setting linknames here is a bad hack.
+       archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+       # The linker will automatically build a .lib file if we build a DLL.
+       old_archive_from_new_cmds='true'
+       # FIXME: Should let the user specify the lib program.
+       old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+       enable_shared_with_static_runtimes=yes
+       ;;
+      esac
+      ;;
+
+    darwin* | rhapsody*)
+
+
+  archive_cmds_need_lc=no
+  hardcode_direct=no
+  hardcode_automatic=yes
+  hardcode_shlibpath_var=unsupported
+  if test "$lt_cv_ld_force_load" = "yes"; then
+    whole_archive_flag_spec='`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+
+  else
+    whole_archive_flag_spec=''
+  fi
+  link_all_deplibs=yes
+  allow_undefined_flag="$_lt_dar_allow_undefined"
+  case $cc_basename in
+     ifort*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test "$_lt_dar_can_shared" = "yes"; then
+    output_verbose_link_cmd=func_echo_all
+    archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+    module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+    archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+    module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+
+  else
+  ld_shlibs=no
+  fi
+
+      ;;
+
+    dgux*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2.*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | dragonfly*)
+      archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    hpux9*)
+      if test "$GCC" = yes; then
+       archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      else
+       archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+      fi
+      hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_direct=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      hardcode_minus_L=yes
+      export_dynamic_flag_spec='${wl}-E'
+      ;;
+
+    hpux10*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+       archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      if test "$with_gnu_ld" = no; then
+       hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+       hardcode_libdir_separator=:
+       hardcode_direct=yes
+       hardcode_direct_absolute=yes
+       export_dynamic_flag_spec='${wl}-E'
+       # hardcode_minus_L: Not really in the search PATH,
+       # but as the default location of the library.
+       hardcode_minus_L=yes
+      fi
+      ;;
+
+    hpux11*)
+      if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+       case $host_cpu in
+       hppa*64*)
+         archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       ia64*)
+         archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+         archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       esac
+      else
+       case $host_cpu in
+       hppa*64*)
+         archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       ia64*)
+         archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+
+         # Older versions of the 11.00 compiler do not understand -b yet
+         # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+         { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5
+$as_echo_n "checking if $CC understands -b... " >&6; }
+if ${lt_cv_prog_compiler__b+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_prog_compiler__b=no
+   save_LDFLAGS="$LDFLAGS"
+   LDFLAGS="$LDFLAGS -b"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         lt_cv_prog_compiler__b=yes
+       fi
+     else
+       lt_cv_prog_compiler__b=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
+$as_echo "$lt_cv_prog_compiler__b" >&6; }
+
+if test x"$lt_cv_prog_compiler__b" = xyes; then
+    archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+else
+    archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+fi
+
+         ;;
+       esac
+      fi
+      if test "$with_gnu_ld" = no; then
+       hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+       hardcode_libdir_separator=:
+
+       case $host_cpu in
+       hppa*64*|ia64*)
+         hardcode_direct=no
+         hardcode_shlibpath_var=no
+         ;;
+       *)
+         hardcode_direct=yes
+         hardcode_direct_absolute=yes
+         export_dynamic_flag_spec='${wl}-E'
+
+         # hardcode_minus_L: Not really in the search PATH,
+         # but as the default location of the library.
+         hardcode_minus_L=yes
+         ;;
+       esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test "$GCC" = yes; then
+       archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+       # Try to use the -exported_symbol ld option, if it does not
+       # work, assume that -exports_file does not work either and
+       # implicitly export all symbols.
+       # This should be the same for all languages, so no per-tag cache variable.
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5
+$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; }
+if ${lt_cv_irix_exported_symbol+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  save_LDFLAGS="$LDFLAGS"
+          LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+          cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  lt_cv_irix_exported_symbol=yes
+else
+  lt_cv_irix_exported_symbol=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+           LDFLAGS="$save_LDFLAGS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
+$as_echo "$lt_cv_irix_exported_symbol" >&6; }
+       if test "$lt_cv_irix_exported_symbol" = yes; then
+          archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+       fi
+      else
+       archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+       archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      inherit_rpath=yes
+      link_all_deplibs=yes
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+       archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+       archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    newsos6)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_shlibpath_var=no
+      ;;
+
+    *nto* | *qnx*)
+      ;;
+
+    openbsd*)
+      if test -f /usr/libexec/ld.so; then
+       hardcode_direct=yes
+       hardcode_shlibpath_var=no
+       hardcode_direct_absolute=yes
+       if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+         archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+         archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+         hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+         export_dynamic_flag_spec='${wl}-E'
+       else
+         case $host_os in
+          openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+            archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+            hardcode_libdir_flag_spec='-R$libdir'
+            ;;
+          *)
+            archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+            hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+            ;;
+         esac
+       fi
+      else
+       ld_shlibs=no
+      fi
+      ;;
+
+    os2*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      allow_undefined_flag=unsupported
+      archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+      old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+      ;;
+
+    osf3*)
+      if test "$GCC" = yes; then
+       allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+       archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+      else
+       allow_undefined_flag=' -expect_unresolved \*'
+       archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+
+    osf4* | osf5*)     # as osf3* with the addition of -msym flag
+      if test "$GCC" = yes; then
+       allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+       archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+       hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      else
+       allow_undefined_flag=' -expect_unresolved \*'
+       archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+       archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+       $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+       # Both c and cxx compiler support -rpath directly
+       hardcode_libdir_flag_spec='-rpath $libdir'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_separator=:
+      ;;
+
+    solaris*)
+      no_undefined_flag=' -z defs'
+      if test "$GCC" = yes; then
+       wlarc='${wl}'
+       archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+       archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+      else
+       case `$CC -V 2>&1` in
+       *"Compilers 5.0"*)
+         wlarc=''
+         archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+         ;;
+       *)
+         wlarc='${wl}'
+         archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+         archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+         $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+         ;;
+       esac
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_shlibpath_var=no
+      case $host_os in
+      solaris2.[0-5] | solaris2.[0-5].*) ;;
+      *)
+       # The compiler driver will combine and reorder linker options,
+       # but understands `-z linker_flag'.  GCC discards it without `$wl',
+       # but is careful enough not to reorder.
+       # Supported since Solaris 2.6 (maybe 2.5.1?)
+       if test "$GCC" = yes; then
+         whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+       else
+         whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+       fi
+       ;;
+      esac
+      link_all_deplibs=yes
+      ;;
+
+    sunos4*)
+      if test "x$host_vendor" = xsequent; then
+       # Use $CC to link under sequent, because it throws in some extra .o
+       # files that make .init and .fini sections work.
+       archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+       sni)
+         archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         hardcode_direct=yes # is this really true???
+       ;;
+       siemens)
+         ## LD is ld it makes a PLAMLIB
+         ## CC just makes a GrossModule.
+         archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+         reload_cmds='$CC -r -o $output$reload_objs'
+         hardcode_direct=no
+        ;;
+       motorola)
+         archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+         hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+       ;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4.3*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var=no
+      export_dynamic_flag_spec='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+       hardcode_shlibpath_var=no
+       runpath_var=LD_RUN_PATH
+       hardcode_runpath_var=yes
+       ld_shlibs=yes
+      fi
+      ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+      no_undefined_flag='${wl}-z,text'
+      archive_cmds_need_lc=no
+      hardcode_shlibpath_var=no
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+       archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6*)
+      # Note: We can NOT use -z defs as we might desire, because we do not
+      # link with -lc, and that would cause any symbols used from libc to
+      # always be unresolved, which means just about no library would
+      # ever link correctly.  If we're not using GNU ld we use -z text
+      # though, which does catch some bad symbols but isn't as heavy-handed
+      # as -z defs.
+      no_undefined_flag='${wl}-z,text'
+      allow_undefined_flag='${wl}-z,nodefs'
+      archive_cmds_need_lc=no
+      hardcode_shlibpath_var=no
+      hardcode_libdir_flag_spec='${wl}-R,$libdir'
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+      export_dynamic_flag_spec='${wl}-Bexport'
+      runpath_var='LD_RUN_PATH'
+
+      if test "$GCC" = yes; then
+       archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+       archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    uts4*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      ld_shlibs=no
+      ;;
+    esac
+
+    if test x$host_vendor = xsni; then
+      case $host in
+      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+       export_dynamic_flag_spec='${wl}-Blargedynsym'
+       ;;
+      esac
+    fi
+  fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test "$ld_shlibs" = no && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+  # Assume -lc should be added
+  archive_cmds_need_lc=yes
+
+  if test "$enable_shared" = yes && test "$GCC" = yes; then
+    case $archive_cmds in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+if ${lt_cv_archive_cmds_need_lc+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  $RM conftest*
+       echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+       if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } 2>conftest.err; then
+         soname=conftest
+         lib=conftest
+         libobjs=conftest.$ac_objext
+         deplibs=
+         wl=$lt_prog_compiler_wl
+         pic_flag=$lt_prog_compiler_pic
+         compiler_flags=-v
+         linker_flags=-v
+         verstring=
+         output_objdir=.
+         libname=conftest
+         lt_save_allow_undefined_flag=$allow_undefined_flag
+         allow_undefined_flag=
+         if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+  (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+         then
+           lt_cv_archive_cmds_need_lc=no
+         else
+           lt_cv_archive_cmds_need_lc=yes
+         fi
+         allow_undefined_flag=$lt_save_allow_undefined_flag
+       else
+         cat conftest.err 1>&5
+       fi
+       $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5
+$as_echo "$lt_cv_archive_cmds_need_lc" >&6; }
+      archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test "$GCC" = yes; then
+  case $host_os in
+    darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+    *) lt_awk_arg="/^libraries:/" ;;
+  esac
+  case $host_os in
+    mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;;
+    *) lt_sed_strip_eq="s,=/,/,g" ;;
+  esac
+  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+  case $lt_search_path_spec in
+  *\;*)
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+    ;;
+  *)
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+    ;;
+  esac
+  # Ok, now we have the path, separated by spaces, we can step through it
+  # and add multilib dir if necessary.
+  lt_tmp_lt_search_path_spec=
+  lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+  for lt_sys_path in $lt_search_path_spec; do
+    if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+    else
+      test -d "$lt_sys_path" && \
+       lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+    fi
+  done
+  lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+  lt_foo="";
+  lt_count=0;
+  for (lt_i = NF; lt_i > 0; lt_i--) {
+    if ($lt_i != "" && $lt_i != ".") {
+      if ($lt_i == "..") {
+        lt_count++;
+      } else {
+        if (lt_count == 0) {
+          lt_foo="/" $lt_i lt_foo;
+        } else {
+          lt_count--;
+        }
+      }
+    }
+  }
+  if (lt_foo != "") { lt_freq[lt_foo]++; }
+  if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+  # AWK program above erroneously prepends '/' to C:/dos/paths
+  # for these hosts.
+  case $host_os in
+    mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+      $SED 's,/\([A-Za-z]:\),\1,g'` ;;
+  esac
+  sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='${libname}${release}${shared_ext}$major'
+  ;;
+
+aix[4-9]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test "$host_cpu" = ia64; then
+    # AIX 5 supports IA64
+    library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line `#! .'.  This would cause the generated library to
+    # depend on `.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+          echo ' yes '
+          echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+       :
+      else
+       can_build_shared=no
+      fi
+      ;;
+    esac
+    # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    if test "$aix_use_runtimelinking" = yes; then
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    else
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='${libname}${release}.a $libname.a'
+      soname_spec='${libname}${release}${shared_ext}$major'
+    fi
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='${libname}${shared_ext}'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[45]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=".dll"
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$cc_basename in
+  yes,*)
+    # gcc
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+
+      sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+      ;;
+    esac
+    dynamic_linker='Win32 ld.exe'
+    ;;
+
+  *,cl*)
+    # Native MSVC
+    libname_spec='$name'
+    soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+    library_names_spec='${libname}.dll.lib'
+
+    case $build_os in
+    mingw*)
+      sys_lib_search_path_spec=
+      lt_save_ifs=$IFS
+      IFS=';'
+      for lt_path in $LIB
+      do
+        IFS=$lt_save_ifs
+        # Let DOS variable expansion print the short 8.3 style file name.
+        lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+        sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+      done
+      IFS=$lt_save_ifs
+      # Convert to MSYS style.
+      sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
+      ;;
+    cygwin*)
+      # Convert to unix form, then to dos form, then back to unix form
+      # but this time dos style (no spaces!) so that the unix form looks
+      # like /cygdrive/c/PROGRA~1:/cygdr...
+      sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+      sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+      sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      ;;
+    *)
+      sys_lib_search_path_spec="$LIB"
+      if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+        # It is most probably a Windows format PATH.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      # FIXME: find the short name or the path components, as spaces are
+      # common. (e.g. "Program Files" -> "PROGRA~1")
+      ;;
+    esac
+
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \${file}`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+    dynamic_linker='Win32 link.exe'
+    ;;
+
+  *)
+    # Assume MSVC wrapper
+    library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+    dynamic_linker='Win32 ld.exe'
+    ;;
+  esac
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+  soname_spec='${libname}${release}${major}$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd* | dragonfly*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[23].*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2.*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[01]* | freebsdelf3.[01]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+  freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+haiku*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  dynamic_linker="$host_os runtime_loader"
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    if test "X$HPUX_IA64_MODE" = X32; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+    fi
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+  postinstall_cmds='chmod 555 $lib'
+  # or fails outright, so override atomically:
+  install_override_mode=555
+  ;;
+
+interix[3-9]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+       if test "$lt_cv_prog_gnu_ld" = yes; then
+               version_type=linux # correct to gnu/linux during the next big refactor
+       else
+               version_type=irix
+       fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+  sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+
+  # Some binutils ld are patched to set DT_RUNPATH
+  if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  lt_cv_shlibpath_overrides_runpath=no
+    save_LDFLAGS=$LDFLAGS
+    save_libdir=$libdir
+    eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+        LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  if  ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+  lt_cv_shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+    LDFLAGS=$save_LDFLAGS
+    libdir=$save_libdir
+
+fi
+
+  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Append ld.so.conf contents to the search path
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[      ]*hwcap[        ]/d;s/[:,      ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+    soname_spec='${libname}${release}${shared_ext}$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec="/usr/lib"
+  need_lib_prefix=no
+  # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+  case $host_os in
+    openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+    *)                         need_version=no  ;;
+  esac
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+    case $host_os in
+      openbsd2.[89] | openbsd2.[89].*)
+       shlibpath_overrides_runpath=no
+       ;;
+      *)
+       shlibpath_overrides_runpath=yes
+       ;;
+      esac
+  else
+    shlibpath_overrides_runpath=yes
+  fi
+  ;;
+
+os2*)
+  libname_spec='$name'
+  shrext_cmds=".dll"
+  need_lib_prefix=no
+  library_names_spec='$libname${shared_ext} $libname.a'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=LIBPATH
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='${libname}${release}${shared_ext}$major'
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test "$with_gnu_ld" = yes; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec ;then
+    version_type=linux # correct to gnu/linux during the next big refactor
+    library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+    soname_spec='$libname${shared_ext}.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=freebsd-elf
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test "$with_gnu_ld" = yes; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+       ;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+  sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+  sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+   test -n "$runpath_var" ||
+   test "X$hardcode_automatic" = "Xyes" ; then
+
+  # We can hardcode non-existent directories.
+  if test "$hardcode_direct" != no &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
+     test "$hardcode_minus_L" != no; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test "$hardcode_action" = relink ||
+   test "$inherit_rpath" = yes; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+     test "$enable_shared" = no; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+
+
+
+
+
+  if test "x$enable_dlopen" != xyes; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen="load_add_on"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32* | cegcc*)
+    lt_cv_dlopen="LoadLibrary"
+    lt_cv_dlopen_libs=
+    ;;
+
+  cygwin*)
+    lt_cv_dlopen="dlopen"
+    lt_cv_dlopen_libs=
+    ;;
+
+  darwin*)
+  # if libdl is installed we need to link against it
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dl_dlopen=yes
+else
+  ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+    lt_cv_dlopen="dyld"
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+
+fi
+
+    ;;
+
+  *)
+    ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+  lt_cv_dlopen="shl_load"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if ${ac_cv_lib_dld_shl_load+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dld_shl_load=yes
+else
+  ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+  lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+  ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dl_dlopen=yes
+else
+  ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if ${ac_cv_lib_svld_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_svld_dlopen=yes
+else
+  ac_cv_lib_svld_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+  lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if ${ac_cv_lib_dld_dld_link+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dld_dld_link=yes
+else
+  ac_cv_lib_dld_dld_link=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+  lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+    ;;
+  esac
+
+  if test "x$lt_cv_dlopen" != xno; then
+    enable_dlopen=yes
+  else
+    enable_dlopen=no
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS="$CPPFLAGS"
+    test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS="$LDFLAGS"
+    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS="$LIBS"
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+         if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL          RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL                DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL                0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW           RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW         DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW       RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW     DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW     0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisbility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+         if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+       }
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}
+_LT_EOF
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&5 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+    if test "x$lt_cv_dlopen_self" = xyes; then
+      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self_static+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+         if test "$cross_compiling" = yes; then :
+  lt_cv_dlopen_self_static=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL          RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL                DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL                0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW           RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW         DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW       RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW     DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW     0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisbility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+         if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+       }
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}
+_LT_EOF
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+    (./conftest; exit; ) >&5 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self_static=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+    fi
+
+    CPPFLAGS="$save_CPPFLAGS"
+    LDFLAGS="$save_LDFLAGS"
+    LIBS="$save_LIBS"
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+  darwin*)
+    if test -n "$STRIP" ; then
+      striplib="$STRIP -x"
+      old_striplib="$STRIP -S"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+    else
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    fi
+    ;;
+  *)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+    ;;
+  esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+  # Report which library types will actually be built
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+  test "$can_build_shared" = "no" && enable_shared=no
+
+  # On AIX, shared libraries and static libraries use the same namespace, and
+  # are all built from PIC.
+  case $host_os in
+  aix3*)
+    test "$enable_shared" = yes && enable_static=no
+    if test -n "$RANLIB"; then
+      archive_cmds="$archive_cmds~\$RANLIB \$lib"
+      postinstall_cmds='$RANLIB $lib'
+    fi
+    ;;
+
+  aix[4-9]*)
+    if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+      test "$enable_shared" = yes && enable_static=no
+    fi
+    ;;
+  esac
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+  # Make sure either enable_shared or enable_static is yes.
+  test "$enable_shared" = yes || enable_static=yes
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+        ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+
+       ac_fn_c_check_func "$LINENO" "ppoll" "ac_cv_func_ppoll"
+if test "x$ac_cv_func_ppoll" = xyes; then :
+  dummy=yes
+else
+
+$as_echo "#define NEED_PPOLL 1" >>confdefs.h
+
+fi
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_dl_dlopen=yes
+else
+  ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+  dummy=yes
+else
+  as_fn_error $? "dynamic linking loader is required" "$LINENO" 5
+fi
+
+
+ac_fn_c_check_header_mongrel "$LINENO" "sys/inotify.h" "ac_cv_header_sys_inotify_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_inotify_h" = xyes; then :
+
+$as_echo "#define HAVE_SYS_INOTIFY_H 1" >>confdefs.h
+
+else
+  as_fn_error $? "inotify headers are required and missing" "$LINENO" 5
+fi
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for DBUS" >&5
+$as_echo_n "checking for DBUS... " >&6; }
+
+if test -n "$DBUS_CFLAGS"; then
+    pkg_cv_DBUS_CFLAGS="$DBUS_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.4\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.4") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.4" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$DBUS_LIBS"; then
+    pkg_cv_DBUS_LIBS="$DBUS_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.4\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.4") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.4" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 >= 1.4" 2>&1`
+        else
+               DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 >= 1.4" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$DBUS_PKG_ERRORS" >&5
+
+       as_fn_error $? "D-Bus >= 1.4 is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+       as_fn_error $? "D-Bus >= 1.4 is required" "$LINENO" 5
+else
+       DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS
+       DBUS_LIBS=$pkg_cv_DBUS_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       dummy=yes
+fi
+
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLIB" >&5
+$as_echo_n "checking for GLIB... " >&6; }
+
+if test -n "$GLIB_CFLAGS"; then
+    pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.28\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.28") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.28" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GLIB_LIBS"; then
+    pkg_cv_GLIB_LIBS="$GLIB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.28\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.28") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.28" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "glib-2.0 >= 2.28" 2>&1`
+        else
+               GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "glib-2.0 >= 2.28" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$GLIB_PKG_ERRORS" >&5
+
+       as_fn_error $? "GLib >= 2.28 is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+       as_fn_error $? "GLib >= 2.28 is required" "$LINENO" 5
+else
+       GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS
+       GLIB_LIBS=$pkg_cv_GLIB_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       dummy=yes
+fi
+
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ALSA" >&5
+$as_echo_n "checking for ALSA... " >&6; }
+
+if test -n "$ALSA_CFLAGS"; then
+    pkg_cv_ALSA_CFLAGS="$ALSA_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"alsa\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "alsa") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_ALSA_CFLAGS=`$PKG_CONFIG --cflags "alsa" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$ALSA_LIBS"; then
+    pkg_cv_ALSA_LIBS="$ALSA_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"alsa\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "alsa") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_ALSA_LIBS=`$PKG_CONFIG --libs "alsa" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               ALSA_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "alsa" 2>&1`
+        else
+               ALSA_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "alsa" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$ALSA_PKG_ERRORS" >&5
+
+       alsa_found=no
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+       alsa_found=no
+else
+       ALSA_CFLAGS=$pkg_cv_ALSA_CFLAGS
+       ALSA_LIBS=$pkg_cv_ALSA_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       alsa_found=yes
+fi
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5
+$as_echo_n "checking for clock_gettime in -lrt... " >&6; }
+if ${ac_cv_lib_rt_clock_gettime+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrt  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime ();
+int
+main ()
+{
+return clock_gettime ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_rt_clock_gettime=yes
+else
+  ac_cv_lib_rt_clock_gettime=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5
+$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; }
+if test "x$ac_cv_lib_rt_clock_gettime" = xyes; then :
+  ALSA_LIBS="$ALSA_LIBS -lrt"
+else
+  alsa_found=no
+fi
+
+
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GSTREAMER" >&5
+$as_echo_n "checking for GSTREAMER... " >&6; }
+
+if test -n "$GSTREAMER_CFLAGS"; then
+    pkg_cv_GSTREAMER_CFLAGS="$GSTREAMER_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GSTREAMER_CFLAGS=`$PKG_CONFIG --cflags "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$GSTREAMER_LIBS"; then
+    pkg_cv_GSTREAMER_LIBS="$GSTREAMER_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_GSTREAMER_LIBS=`$PKG_CONFIG --libs "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               GSTREAMER_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10" 2>&1`
+        else
+               GSTREAMER_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$GSTREAMER_PKG_ERRORS" >&5
+
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: GStreamer library version 0.10.30 or later is required" >&5
+$as_echo "$as_me: WARNING: GStreamer library version 0.10.30 or later is required" >&2;};gstreamer_found=no
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: GStreamer library version 0.10.30 or later is required" >&5
+$as_echo "$as_me: WARNING: GStreamer library version 0.10.30 or later is required" >&2;};gstreamer_found=no
+else
+       GSTREAMER_CFLAGS=$pkg_cv_GSTREAMER_CFLAGS
+       GSTREAMER_LIBS=$pkg_cv_GSTREAMER_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       gstreamer_found=yes
+fi
+
+
+       GSTREAMER_PLUGINSDIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-0.10`
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for USB" >&5
+$as_echo_n "checking for USB... " >&6; }
+
+if test -n "$USB_CFLAGS"; then
+    pkg_cv_USB_CFLAGS="$USB_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libusb\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libusb") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_USB_CFLAGS=`$PKG_CONFIG --cflags "libusb" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$USB_LIBS"; then
+    pkg_cv_USB_LIBS="$USB_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libusb\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libusb") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_USB_LIBS=`$PKG_CONFIG --libs "libusb" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               USB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libusb" 2>&1`
+        else
+               USB_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libusb" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$USB_PKG_ERRORS" >&5
+
+       usb_found=no
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+       usb_found=no
+else
+       USB_CFLAGS=$pkg_cv_USB_CFLAGS
+       USB_LIBS=$pkg_cv_USB_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       usb_found=yes
+fi
+
+
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for usb_get_busses in -lusb" >&5
+$as_echo_n "checking for usb_get_busses in -lusb... " >&6; }
+if ${ac_cv_lib_usb_usb_get_busses+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lusb  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char usb_get_busses ();
+int
+main ()
+{
+return usb_get_busses ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_usb_usb_get_busses=yes
+else
+  ac_cv_lib_usb_usb_get_busses=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_usb_usb_get_busses" >&5
+$as_echo "$ac_cv_lib_usb_usb_get_busses" >&6; }
+if test "x$ac_cv_lib_usb_usb_get_busses" = xyes; then :
+  dummy=yes
+else
+
+$as_echo "#define NEED_USB_GET_BUSSES 1" >>confdefs.h
+
+fi
+
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for usb_interrupt_read in -lusb" >&5
+$as_echo_n "checking for usb_interrupt_read in -lusb... " >&6; }
+if ${ac_cv_lib_usb_usb_interrupt_read+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lusb  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char usb_interrupt_read ();
+int
+main ()
+{
+return usb_interrupt_read ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_usb_usb_interrupt_read=yes
+else
+  ac_cv_lib_usb_usb_interrupt_read=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_usb_usb_interrupt_read" >&5
+$as_echo "$ac_cv_lib_usb_usb_interrupt_read" >&6; }
+if test "x$ac_cv_lib_usb_usb_interrupt_read" = xyes; then :
+  dummy=yes
+else
+
+$as_echo "#define NEED_USB_INTERRUPT_READ 1" >>confdefs.h
+
+fi
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for UDEV" >&5
+$as_echo_n "checking for UDEV... " >&6; }
+
+if test -n "$UDEV_CFLAGS"; then
+    pkg_cv_UDEV_CFLAGS="$UDEV_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libudev\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libudev") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_UDEV_CFLAGS=`$PKG_CONFIG --cflags "libudev" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$UDEV_LIBS"; then
+    pkg_cv_UDEV_LIBS="$UDEV_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libudev\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libudev") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_UDEV_LIBS=`$PKG_CONFIG --libs "libudev" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               UDEV_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libudev" 2>&1`
+        else
+               UDEV_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libudev" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$UDEV_PKG_ERRORS" >&5
+
+       udev_found=no
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+       udev_found=no
+else
+       UDEV_CFLAGS=$pkg_cv_UDEV_CFLAGS
+       UDEV_LIBS=$pkg_cv_UDEV_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       udev_found=yes
+fi
+
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SNDFILE" >&5
+$as_echo_n "checking for SNDFILE... " >&6; }
+
+if test -n "$SNDFILE_CFLAGS"; then
+    pkg_cv_SNDFILE_CFLAGS="$SNDFILE_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sndfile\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "sndfile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_SNDFILE_CFLAGS=`$PKG_CONFIG --cflags "sndfile" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$SNDFILE_LIBS"; then
+    pkg_cv_SNDFILE_LIBS="$SNDFILE_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sndfile\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "sndfile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_SNDFILE_LIBS=`$PKG_CONFIG --libs "sndfile" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               SNDFILE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sndfile" 2>&1`
+        else
+               SNDFILE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sndfile" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$SNDFILE_PKG_ERRORS" >&5
+
+       sndfile_found=no
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+       sndfile_found=no
+else
+       SNDFILE_CFLAGS=$pkg_cv_SNDFILE_CFLAGS
+       SNDFILE_LIBS=$pkg_cv_SNDFILE_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       sndfile_found=yes
+fi
+
+
+
+
+
+# Check whether --with-ouifile was given.
+if test "${with_ouifile+set}" = set; then :
+  withval=$with_ouifile; ac_with_ouifile=$withval
+else
+  ac_with_ouifile="/var/lib/misc/oui.txt"
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define OUIFILE "$ac_with_ouifile"
+_ACEOF
+
+
+
+       ac_fn_c_check_header_mongrel "$LINENO" "readline/readline.h" "ac_cv_header_readline_readline_h" "$ac_includes_default"
+if test "x$ac_cv_header_readline_readline_h" = xyes; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lreadline" >&5
+$as_echo_n "checking for main in -lreadline... " >&6; }
+if ${ac_cv_lib_readline_main+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lreadline  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+
+int
+main ()
+{
+return main ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_readline_main=yes
+else
+  ac_cv_lib_readline_main=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_readline_main" >&5
+$as_echo "$ac_cv_lib_readline_main" >&6; }
+if test "x$ac_cv_lib_readline_main" = xyes; then :
+   readline_found=yes
+                       READLINE_LIBS="-lreadline"
+
+
+else
+  readline_found=no
+fi
+
+fi
+
+
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CHECK" >&5
+$as_echo_n "checking for CHECK... " >&6; }
+
+if test -n "$CHECK_CFLAGS"; then
+    pkg_cv_CHECK_CFLAGS="$CHECK_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"check >= 0.9.6\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "check >= 0.9.6") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_CHECK_CFLAGS=`$PKG_CONFIG --cflags "check >= 0.9.6" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$CHECK_LIBS"; then
+    pkg_cv_CHECK_LIBS="$CHECK_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"check >= 0.9.6\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "check >= 0.9.6") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_CHECK_LIBS=`$PKG_CONFIG --libs "check >= 0.9.6" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               CHECK_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "check >= 0.9.6" 2>&1`
+        else
+               CHECK_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "check >= 0.9.6" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$CHECK_PKG_ERRORS" >&5
+
+       check_found=no
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+       check_found=no
+else
+       CHECK_CFLAGS=$pkg_cv_CHECK_CFLAGS
+       CHECK_LIBS=$pkg_cv_CHECK_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       check_found=yes
+fi
+
+
+
+
+
+       debug_enable=no
+       optimization_enable=yes
+       fortify_enable=yes
+       pie_enable=yes
+       sndfile_enable=${sndfile_found}
+       hal_enable=no
+       usb_enable=${usb_found}
+       alsa_enable=${alsa_found}
+       gstreamer_enable=${gstreamer_found}
+       audio_enable=yes
+       input_enable=yes
+       serial_enable=yes
+       network_enable=yes
+       sap_enable=no
+       service_enable=yes
+       health_enable=no
+       pnat_enable=no
+       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
+       datafiles_enable=yes
+       telephony_driver=dummy
+       maemo6_enable=no
+       sap_driver=dummy
+       dbusoob_enable=no
+       wiimote_enable=no
+       gatt_enable=no
+
+       # Check whether --enable-optimization was given.
+if test "${enable_optimization+set}" = set; then :
+  enableval=$enable_optimization;
+               optimization_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-fortify was given.
+if test "${enable_fortify+set}" = set; then :
+  enableval=$enable_fortify;
+               fortify_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-pie was given.
+if test "${enable_pie+set}" = set; then :
+  enableval=$enable_pie;
+               pie_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-network was given.
+if test "${enable_network+set}" = set; then :
+  enableval=$enable_network;
+               network_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-sap was given.
+if test "${enable_sap+set}" = set; then :
+  enableval=$enable_sap;
+               sap_enable=${enableval}
+
+fi
+
+
+
+# Check whether --with-sap was given.
+if test "${with_sap+set}" = set; then :
+  withval=$with_sap;
+               sap_driver=${withval}
+
+fi
+
+       SAP_DRIVER=sap-${sap_driver}.c
+
+
+       # Check whether --enable-serial was given.
+if test "${enable_serial+set}" = set; then :
+  enableval=$enable_serial;
+               serial_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-input was given.
+if test "${enable_input+set}" = set; then :
+  enableval=$enable_input;
+               input_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-audio was given.
+if test "${enable_audio+set}" = set; then :
+  enableval=$enable_audio;
+               audio_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-service was given.
+if test "${enable_service+set}" = set; then :
+  enableval=$enable_service;
+               service_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-health was given.
+if test "${enable_health+set}" = set; then :
+  enableval=$enable_health;
+               health_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-pnat was given.
+if test "${enable_pnat+set}" = set; then :
+  enableval=$enable_pnat;
+               pnat_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-gstreamer was given.
+if test "${enable_gstreamer+set}" = set; then :
+  enableval=$enable_gstreamer;
+               gstreamer_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-alsa was given.
+if test "${enable_alsa+set}" = set; then :
+  enableval=$enable_alsa;
+               alsa_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-usb was given.
+if test "${enable_usb+set}" = set; then :
+  enableval=$enable_usb;
+               usb_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-tools was given.
+if test "${enable_tools+set}" = set; then :
+  enableval=$enable_tools;
+               tools_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-bccmd was given.
+if test "${enable_bccmd+set}" = set; then :
+  enableval=$enable_bccmd;
+               bccmd_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-pcmcia was given.
+if test "${enable_pcmcia+set}" = set; then :
+  enableval=$enable_pcmcia;
+               pcmcia_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-hid2hci was given.
+if test "${enable_hid2hci+set}" = set; then :
+  enableval=$enable_hid2hci;
+               hid2hci_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-dfutool was given.
+if test "${enable_dfutool+set}" = set; then :
+  enableval=$enable_dfutool;
+               dfutool_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-hidd was given.
+if test "${enable_hidd+set}" = set; then :
+  enableval=$enable_hidd;
+               hidd_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-pand was given.
+if test "${enable_pand+set}" = set; then :
+  enableval=$enable_pand;
+               pand_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-dund was given.
+if test "${enable_dund+set}" = set; then :
+  enableval=$enable_dund;
+               dund_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-cups was given.
+if test "${enable_cups+set}" = set; then :
+  enableval=$enable_cups;
+               cups_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-test was given.
+if test "${enable_test+set}" = set; then :
+  enableval=$enable_test;
+               test_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-datafiles was given.
+if test "${enable_datafiles+set}" = set; then :
+  enableval=$enable_datafiles;
+               datafiles_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then :
+  enableval=$enable_debug;
+               debug_enable=${enableval}
+
+fi
+
+
+
+# Check whether --with-telephony was given.
+if test "${with_telephony+set}" = set; then :
+  withval=$with_telephony;
+               telephony_driver=${withval}
+
+fi
+
+
+       TELEPHONY_DRIVER=telephony-${telephony_driver}.c
+
+
+       # Check whether --enable-maemo6 was given.
+if test "${enable_maemo6+set}" = set; then :
+  enableval=$enable_maemo6;
+               maemo6_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-dbusoob was given.
+if test "${enable_dbusoob+set}" = set; then :
+  enableval=$enable_dbusoob;
+               dbusoob_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-wiimote was given.
+if test "${enable_wiimote+set}" = set; then :
+  enableval=$enable_wiimote;
+               wiimote_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-hal was given.
+if test "${enable_hal+set}" = set; then :
+  enableval=$enable_hal;
+               hal_enable=${enableval}
+
+fi
+
+
+       # Check whether --enable-gatt was given.
+if test "${enable_gatt+set}" = set; then :
+  enableval=$enable_gatt;
+               gatt_enable=${enableval}
+
+fi
+
+
+       misc_cflags=""
+       misc_ldflags=""
+
+       if (test "${fortify_enable}" = "yes"); then
+               misc_cflags="$misc_cflags -D_FORTIFY_SOURCE=2"
+       fi
+
+       if (test "${pie_enable}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then
+               misc_cflags="$misc_cflags -fPIC"
+               misc_ldflags="$misc_ldflags -pie"
+       fi
+
+       if (test "${debug_enable}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then
+               misc_cflags="$misc_cflags -g"
+       fi
+
+       if (test "${optimization_enable}" = "no"); then
+               misc_cflags="$misc_cflags -O0"
+       fi
+
+       MISC_CFLAGS=$misc_cflags
+
+       MISC_LDFLAGS=$misc_ldflags
+
+
+       if (test "${usb_enable}" = "yes" && test "${usb_found}" = "yes"); then
+
+$as_echo "#define HAVE_LIBUSB 1" >>confdefs.h
+
+       fi
+
+        if test "${sndfile_enable}" = "yes" && test "${sndfile_found}" = "yes"; then
+  SNDFILE_TRUE=
+  SNDFILE_FALSE='#'
+else
+  SNDFILE_TRUE='#'
+  SNDFILE_FALSE=
+fi
+
+        if test "${usb_enable}" = "yes" && test "${usb_found}" = "yes"; then
+  USB_TRUE=
+  USB_FALSE='#'
+else
+  USB_TRUE='#'
+  USB_FALSE=
+fi
+
+        if test "${alsa_enable}" = "yes" || test "${gstreamer_enable}" = "yes" ||
+                                                                       test "${test_enable}" = "yes"; then
+  SBC_TRUE=
+  SBC_FALSE='#'
+else
+  SBC_TRUE='#'
+  SBC_FALSE=
+fi
+
+        if test "${alsa_enable}" = "yes" && test "${alsa_found}" = "yes"; then
+  ALSA_TRUE=
+  ALSA_FALSE='#'
+else
+  ALSA_TRUE='#'
+  ALSA_FALSE=
+fi
+
+        if test "${gstreamer_enable}" = "yes" && test "${gstreamer_found}" = "yes"; then
+  GSTREAMER_TRUE=
+  GSTREAMER_FALSE='#'
+else
+  GSTREAMER_TRUE='#'
+  GSTREAMER_FALSE=
+fi
+
+        if test "${audio_enable}" = "yes"; then
+  AUDIOPLUGIN_TRUE=
+  AUDIOPLUGIN_FALSE='#'
+else
+  AUDIOPLUGIN_TRUE='#'
+  AUDIOPLUGIN_FALSE=
+fi
+
+        if test "${input_enable}" = "yes"; then
+  INPUTPLUGIN_TRUE=
+  INPUTPLUGIN_FALSE='#'
+else
+  INPUTPLUGIN_TRUE='#'
+  INPUTPLUGIN_FALSE=
+fi
+
+        if test "${serial_enable}" = "yes"; then
+  SERIALPLUGIN_TRUE=
+  SERIALPLUGIN_FALSE='#'
+else
+  SERIALPLUGIN_TRUE='#'
+  SERIALPLUGIN_FALSE=
+fi
+
+        if test "${network_enable}" = "yes"; then
+  NETWORKPLUGIN_TRUE=
+  NETWORKPLUGIN_FALSE='#'
+else
+  NETWORKPLUGIN_TRUE='#'
+  NETWORKPLUGIN_FALSE=
+fi
+
+        if test "${sap_enable}" = "yes"; then
+  SAPPLUGIN_TRUE=
+  SAPPLUGIN_FALSE='#'
+else
+  SAPPLUGIN_TRUE='#'
+  SAPPLUGIN_FALSE=
+fi
+
+        if test "${service_enable}" = "yes"; then
+  SERVICEPLUGIN_TRUE=
+  SERVICEPLUGIN_FALSE='#'
+else
+  SERVICEPLUGIN_TRUE='#'
+  SERVICEPLUGIN_FALSE=
+fi
+
+        if test "${health_enable}" = "yes"; then
+  HEALTHPLUGIN_TRUE=
+  HEALTHPLUGIN_FALSE='#'
+else
+  HEALTHPLUGIN_TRUE='#'
+  HEALTHPLUGIN_FALSE=
+fi
+
+        if test "${health_enable}" = "yes"; then
+  MCAP_TRUE=
+  MCAP_FALSE='#'
+else
+  MCAP_TRUE='#'
+  MCAP_FALSE=
+fi
+
+        if test "${hal_enable}" = "yes"; then
+  HAL_TRUE=
+  HAL_FALSE='#'
+else
+  HAL_TRUE='#'
+  HAL_FALSE=
+fi
+
+        if test "${readline_found}" = "yes"; then
+  READLINE_TRUE=
+  READLINE_FALSE='#'
+else
+  READLINE_TRUE='#'
+  READLINE_FALSE=
+fi
+
+        if test "${pnat_enable}" = "yes"; then
+  PNATPLUGIN_TRUE=
+  PNATPLUGIN_FALSE='#'
+else
+  PNATPLUGIN_TRUE='#'
+  PNATPLUGIN_FALSE=
+fi
+
+        if test "${hidd_enable}" = "yes"; then
+  HIDD_TRUE=
+  HIDD_FALSE='#'
+else
+  HIDD_TRUE='#'
+  HIDD_FALSE=
+fi
+
+        if test "${pand_enable}" = "yes"; then
+  PAND_TRUE=
+  PAND_FALSE='#'
+else
+  PAND_TRUE='#'
+  PAND_FALSE=
+fi
+
+        if test "${dund_enable}" = "yes"; then
+  DUND_TRUE=
+  DUND_FALSE='#'
+else
+  DUND_TRUE='#'
+  DUND_FALSE=
+fi
+
+        if test "${cups_enable}" = "yes"; then
+  CUPS_TRUE=
+  CUPS_FALSE='#'
+else
+  CUPS_TRUE='#'
+  CUPS_FALSE=
+fi
+
+        if test "${test_enable}" = "yes" && test "${check_found}" = "yes"; then
+  TEST_TRUE=
+  TEST_FALSE='#'
+else
+  TEST_TRUE='#'
+  TEST_FALSE=
+fi
+
+        if test "${tools_enable}" = "yes"; then
+  TOOLS_TRUE=
+  TOOLS_FALSE='#'
+else
+  TOOLS_TRUE='#'
+  TOOLS_FALSE=
+fi
+
+        if test "${bccmd_enable}" = "yes"; then
+  BCCMD_TRUE=
+  BCCMD_FALSE='#'
+else
+  BCCMD_TRUE='#'
+  BCCMD_FALSE=
+fi
+
+        if test "${pcmcia_enable}" = "yes"; then
+  PCMCIA_TRUE=
+  PCMCIA_FALSE='#'
+else
+  PCMCIA_TRUE='#'
+  PCMCIA_FALSE=
+fi
+
+        if test "${hid2hci_enable}" = "yes" && test "${usb_found}" = "yes" && test "${udev_found}" = "yes"; then
+  HID2HCI_TRUE=
+  HID2HCI_FALSE='#'
+else
+  HID2HCI_TRUE='#'
+  HID2HCI_FALSE=
+fi
+
+        if test "${dfutool_enable}" = "yes" && test "${usb_found}" = "yes"; then
+  DFUTOOL_TRUE=
+  DFUTOOL_FALSE='#'
+else
+  DFUTOOL_TRUE='#'
+  DFUTOOL_FALSE=
+fi
+
+        if test "${datafiles_enable}" = "yes"; then
+  DATAFILES_TRUE=
+  DATAFILES_FALSE='#'
+else
+  DATAFILES_TRUE='#'
+  DATAFILES_FALSE=
+fi
+
+        if test "${maemo6_enable}" = "yes"; then
+  MAEMO6PLUGIN_TRUE=
+  MAEMO6PLUGIN_FALSE='#'
+else
+  MAEMO6PLUGIN_TRUE='#'
+  MAEMO6PLUGIN_FALSE=
+fi
+
+        if test "${dbusoob_enable}" = "yes"; then
+  DBUSOOBPLUGIN_TRUE=
+  DBUSOOBPLUGIN_FALSE='#'
+else
+  DBUSOOBPLUGIN_TRUE='#'
+  DBUSOOBPLUGIN_FALSE=
+fi
+
+        if test "${wiimote_enable}" = "yes"; then
+  WIIMOTEPLUGIN_TRUE=
+  WIIMOTEPLUGIN_FALSE='#'
+else
+  WIIMOTEPLUGIN_TRUE='#'
+  WIIMOTEPLUGIN_FALSE=
+fi
+
+        if test "${gatt_enable}" = "yes"; then
+  GATTMODULES_TRUE=
+  GATTMODULES_FALSE='#'
+else
+  GATTMODULES_TRUE='#'
+  GATTMODULES_FALSE=
+fi
+
+
+
+
+# Check whether --with-systemdunitdir was given.
+if test "${with_systemdunitdir+set}" = set; then :
+  withval=$with_systemdunitdir; path_systemdunit=${withval}
+else
+  path_systemdunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"
+fi
+
+if (test -n "${path_systemdunit}"); then
+       SYSTEMD_UNITDIR="${path_systemdunit}"
+
+fi
+ if test -n "${path_systemdunit}"; then
+  SYSTEMD_TRUE=
+  SYSTEMD_FALSE='#'
+else
+  SYSTEMD_TRUE='#'
+  SYSTEMD_FALSE=
+fi
+
+
+ac_config_files="$ac_config_files Makefile doc/version.xml src/bluetoothd.8 src/bluetooth.service bluez.pc"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+       cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+         mv -f confcache "$cache_file"$$ &&
+         mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+         mv -f confcache "$cache_file" ;;
+       esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+ if test -n "$EXEEXT"; then
+  am__EXEEXT_TRUE=
+  am__EXEEXT_FALSE='#'
+else
+  am__EXEEXT_TRUE='#'
+  am__EXEEXT_FALSE=
+fi
+
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+  as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+  as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+  as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SNDFILE_TRUE}" && test -z "${SNDFILE_FALSE}"; then
+  as_fn_error $? "conditional \"SNDFILE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${USB_TRUE}" && test -z "${USB_FALSE}"; then
+  as_fn_error $? "conditional \"USB\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SBC_TRUE}" && test -z "${SBC_FALSE}"; then
+  as_fn_error $? "conditional \"SBC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${ALSA_TRUE}" && test -z "${ALSA_FALSE}"; then
+  as_fn_error $? "conditional \"ALSA\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${GSTREAMER_TRUE}" && test -z "${GSTREAMER_FALSE}"; then
+  as_fn_error $? "conditional \"GSTREAMER\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${AUDIOPLUGIN_TRUE}" && test -z "${AUDIOPLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"AUDIOPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${INPUTPLUGIN_TRUE}" && test -z "${INPUTPLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"INPUTPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SERIALPLUGIN_TRUE}" && test -z "${SERIALPLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"SERIALPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${NETWORKPLUGIN_TRUE}" && test -z "${NETWORKPLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"NETWORKPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SAPPLUGIN_TRUE}" && test -z "${SAPPLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"SAPPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SERVICEPLUGIN_TRUE}" && test -z "${SERVICEPLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"SERVICEPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HEALTHPLUGIN_TRUE}" && test -z "${HEALTHPLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"HEALTHPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${MCAP_TRUE}" && test -z "${MCAP_FALSE}"; then
+  as_fn_error $? "conditional \"MCAP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAL_TRUE}" && test -z "${HAL_FALSE}"; then
+  as_fn_error $? "conditional \"HAL\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${READLINE_TRUE}" && test -z "${READLINE_FALSE}"; then
+  as_fn_error $? "conditional \"READLINE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${PNATPLUGIN_TRUE}" && test -z "${PNATPLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"PNATPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HIDD_TRUE}" && test -z "${HIDD_FALSE}"; then
+  as_fn_error $? "conditional \"HIDD\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${PAND_TRUE}" && test -z "${PAND_FALSE}"; then
+  as_fn_error $? "conditional \"PAND\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DUND_TRUE}" && test -z "${DUND_FALSE}"; then
+  as_fn_error $? "conditional \"DUND\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${CUPS_TRUE}" && test -z "${CUPS_FALSE}"; then
+  as_fn_error $? "conditional \"CUPS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${TEST_TRUE}" && test -z "${TEST_FALSE}"; then
+  as_fn_error $? "conditional \"TEST\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${TOOLS_TRUE}" && test -z "${TOOLS_FALSE}"; then
+  as_fn_error $? "conditional \"TOOLS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${BCCMD_TRUE}" && test -z "${BCCMD_FALSE}"; then
+  as_fn_error $? "conditional \"BCCMD\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${PCMCIA_TRUE}" && test -z "${PCMCIA_FALSE}"; then
+  as_fn_error $? "conditional \"PCMCIA\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HID2HCI_TRUE}" && test -z "${HID2HCI_FALSE}"; then
+  as_fn_error $? "conditional \"HID2HCI\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DFUTOOL_TRUE}" && test -z "${DFUTOOL_FALSE}"; then
+  as_fn_error $? "conditional \"DFUTOOL\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DATAFILES_TRUE}" && test -z "${DATAFILES_FALSE}"; then
+  as_fn_error $? "conditional \"DATAFILES\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${MAEMO6PLUGIN_TRUE}" && test -z "${MAEMO6PLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"MAEMO6PLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${DBUSOOBPLUGIN_TRUE}" && test -z "${DBUSOOBPLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"DBUSOOBPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${WIIMOTEPLUGIN_TRUE}" && test -z "${WIIMOTEPLUGIN_FALSE}"; then
+  as_fn_error $? "conditional \"WIIMOTEPLUGIN\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${GATTMODULES_TRUE}" && test -z "${GATTMODULES_FALSE}"; then
+  as_fn_error $? "conditional \"GATTMODULES\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SYSTEMD_TRUE}" && test -z "${SYSTEMD_FALSE}"; then
+  as_fn_error $? "conditional \"SYSTEMD\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by bluez $as_me 4.101, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+bluez config.status 4.101
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
+macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
+SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`'
+ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`'
+PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`'
+host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`'
+host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`'
+host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`'
+build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`'
+build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`'
+build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`'
+SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`'
+Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`'
+GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`'
+EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`'
+FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`'
+LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`'
+NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`'
+LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`'
+exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`'
+file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`'
+want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`'
+DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
+sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`'
+AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`'
+archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`'
+STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`'
+lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`'
+CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`'
+compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`'
+GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`'
+nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`'
+lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`'
+objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`'
+need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`'
+MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`'
+LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`'
+libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`'
+postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`'
+need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`'
+version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`'
+install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`'
+sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`'
+striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in SHELL \
+ECHO \
+PATH_SEPARATOR \
+SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+file_magic_glob \
+want_nocaseglob \
+DLLTOOL \
+sharedlib_from_linklib_cmd \
+AR \
+AR_FLAGS \
+archiver_list_spec \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+nm_file_list_spec \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_pic \
+lt_prog_compiler_wl \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+MANIFEST_TOOL \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_separator \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+install_override_mode \
+finish_eval \
+old_striplib \
+striplib; do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[\\\\\\\`\\"\\\$]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postlink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+sys_lib_dlsearch_path_spec; do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[\\\\\\\`\\"\\\$]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+ac_aux_dir='$ac_aux_dir'
+xsi_shell='$xsi_shell'
+lt_shell_append='$lt_shell_append'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+   setopt NO_GLOB_SUBST
+fi
+
+
+    PACKAGE='$PACKAGE'
+    VERSION='$VERSION'
+    TIMESTAMP='$TIMESTAMP'
+    RM='$RM'
+    ofile='$ofile'
+
+
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+    "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+    "doc/version.xml") CONFIG_FILES="$CONFIG_FILES doc/version.xml" ;;
+    "src/bluetoothd.8") CONFIG_FILES="$CONFIG_FILES src/bluetoothd.8" ;;
+    "src/bluetooth.service") CONFIG_FILES="$CONFIG_FILES src/bluetooth.service" ;;
+    "bluez.pc") CONFIG_FILES="$CONFIG_FILES bluez.pc" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=[    ]*/{
+h
+s///
+s/^/:/
+s/[     ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[  ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+  esac
+  ac_MKDIR_P=$MKDIR_P
+  case $MKDIR_P in
+  [\\/$]* | ?:[\\/]* ) ;;
+  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+  esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $_am_arg | $_am_arg:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$_am_arg" : 'X\(//\)[^/]' \| \
+        X"$_am_arg" : 'X\(//\)$' \| \
+        X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+  :C)  { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+  esac
+
+
+  case $ac_file$ac_mode in
+    "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+  # Autoconf 2.62 quotes --file arguments for eval, but not when files
+  # are listed without --file.  Let's play safe and only enable the eval
+  # if we detect the quoting.
+  case $CONFIG_FILES in
+  *\'*) eval set x "$CONFIG_FILES" ;;
+  *)   set x $CONFIG_FILES ;;
+  esac
+  shift
+  for mf
+  do
+    # Strip MF so we end up with the name of the file.
+    mf=`echo "$mf" | sed -e 's/:.*$//'`
+    # Check whether this is an Automake generated Makefile or not.
+    # We used to match only the files named `Makefile.in', but
+    # some people rename them; so instead we look at the file content.
+    # Grep'ing the first line is not enough: some people post-process
+    # each Makefile.in and add a new line on top of each file to say so.
+    # Grep'ing the whole file is not good either: AIX grep has a line
+    # limit of 2048, but all sed's we know have understand at least 4000.
+    if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+      dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$mf" : 'X\(//\)[^/]' \| \
+        X"$mf" : 'X\(//\)$' \| \
+        X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+    else
+      continue
+    fi
+    # Extract the definition of DEPDIR, am__include, and am__quote
+    # from the Makefile without running `make'.
+    DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+    test -z "$DEPDIR" && continue
+    am__include=`sed -n 's/^am__include = //p' < "$mf"`
+    test -z "am__include" && continue
+    am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+    # When using ansi2knr, U may be empty or an underscore; expand it
+    U=`sed -n 's/^U = //p' < "$mf"`
+    # Find all dependency output files, they are included files with
+    # $(DEPDIR) in their names.  We invoke sed twice because it is the
+    # simplest approach to changing $(DEPDIR) to its actual value in the
+    # expansion.
+    for file in `sed -n "
+      s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+        sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+      # Make sure the directory exists.
+      test -f "$dirpart/$file" && continue
+      fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$file" : 'X\(//\)[^/]' \| \
+        X"$file" : 'X\(//\)$' \| \
+        X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      as_dir=$dirpart/$fdir; as_fn_mkdir_p
+      # echo "creating $dirpart/$file"
+      echo '# dummy' > "$dirpart/$file"
+    done
+  done
+}
+ ;;
+    "libtool":C)
+
+    # See if we are running on zsh, and set the options which allow our
+    # commands through without removal of \ escapes.
+    if test -n "${ZSH_VERSION+set}" ; then
+      setopt NO_GLOB_SUBST
+    fi
+
+    cfgfile="${ofile}T"
+    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+    $RM "$cfgfile"
+
+    cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+#   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+#                 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+#                 Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+#   This file is part of GNU Libtool.
+#
+# GNU Libtool is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=""
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that protects backslashes.
+ECHO=$lt_ECHO
+
+# The PATH separator for the build system.
+PATH_SEPARATOR=$lt_PATH_SEPARATOR
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# convert \$build file names to \$host format.
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+
+# convert \$build files to toolchain format.
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method = "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# How to find potential files when deplibs_check_method = "file_magic".
+file_magic_glob=$lt_file_magic_glob
+
+# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
+want_nocaseglob=$lt_want_nocaseglob
+
+# DLL creation program.
+DLLTOOL=$lt_DLLTOOL
+
+# Command to associate shared and link libraries.
+sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd
+
+# The archiver.
+AR=$lt_AR
+
+# Flags to create an archive.
+AR_FLAGS=$lt_AR_FLAGS
+
+# How to feed a file listing to the archiver.
+archiver_list_spec=$lt_archiver_list_spec
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Whether to use a lock for old archive extraction.
+lock_old_archive_extraction=$lock_old_archive_extraction
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# Specify filename containing input files for \$NM.
+nm_file_list_spec=$lt_nm_file_list_spec
+
+# The root where to search for dependent libraries,and in which our libraries should be installed.
+lt_sysroot=$lt_sysroot
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Manifest tool.
+MANIFEST_TOOL=$lt_MANIFEST_TOOL
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Permission mode override for installation of shared libraries.
+install_override_mode=$lt_install_override_mode
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Commands necessary for finishing linking programs.
+postlink_cmds=$lt_postlink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+  case $host_os in
+  aix3*)
+    cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+_LT_EOF
+    ;;
+  esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "$cfgfile" \
+     || (rm -f "$cfgfile"; exit 1)
+
+  if test x"$xsi_shell" = xyes; then
+  sed -e '/^func_dirname ()$/,/^} # func_dirname /c\
+func_dirname ()\
+{\
+\    case ${1} in\
+\      */*) func_dirname_result="${1%/*}${2}" ;;\
+\      *  ) func_dirname_result="${3}" ;;\
+\    esac\
+} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_basename ()$/,/^} # func_basename /c\
+func_basename ()\
+{\
+\    func_basename_result="${1##*/}"\
+} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\
+func_dirname_and_basename ()\
+{\
+\    case ${1} in\
+\      */*) func_dirname_result="${1%/*}${2}" ;;\
+\      *  ) func_dirname_result="${3}" ;;\
+\    esac\
+\    func_basename_result="${1##*/}"\
+} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_stripname ()$/,/^} # func_stripname /c\
+func_stripname ()\
+{\
+\    # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\
+\    # positional parameters, so assign one to ordinary parameter first.\
+\    func_stripname_result=${3}\
+\    func_stripname_result=${func_stripname_result#"${1}"}\
+\    func_stripname_result=${func_stripname_result%"${2}"}\
+} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\
+func_split_long_opt ()\
+{\
+\    func_split_long_opt_name=${1%%=*}\
+\    func_split_long_opt_arg=${1#*=}\
+} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\
+func_split_short_opt ()\
+{\
+\    func_split_short_opt_arg=${1#??}\
+\    func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\
+} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\
+func_lo2o ()\
+{\
+\    case ${1} in\
+\      *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\
+\      *)    func_lo2o_result=${1} ;;\
+\    esac\
+} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_xform ()$/,/^} # func_xform /c\
+func_xform ()\
+{\
+    func_xform_result=${1%.*}.lo\
+} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_arith ()$/,/^} # func_arith /c\
+func_arith ()\
+{\
+    func_arith_result=$(( $* ))\
+} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_len ()$/,/^} # func_len /c\
+func_len ()\
+{\
+    func_len_result=${#1}\
+} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+fi
+
+if test x"$lt_shell_append" = xyes; then
+  sed -e '/^func_append ()$/,/^} # func_append /c\
+func_append ()\
+{\
+    eval "${1}+=\\${2}"\
+} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\
+func_append_quoted ()\
+{\
+\    func_quote_for_eval "${2}"\
+\    eval "${1}+=\\\\ \\$func_quote_for_eval_result"\
+} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \
+  && mv -f "$cfgfile.tmp" "$cfgfile" \
+    || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+test 0 -eq $? || _lt_function_replace_fail=:
+
+
+  # Save a `func_append' function call where possible by direct use of '+='
+  sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+else
+  # Save a `func_append' function call even when '+=' is not available
+  sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \
+    && mv -f "$cfgfile.tmp" "$cfgfile" \
+      || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
+  test 0 -eq $? || _lt_function_replace_fail=:
+fi
+
+if test x"$_lt_function_replace_fail" = x":"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5
+$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;}
+fi
+
+
+   mv -f "$cfgfile" "$ofile" ||
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+
+ ;;
+
+  esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..f2db920
--- /dev/null
@@ -0,0 +1,64 @@
+AC_PREREQ(2.60)
+AC_INIT(bluez, 4.101)
+
+AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
+AM_CONFIG_HEADER(config.h)
+
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+AM_MAINTAINER_MODE
+
+PKG_PROG_PKG_CONFIG
+
+AC_INIT_BLUEZ
+
+COMPILER_FLAGS
+
+AC_LANG_C
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CC_PIE
+AC_PROG_INSTALL
+AC_PROG_YACC
+AM_PROG_LEX
+AM_PROG_MKDIR_P
+
+m4_define([_LT_AC_TAGCONFIG], [])
+m4_ifdef([AC_LIBTOOL_TAGS], [AC_LIBTOOL_TAGS([])])
+
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+AC_FUNC_PPOLL
+
+AC_CHECK_LIB(dl, dlopen, dummy=yes,
+                       AC_MSG_ERROR(dynamic linking loader is required))
+
+AC_CHECK_HEADER([sys/inotify.h],
+               [AC_DEFINE([HAVE_SYS_INOTIFY_H], 1,
+                       [Define to 1 if you have <sys/inotify.h>.])],
+                       [AC_MSG_ERROR(inotify headers are required and missing)])
+AC_PATH_DBUS
+AC_PATH_GLIB
+AC_PATH_ALSA
+AC_PATH_GSTREAMER
+AC_PATH_USB
+AC_PATH_UDEV
+AC_PATH_SNDFILE
+AC_PATH_OUI
+AC_PATH_READLINE
+AC_PATH_CHECK
+
+AC_ARG_BLUEZ
+
+AC_ARG_WITH([systemdunitdir], AC_HELP_STRING([--with-systemdunitdir=DIR],
+       [path to systemd system service directory]), [path_systemdunit=${withval}],
+               [path_systemdunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"])
+if (test -n "${path_systemdunit}"); then
+       SYSTEMD_UNITDIR="${path_systemdunit}"
+       AC_SUBST(SYSTEMD_UNITDIR)
+fi
+AM_CONDITIONAL(SYSTEMD, test -n "${path_systemdunit}")
+
+AC_OUTPUT(Makefile doc/version.xml src/bluetoothd.8 src/bluetooth.service bluez.pc)
diff --git a/cups/cups.h b/cups/cups.h
new file mode 100644 (file)
index 0000000..f4e0c01
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+enum {                                 /**** Backend exit codes ****/
+       CUPS_BACKEND_OK = 0,            /* Job completed successfully */
+       CUPS_BACKEND_FAILED = 1,        /* Job failed, use error-policy */
+       CUPS_BACKEND_AUTH_REQUIRED = 2, /* Job failed, authentication required */
+       CUPS_BACKEND_HOLD = 3,          /* Job failed, hold job */
+       CUPS_BACKEND_STOP = 4,          /* Job failed, stop queue */
+       CUPS_BACKEND_CANCEL = 5,        /* Job failed, cancel job */
+       CUPS_BACKEND_RETRY = 6,         /* Failure requires us to retry (BlueZ specific) */
+};
+
+int sdp_search_spp(sdp_session_t *sdp, uint8_t *channel);
+int sdp_search_hcrp(sdp_session_t *sdp, unsigned short *ctrl_psm, unsigned short *data_psm);
+
+int spp_print(bdaddr_t *src, bdaddr_t *dst, uint8_t channel, int fd, int copies, const char *cups_class);
+int hcrp_print(bdaddr_t *src, bdaddr_t *dst, unsigned short ctrl_psm, unsigned short data_psm, int fd, int copies, const char *cups_class);
diff --git a/cups/hcrp.c b/cups/hcrp.c
new file mode 100644 (file)
index 0000000..a93dda0
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.h>
+
+#include "cups.h"
+
+#define HCRP_PDU_CREDIT_GRANT          0x0001
+#define HCRP_PDU_CREDIT_REQUEST                0x0002
+#define HCRP_PDU_GET_LPT_STATUS                0x0005
+
+#define HCRP_STATUS_FEATURE_UNSUPPORTED        0x0000
+#define HCRP_STATUS_SUCCESS            0x0001
+#define HCRP_STATUS_CREDIT_SYNC_ERROR  0x0002
+#define HCRP_STATUS_GENERIC_FAILURE    0xffff
+
+struct hcrp_pdu_hdr {
+       uint16_t pid;
+       uint16_t tid;
+       uint16_t plen;
+} __attribute__ ((packed));
+#define HCRP_PDU_HDR_SIZE 6
+
+struct hcrp_credit_grant_cp {
+       uint32_t credit;
+} __attribute__ ((packed));
+#define HCRP_CREDIT_GRANT_CP_SIZE 4
+
+struct hcrp_credit_grant_rp {
+       uint16_t status;
+} __attribute__ ((packed));
+#define HCRP_CREDIT_GRANT_RP_SIZE 2
+
+struct hcrp_credit_request_rp {
+       uint16_t status;
+       uint32_t credit;
+} __attribute__ ((packed));
+#define HCRP_CREDIT_REQUEST_RP_SIZE 6
+
+struct hcrp_get_lpt_status_rp {
+       uint16_t status;
+       uint8_t  lpt_status;
+} __attribute__ ((packed));
+#define HCRP_GET_LPT_STATUS_RP_SIZE 3
+
+static int hcrp_credit_grant(int sk, uint16_t tid, uint32_t credit)
+{
+       struct hcrp_pdu_hdr hdr;
+       struct hcrp_credit_grant_cp cp;
+       struct hcrp_credit_grant_rp rp;
+       unsigned char buf[128];
+       int len;
+
+       hdr.pid = htons(HCRP_PDU_CREDIT_GRANT);
+       hdr.tid = htons(tid);
+       hdr.plen = htons(HCRP_CREDIT_GRANT_CP_SIZE);
+       cp.credit = credit;
+       memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
+       memcpy(buf + HCRP_PDU_HDR_SIZE, &cp, HCRP_CREDIT_GRANT_CP_SIZE);
+       len = write(sk, buf, HCRP_PDU_HDR_SIZE + HCRP_CREDIT_GRANT_CP_SIZE);
+       if (len < 0)
+               return len;
+
+       len = read(sk, buf, sizeof(buf));
+       if (len < 0)
+               return len;
+
+       memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
+       memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_GRANT_RP_SIZE);
+
+       if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+static int hcrp_credit_request(int sk, uint16_t tid, uint32_t *credit)
+{
+       struct hcrp_pdu_hdr hdr;
+       struct hcrp_credit_request_rp rp;
+       unsigned char buf[128];
+       int len;
+
+       hdr.pid = htons(HCRP_PDU_CREDIT_REQUEST);
+       hdr.tid = htons(tid);
+       hdr.plen = htons(0);
+       memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
+       len = write(sk, buf, HCRP_PDU_HDR_SIZE);
+       if (len < 0)
+               return len;
+
+       len = read(sk, buf, sizeof(buf));
+       if (len < 0)
+               return len;
+
+       memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
+       memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_REQUEST_RP_SIZE);
+
+       if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) {
+               errno = EIO;
+               return -1;
+       }
+
+       if (credit)
+               *credit = ntohl(rp.credit);
+
+       return 0;
+}
+
+static int hcrp_get_lpt_status(int sk, uint16_t tid, uint8_t *lpt_status)
+{
+       struct hcrp_pdu_hdr hdr;
+       struct hcrp_get_lpt_status_rp rp;
+       unsigned char buf[128];
+       int len;
+
+       hdr.pid = htons(HCRP_PDU_GET_LPT_STATUS);
+       hdr.tid = htons(tid);
+       hdr.plen = htons(0);
+       memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
+       len = write(sk, buf, HCRP_PDU_HDR_SIZE);
+       if (len < 0)
+               return len;
+
+       len = read(sk, buf, sizeof(buf));
+       if (len < 0)
+               return len;
+
+       memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
+       memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_GET_LPT_STATUS_RP_SIZE);
+
+       if (ntohs(rp.status) != HCRP_STATUS_SUCCESS) {
+               errno = EIO;
+               return -1;
+       }
+
+       if (lpt_status)
+               *lpt_status = rp.lpt_status;
+
+       return 0;
+}
+
+static inline int hcrp_get_next_tid(int tid)
+{
+       if (tid > 0xf000)
+               return 0;
+       else
+               return tid + 1;
+}
+
+int hcrp_print(bdaddr_t *src, bdaddr_t *dst, unsigned short ctrl_psm, unsigned short data_psm, int fd, int copies, const char *cups_class)
+{
+       struct sockaddr_l2 addr;
+       struct l2cap_options opts;
+       socklen_t size;
+       unsigned char buf[2048];
+       int i, ctrl_sk, data_sk, count, len, timeout = 0;
+       unsigned int mtu;
+       uint8_t status;
+       uint16_t tid = 0;
+       uint32_t tmp, credit = 0;
+
+       if ((ctrl_sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
+               perror("ERROR: Can't create socket");
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, src);
+
+       if (bind(ctrl_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("ERROR: Can't bind socket");
+               close(ctrl_sk);
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, dst);
+       addr.l2_psm = htobs(ctrl_psm);
+
+       if (connect(ctrl_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("ERROR: Can't connect to device");
+               close(ctrl_sk);
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       if ((data_sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
+               perror("ERROR: Can't create socket");
+               close(ctrl_sk);
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, src);
+
+       if (bind(data_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("ERROR: Can't bind socket");
+               close(data_sk);
+               close(ctrl_sk);
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, dst);
+       addr.l2_psm = htobs(data_psm);
+
+       if (connect(data_sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("ERROR: Can't connect to device");
+               close(data_sk);
+               close(ctrl_sk);
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       fputs("STATE: -connecting-to-device\n", stderr);
+
+       memset(&opts, 0, sizeof(opts));
+       size = sizeof(opts);
+
+       if (getsockopt(data_sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) {
+               perror("ERROR: Can't get socket options");
+               close(data_sk);
+               close(ctrl_sk);
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       mtu = opts.omtu;
+
+       /* Ignore SIGTERM signals if printing from stdin */
+       if (fd == 0) {
+#ifdef HAVE_SIGSET
+               sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+               memset(&action, 0, sizeof(action));
+               sigemptyset(&action.sa_mask);
+               action.sa_handler = SIG_IGN;
+               sigaction(SIGTERM, &action, NULL);
+#else
+               signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+       }
+
+       tid = hcrp_get_next_tid(tid);
+       if (hcrp_credit_grant(ctrl_sk, tid, 0) < 0) {
+               fprintf(stderr, "ERROR: Can't grant initial credits\n");
+               close(data_sk);
+               close(ctrl_sk);
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       for (i = 0; i < copies; i++) {
+
+               if (fd != 0) {
+                       fprintf(stderr, "PAGE: 1 1\n");
+                       lseek(fd, 0, SEEK_SET);
+               }
+
+               while (1) {
+                       if (credit < mtu) {
+                               tid = hcrp_get_next_tid(tid);
+                               if (!hcrp_credit_request(ctrl_sk, tid, &tmp)) {
+                                       credit += tmp;
+                                       timeout = 0;
+                               }
+                       }
+
+                       if (!credit) {
+                               if (timeout++ > 300) {
+                                       tid = hcrp_get_next_tid(tid);
+                                       if (!hcrp_get_lpt_status(ctrl_sk, tid, &status))
+                                               fprintf(stderr, "ERROR: LPT status 0x%02x\n", status);
+                                       break;
+                               }
+
+                               sleep(1);
+                               continue;
+                       }
+
+                       count = read(fd, buf, (credit > mtu) ? mtu : credit);
+                       if (count <= 0)
+                               break;
+
+                       len = write(data_sk, buf, count);
+                       if (len < 0) {
+                               perror("ERROR: Error writing to device");
+                               close(data_sk);
+                               close(ctrl_sk);
+                               return CUPS_BACKEND_FAILED;
+                       }
+
+                       if (len != count)
+                               fprintf(stderr, "ERROR: Can't send complete data\n");
+
+                       credit -= len;
+               }
+
+       }
+
+       close(data_sk);
+       close(ctrl_sk);
+
+       return CUPS_BACKEND_OK;
+}
diff --git a/cups/main.c b/cups/main.c
new file mode 100644 (file)
index 0000000..a884c6e
--- /dev/null
@@ -0,0 +1,896 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <gdbus.h>
+
+#include "cups.h"
+
+struct cups_device {
+       char *bdaddr;
+       char *name;
+       char *id;
+};
+
+static GSList *device_list = NULL;
+static GMainLoop *loop = NULL;
+static DBusConnection *conn = NULL;
+static gboolean doing_disco = FALSE;
+
+#define ATTRID_1284ID 0x0300
+
+struct context_data {
+       gboolean found;
+       char *id;
+};
+
+static void element_start(GMarkupParseContext *context,
+                               const gchar *element_name,
+                               const gchar **attribute_names,
+                               const gchar **attribute_values,
+                               gpointer user_data, GError **err)
+{
+       struct context_data *ctx_data = user_data;
+
+       if (!strcmp(element_name, "record"))
+               return;
+
+       if (!strcmp(element_name, "attribute")) {
+               int i;
+               for (i = 0; attribute_names[i]; i++) {
+                       if (strcmp(attribute_names[i], "id") != 0)
+                               continue;
+                       if (strtol(attribute_values[i], 0, 0) == ATTRID_1284ID)
+                               ctx_data->found = TRUE;
+                       break;
+               }
+               return;
+       }
+
+       if (ctx_data->found  && !strcmp(element_name, "text")) {
+               int i;
+               for (i = 0; attribute_names[i]; i++) {
+                       if (!strcmp(attribute_names[i], "value")) {
+                               ctx_data->id = g_strdup(attribute_values[i] + 2);
+                               ctx_data->found = FALSE;
+                       }
+               }
+       }
+}
+
+static GMarkupParser parser = {
+       element_start, NULL, NULL, NULL, NULL
+};
+
+static char *sdp_xml_parse_record(const char *data)
+{
+       GMarkupParseContext *ctx;
+       struct context_data ctx_data;
+       int size;
+
+       size = strlen(data);
+       ctx_data.found = FALSE;
+       ctx_data.id = NULL;
+       ctx = g_markup_parse_context_new(&parser, 0, &ctx_data, NULL);
+
+       if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
+               g_markup_parse_context_free(ctx);
+               g_free(ctx_data.id);
+               return NULL;
+       }
+
+       g_markup_parse_context_free(ctx);
+
+       return ctx_data.id;
+}
+
+static char *device_get_ieee1284_id(const char *adapter, const char *device)
+{
+       DBusMessage *message, *reply;
+       DBusMessageIter iter, reply_iter;
+       DBusMessageIter reply_iter_entry;
+       const char *hcr_print = "00001126-0000-1000-8000-00805f9b34fb";
+       const char *xml;
+       char *id = NULL;
+
+       /* Look for the service handle of the HCRP service */
+       message = dbus_message_new_method_call("org.bluez", device,
+                                               "org.bluez.Device",
+                                               "DiscoverServices");
+       dbus_message_iter_init_append(message, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &hcr_print);
+
+       reply = dbus_connection_send_with_reply_and_block(conn,
+                                                       message, -1, NULL);
+
+       dbus_message_unref(message);
+
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init(reply, &reply_iter);
+
+       if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       dbus_message_iter_recurse(&reply_iter, &reply_iter_entry);
+
+       /* Hopefully we only get one handle, or take a punt */
+       while (dbus_message_iter_get_arg_type(&reply_iter_entry) ==
+                                                       DBUS_TYPE_DICT_ENTRY) {
+               guint32 key;
+               DBusMessageIter dict_entry;
+
+               dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
+
+               /* Key ? */
+               dbus_message_iter_get_basic(&dict_entry, &key);
+               if (!key) {
+                       dbus_message_iter_next(&reply_iter_entry);
+                       continue;
+               }
+
+               /* Try to get the value */
+               if (!dbus_message_iter_next(&dict_entry)) {
+                       dbus_message_iter_next(&reply_iter_entry);
+                       continue;
+               }
+
+               dbus_message_iter_get_basic(&dict_entry, &xml);
+
+               id = sdp_xml_parse_record(xml);
+               if (id != NULL)
+                       break;
+               dbus_message_iter_next(&reply_iter_entry);
+       }
+
+       dbus_message_unref(reply);
+
+       return id;
+}
+
+static void print_printer_details(const char *name, const char *bdaddr,
+                                                               const char *id)
+{
+       char *uri, *escaped;
+
+       escaped = g_strdelimit(g_strdup(name), "\"", '\'');
+       uri = g_strdup_printf("bluetooth://%c%c%c%c%c%c%c%c%c%c%c%c",
+                               bdaddr[0], bdaddr[1],
+                               bdaddr[3], bdaddr[4],
+                               bdaddr[6], bdaddr[7],
+                               bdaddr[9], bdaddr[10],
+                               bdaddr[12], bdaddr[13],
+                               bdaddr[15], bdaddr[16]);
+       printf("direct %s \"%s\" \"%s (Bluetooth)\"", uri, escaped, escaped);
+       if (id != NULL)
+               printf(" \"%s\"\n", id);
+       else
+               printf("\n");
+       g_free(escaped);
+       g_free(uri);
+}
+
+static void add_device_to_list(const char *name, const char *bdaddr,
+                                                               const char *id)
+{
+       struct cups_device *device;
+       GSList *l;
+
+       /* Look for the device in the list */
+       for (l = device_list; l != NULL; l = l->next) {
+               device = (struct cups_device *) l->data;
+
+               if (strcmp(device->bdaddr, bdaddr) == 0) {
+                       if (device->name != name) {
+                               g_free(device->name);
+                               device->name = g_strdup(name);
+                       }
+                       g_free(device->id);
+                       device->id = g_strdup(id);
+                       return;
+               }
+       }
+
+       /* Or add it to the list if it's not there */
+       device = g_new0(struct cups_device, 1);
+       device->bdaddr = g_strdup(bdaddr);
+       device->name = g_strdup(name);
+       device->id = g_strdup(id);
+
+       device_list = g_slist_prepend(device_list, device);
+       print_printer_details(device->name, device->bdaddr, device->id);
+}
+
+static gboolean parse_device_properties(DBusMessageIter *reply_iter,
+                                               char **name, char **bdaddr)
+{
+       guint32 class = 0;
+       DBusMessageIter reply_iter_entry;
+
+       if (dbus_message_iter_get_arg_type(reply_iter) != DBUS_TYPE_ARRAY)
+               return FALSE;
+
+       dbus_message_iter_recurse(reply_iter, &reply_iter_entry);
+
+       while (dbus_message_iter_get_arg_type(&reply_iter_entry) ==
+                                                       DBUS_TYPE_DICT_ENTRY) {
+               const char *key;
+               DBusMessageIter dict_entry, iter_dict_val;
+
+               dbus_message_iter_recurse(&reply_iter_entry, &dict_entry);
+
+               /* Key == Class ? */
+               dbus_message_iter_get_basic(&dict_entry, &key);
+               if (!key) {
+                       dbus_message_iter_next(&reply_iter_entry);
+                       continue;
+               }
+
+               if (strcmp(key, "Class") != 0 &&
+                               strcmp(key, "Alias") != 0 &&
+                               strcmp(key, "Address") != 0) {
+                       dbus_message_iter_next(&reply_iter_entry);
+                       continue;
+               }
+
+               /* Try to get the value */
+               if (!dbus_message_iter_next(&dict_entry)) {
+                       dbus_message_iter_next(&reply_iter_entry);
+                       continue;
+               }
+               dbus_message_iter_recurse(&dict_entry, &iter_dict_val);
+               if (strcmp(key, "Class") == 0) {
+                       dbus_message_iter_get_basic(&iter_dict_val, &class);
+               } else {
+                       const char *value;
+                       dbus_message_iter_get_basic(&iter_dict_val, &value);
+                       if (strcmp(key, "Alias") == 0) {
+                               *name = g_strdup(value);
+                       } else if (bdaddr) {
+                               *bdaddr = g_strdup(value);
+                       }
+               }
+               dbus_message_iter_next(&reply_iter_entry);
+       }
+
+       if (class == 0)
+               return FALSE;
+       if (((class & 0x1f00) >> 8) == 0x06 && (class & 0x80))
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean device_is_printer(const char *adapter, const char *device_path, char **name, char **bdaddr)
+{
+       DBusMessage *message, *reply;
+       DBusMessageIter reply_iter;
+       gboolean retval;
+
+       message = dbus_message_new_method_call("org.bluez", device_path,
+                                                       "org.bluez.Device",
+                                                       "GetProperties");
+
+       reply = dbus_connection_send_with_reply_and_block(conn,
+                                                       message, -1, NULL);
+
+       dbus_message_unref(message);
+
+       if (!reply)
+               return FALSE;
+
+       dbus_message_iter_init(reply, &reply_iter);
+
+       retval = parse_device_properties(&reply_iter, name, bdaddr);
+
+       dbus_message_unref(reply);
+
+       return retval;
+}
+
+static void remote_device_found(const char *adapter, const char *bdaddr,
+                                                       const char *name)
+{
+       DBusMessage *message, *reply, *adapter_reply;
+       DBusMessageIter iter;
+       char *object_path = NULL;
+       char *id;
+
+       adapter_reply = NULL;
+
+       if (adapter == NULL) {
+               message = dbus_message_new_method_call("org.bluez", "/",
+                                                       "org.bluez.Manager",
+                                                       "DefaultAdapter");
+
+               adapter_reply = dbus_connection_send_with_reply_and_block(conn,
+                                                       message, -1, NULL);
+
+               dbus_message_unref(message);
+
+               if (!adapter_reply)
+                       return;
+
+               if (dbus_message_get_args(adapter_reply, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &adapter,
+                                       DBUS_TYPE_INVALID) == FALSE) {
+                       dbus_message_unref(adapter_reply);
+                       return;
+               }
+       }
+
+       message = dbus_message_new_method_call("org.bluez", adapter,
+                                                       "org.bluez.Adapter",
+                                                       "FindDevice");
+       dbus_message_iter_init_append(message, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
+
+       if (adapter_reply != NULL)
+               dbus_message_unref(adapter_reply);
+
+       reply = dbus_connection_send_with_reply_and_block(conn,
+                                                       message, -1, NULL);
+
+       dbus_message_unref(message);
+
+       if (!reply) {
+               message = dbus_message_new_method_call("org.bluez", adapter,
+                                                       "org.bluez.Adapter",
+                                                       "CreateDevice");
+               dbus_message_iter_init_append(message, &iter);
+               dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
+
+               reply = dbus_connection_send_with_reply_and_block(conn,
+                                                       message, -1, NULL);
+
+               dbus_message_unref(message);
+
+               if (!reply)
+                       return;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &object_path,
+                                       DBUS_TYPE_INVALID) == FALSE) {
+               dbus_message_unref(reply);
+               return;
+       }
+
+       id = device_get_ieee1284_id(adapter, object_path);
+       add_device_to_list(name, bdaddr, id);
+       g_free(id);
+
+       dbus_message_unref(reply);
+}
+
+static void discovery_completed(void)
+{
+       g_slist_free(device_list);
+       device_list = NULL;
+
+       g_main_loop_quit(loop);
+}
+
+static void remote_device_disappeared(const char *bdaddr)
+{
+       GSList *l;
+
+       for (l = device_list; l != NULL; l = l->next) {
+               struct cups_device *device = l->data;
+
+               if (strcmp(device->bdaddr, bdaddr) == 0) {
+                       g_free(device->name);
+                       g_free(device->bdaddr);
+                       g_free(device);
+                       device_list = g_slist_delete_link(device_list, l);
+                       return;
+               }
+       }
+}
+
+static gboolean list_known_printers(const char *adapter)
+{
+       DBusMessageIter reply_iter, iter_array;
+       DBusError error;
+       DBusMessage *message, *reply;
+
+       message = dbus_message_new_method_call("org.bluez", adapter,
+                                               "org.bluez.Adapter",
+                                               "ListDevices");
+       if (message == NULL)
+               return FALSE;
+
+       dbus_error_init(&error);
+       reply = dbus_connection_send_with_reply_and_block(conn, message,
+                                                               -1, &error);
+
+       dbus_message_unref(message);
+
+       if (dbus_error_is_set(&error))
+               return FALSE;
+
+       dbus_message_iter_init(reply, &reply_iter);
+       if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
+               dbus_message_unref(reply);
+               return FALSE;
+       }
+
+       dbus_message_iter_recurse(&reply_iter, &iter_array);
+       while (dbus_message_iter_get_arg_type(&iter_array) ==
+                                               DBUS_TYPE_OBJECT_PATH) {
+               const char *object_path;
+               char *name = NULL;
+               char *bdaddr = NULL;
+
+               dbus_message_iter_get_basic(&iter_array, &object_path);
+               if (device_is_printer(adapter, object_path, &name, &bdaddr)) {
+                       char *id;
+
+                       id = device_get_ieee1284_id(adapter, object_path);
+                       add_device_to_list(name, bdaddr, id);
+                       g_free(id);
+               }
+               g_free(name);
+               g_free(bdaddr);
+               dbus_message_iter_next(&iter_array);
+       }
+
+       dbus_message_unref(reply);
+
+       return FALSE;
+}
+
+static DBusHandlerResult filter_func(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       if (dbus_message_is_signal(message, "org.bluez.Adapter",
+                                               "DeviceFound")) {
+               const char *adapter, *bdaddr;
+               char *name;
+               DBusMessageIter iter;
+
+               dbus_message_iter_init(message, &iter);
+               dbus_message_iter_get_basic(&iter, &bdaddr);
+               dbus_message_iter_next(&iter);
+
+               adapter = dbus_message_get_path(message);
+               if (parse_device_properties(&iter, &name, NULL))
+                       remote_device_found(adapter, bdaddr, name);
+               g_free (name);
+       } else if (dbus_message_is_signal(message, "org.bluez.Adapter",
+                                               "DeviceDisappeared")) {
+               const char *bdaddr;
+
+               dbus_message_get_args(message, NULL,
+                                       DBUS_TYPE_STRING, &bdaddr,
+                                       DBUS_TYPE_INVALID);
+               remote_device_disappeared(bdaddr);
+       } else if (dbus_message_is_signal(message, "org.bluez.Adapter",
+                                               "PropertyChanged")) {
+               DBusMessageIter iter, value_iter;
+               const char *name;
+               gboolean discovering;
+
+               dbus_message_iter_init(message, &iter);
+               dbus_message_iter_get_basic(&iter, &name);
+               if (name == NULL || strcmp(name, "Discovering") != 0)
+                       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+               dbus_message_iter_next(&iter);
+               dbus_message_iter_recurse(&iter, &value_iter);
+               dbus_message_iter_get_basic(&value_iter, &discovering);
+
+               if (discovering == FALSE && doing_disco) {
+                       doing_disco = FALSE;
+                       discovery_completed();
+               }
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean list_printers(void)
+{
+       /* 1. Connect to the bus
+        * 2. Get the manager
+        * 3. Get the default adapter
+        * 4. Get a list of devices
+        * 5. Get the class of each device
+        * 6. Print the details from each printer device
+        */
+       DBusError error;
+       dbus_bool_t hcid_exists;
+       DBusMessage *reply, *message;
+       DBusMessageIter reply_iter;
+       char *adapter, *match;
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+       if (conn == NULL)
+               return TRUE;
+
+       dbus_error_init(&error);
+       hcid_exists = dbus_bus_name_has_owner(conn, "org.bluez", &error);
+       if (dbus_error_is_set(&error))
+               return TRUE;
+
+       if (!hcid_exists)
+               return TRUE;
+
+       /* Get the default adapter */
+       message = dbus_message_new_method_call("org.bluez", "/",
+                                               "org.bluez.Manager",
+                                               "DefaultAdapter");
+       if (message == NULL) {
+               dbus_connection_unref(conn);
+               return FALSE;
+       }
+
+       reply = dbus_connection_send_with_reply_and_block(conn,
+                                                       message, -1, &error);
+
+       dbus_message_unref(message);
+
+       if (dbus_error_is_set(&error)) {
+               dbus_connection_unref(conn);
+               /* No adapter */
+               return TRUE;
+       }
+
+       dbus_message_iter_init(reply, &reply_iter);
+       if (dbus_message_iter_get_arg_type(&reply_iter) !=
+                                               DBUS_TYPE_OBJECT_PATH) {
+               dbus_message_unref(reply);
+               dbus_connection_unref(conn);
+               return FALSE;
+       }
+
+       dbus_message_iter_get_basic(&reply_iter, &adapter);
+       adapter = g_strdup(adapter);
+       dbus_message_unref(reply);
+
+       if (!dbus_connection_add_filter(conn, filter_func, adapter, g_free)) {
+               g_free(adapter);
+               dbus_connection_unref(conn);
+               return FALSE;
+       }
+
+#define MATCH_FORMAT                           \
+       "type='signal',"                        \
+       "interface='org.bluez.Adapter',"        \
+       "sender='org.bluez',"                   \
+       "path='%s'"
+
+       match = g_strdup_printf(MATCH_FORMAT, adapter);
+       dbus_bus_add_match(conn, match, &error);
+       g_free(match);
+
+       /* Add the the recent devices */
+       list_known_printers(adapter);
+
+       doing_disco = TRUE;
+       message = dbus_message_new_method_call("org.bluez", adapter,
+                                       "org.bluez.Adapter",
+                                       "StartDiscovery");
+
+       if (!dbus_connection_send_with_reply(conn, message, NULL, -1)) {
+               dbus_message_unref(message);
+               dbus_connection_unref(conn);
+               g_free(adapter);
+               return FALSE;
+       }
+       dbus_message_unref(message);
+
+       loop = g_main_loop_new(NULL, TRUE);
+       g_main_loop_run(loop);
+
+       g_free(adapter);
+       dbus_connection_unref(conn);
+
+       return TRUE;
+}
+
+static gboolean print_ieee1284(const char *bdaddr)
+{
+       DBusMessage *message, *reply, *adapter_reply;
+       DBusMessageIter iter;
+       char *object_path = NULL;
+       char *adapter;
+       char *id;
+
+       adapter_reply = NULL;
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+       if (conn == NULL)
+               return FALSE;
+
+       message = dbus_message_new_method_call("org.bluez", "/",
+                       "org.bluez.Manager",
+                       "DefaultAdapter");
+
+       adapter_reply = dbus_connection_send_with_reply_and_block(conn,
+                       message, -1, NULL);
+
+       dbus_message_unref(message);
+
+       if (!adapter_reply)
+               return FALSE;
+
+       if (dbus_message_get_args(adapter_reply, NULL,
+                       DBUS_TYPE_OBJECT_PATH, &adapter,
+                       DBUS_TYPE_INVALID) == FALSE) {
+               dbus_message_unref(adapter_reply);
+               return FALSE;
+       }
+
+       message = dbus_message_new_method_call("org.bluez", adapter,
+                       "org.bluez.Adapter",
+                       "FindDevice");
+       dbus_message_iter_init_append(message, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &bdaddr);
+
+       if (adapter_reply != NULL)
+               dbus_message_unref(adapter_reply);
+
+       reply = dbus_connection_send_with_reply_and_block(conn,
+                       message, -1, NULL);
+
+       dbus_message_unref(message);
+
+       if (!reply) {
+               message = dbus_message_new_method_call("org.bluez", adapter,
+                               "org.bluez.Adapter",
+                               "CreateDevice");
+               dbus_message_iter_init_append(message, &iter);
+               dbus_message_iter_append_basic(&iter,
+                               DBUS_TYPE_STRING, &bdaddr);
+
+               reply = dbus_connection_send_with_reply_and_block(conn,
+                               message, -1, NULL);
+
+               dbus_message_unref(message);
+
+               if (!reply)
+                       return FALSE;
+       }
+
+       if (dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_OBJECT_PATH, &object_path,
+                                       DBUS_TYPE_INVALID) == FALSE) {
+               dbus_message_unref(reply);
+               return FALSE;
+       }
+
+       id = device_get_ieee1284_id(adapter, object_path);
+       if (id == NULL) {
+               dbus_message_unref(reply);
+               return FALSE;
+       }
+       printf("%s", id);
+       g_free(id);
+
+       dbus_message_unref(reply);
+
+       return TRUE;
+}
+
+/*
+ *  Usage: printer-uri job-id user title copies options [file]
+ *
+ */
+
+int main(int argc, char *argv[])
+{
+       sdp_session_t *sdp;
+       bdaddr_t bdaddr;
+       unsigned short ctrl_psm, data_psm;
+       uint8_t channel, b[6];
+       char *ptr, str[3], device[18], service[12];
+       const char *uri, *cups_class;
+       int i, err, fd, copies, proto;
+
+       /* Make sure status messages are not buffered */
+       setbuf(stderr, NULL);
+
+       /* Make sure output is not buffered */
+       setbuf(stdout, NULL);
+
+       /* Ignore SIGPIPE signals */
+#ifdef HAVE_SIGSET
+       sigset(SIGPIPE, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+       memset(&action, 0, sizeof(action));
+       action.sa_handler = SIG_IGN;
+       sigaction(SIGPIPE, &action, NULL);
+#else
+       signal(SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+
+       if (argc == 1) {
+               if (list_printers() == TRUE)
+                       return CUPS_BACKEND_OK;
+               else
+                       return CUPS_BACKEND_FAILED;
+       } else if (argc == 3 && strcmp(argv[1], "--get-deviceid") == 0) {
+               if (bachk(argv[2]) < 0) {
+                       fprintf(stderr, "Invalid Bluetooth address '%s'\n",
+                                       argv[2]);
+                       return CUPS_BACKEND_FAILED;
+               }
+               if (print_ieee1284(argv[2]) == FALSE)
+                       return CUPS_BACKEND_FAILED;
+               return CUPS_BACKEND_OK;
+       }
+
+       if (argc < 6 || argc > 7) {
+               fprintf(stderr, "Usage: bluetooth job-id user title copies"
+                               " options [file]\n");
+               fprintf(stderr, "       bluetooth --get-deviceid [bdaddr]\n");
+               return CUPS_BACKEND_FAILED;
+       }
+
+       if (argc == 6) {
+               fd = 0;
+               copies = 1;
+       } else {
+               if ((fd = open(argv[6], O_RDONLY)) < 0) {
+                       perror("ERROR: Unable to open print file");
+                       return CUPS_BACKEND_FAILED;
+               }
+               copies = atoi(argv[4]);
+       }
+
+       uri = getenv("DEVICE_URI");
+       if (!uri)
+               uri = argv[0];
+
+       if (strncasecmp(uri, "bluetooth://", 12)) {
+               fprintf(stderr, "ERROR: No device URI found\n");
+               return CUPS_BACKEND_FAILED;
+       }
+
+       ptr = argv[0] + 12;
+       for (i = 0; i < 6; i++) {
+               strncpy(str, ptr, 2);
+               b[i] = (uint8_t) strtol(str, NULL, 16);
+               ptr += 2;
+       }
+       sprintf(device, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+                       b[0], b[1], b[2], b[3], b[4], b[5]);
+
+       str2ba(device, &bdaddr);
+
+       ptr = strchr(ptr, '/');
+       if (ptr) {
+               strncpy(service, ptr + 1, 12);
+
+               if (!strncasecmp(ptr + 1, "spp", 3))
+                       proto = 1;
+               else if (!strncasecmp(ptr + 1, "hcrp", 4))
+                       proto = 2;
+               else
+                       proto = 0;
+       } else {
+               strcpy(service, "auto");
+               proto = 0;
+       }
+
+       cups_class = getenv("CLASS");
+
+       fprintf(stderr,
+               "DEBUG: %s device %s service %s fd %d copies %d class %s\n",
+                       argv[0], device, service, fd, copies,
+                       cups_class ? cups_class : "(none)");
+
+       fputs("STATE: +connecting-to-device\n", stderr);
+
+service_search:
+       sdp = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
+       if (!sdp) {
+               fprintf(stderr, "ERROR: Can't open Bluetooth connection\n");
+               return CUPS_BACKEND_FAILED;
+       }
+
+       switch (proto) {
+       case 1:
+               err = sdp_search_spp(sdp, &channel);
+               break;
+       case 2:
+               err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
+               break;
+       default:
+               proto = 2;
+               err = sdp_search_hcrp(sdp, &ctrl_psm, &data_psm);
+               if (err) {
+                       proto = 1;
+                       err = sdp_search_spp(sdp, &channel);
+               }
+               break;
+       }
+
+       sdp_close(sdp);
+
+       if (err) {
+               if (cups_class) {
+                       fputs("INFO: Unable to contact printer, queuing on "
+                                       "next printer in class...\n", stderr);
+                       sleep(5);
+                       return CUPS_BACKEND_FAILED;
+               }
+               sleep(20);
+               fprintf(stderr, "ERROR: Can't get service information\n");
+               goto service_search;
+       }
+
+connect:
+       switch (proto) {
+       case 1:
+               err = spp_print(BDADDR_ANY, &bdaddr, channel,
+                                               fd, copies, cups_class);
+               break;
+       case 2:
+               err = hcrp_print(BDADDR_ANY, &bdaddr, ctrl_psm, data_psm,
+                                               fd, copies, cups_class);
+               break;
+       default:
+               err = CUPS_BACKEND_FAILED;
+               fprintf(stderr, "ERROR: Unsupported protocol\n");
+               break;
+       }
+
+       if (err == CUPS_BACKEND_FAILED && cups_class) {
+               fputs("INFO: Unable to contact printer, queuing on "
+                                       "next printer in class...\n", stderr);
+               sleep(5);
+               return CUPS_BACKEND_FAILED;
+       } else if (err == CUPS_BACKEND_RETRY) {
+               sleep(20);
+               goto connect;
+       }
+
+       if (fd != 0)
+               close(fd);
+
+       if (!err)
+               fprintf(stderr, "INFO: Ready to print\n");
+
+       return err;
+}
diff --git a/cups/sdp.c b/cups/sdp.c
new file mode 100644 (file)
index 0000000..c7f17a4
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "cups.h"
+
+int sdp_search_hcrp(sdp_session_t *sdp, unsigned short *ctrl_psm, unsigned short *data_psm)
+{
+       sdp_list_t *srch, *attrs, *rsp;
+       uuid_t svclass;
+       uint16_t attr1, attr2;
+       int err;
+
+       if (!sdp)
+               return -1;
+
+       sdp_uuid16_create(&svclass, HCR_PRINT_SVCLASS_ID);
+       srch = sdp_list_append(NULL, &svclass);
+
+       attr1 = SDP_ATTR_PROTO_DESC_LIST;
+       attrs = sdp_list_append(NULL, &attr1);
+       attr2 = SDP_ATTR_ADD_PROTO_DESC_LIST;
+       attrs = sdp_list_append(attrs, &attr2);
+
+       err = sdp_service_search_attr_req(sdp, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+       if (err)
+               return -1;
+
+       for (; rsp; rsp = rsp->next) {
+               sdp_record_t *rec = (sdp_record_t *) rsp->data;
+               sdp_list_t *protos;
+
+               if (!sdp_get_access_protos(rec, &protos)) {
+                       unsigned short psm = sdp_get_proto_port(protos, L2CAP_UUID);
+                       if (psm > 0) {
+                               *ctrl_psm = psm;
+                       }
+               }
+
+               if (!sdp_get_add_access_protos(rec, &protos)) {
+                       unsigned short psm = sdp_get_proto_port(protos, L2CAP_UUID);
+                       if (psm > 0 && *ctrl_psm > 0) {
+                               *data_psm = psm;
+                               return 0;
+                       }
+               }
+       }
+
+       return -1;
+}
+
+int sdp_search_spp(sdp_session_t *sdp, uint8_t *channel)
+{
+       sdp_list_t *srch, *attrs, *rsp;
+       uuid_t svclass;
+       uint16_t attr;
+       int err;
+
+       if (!sdp)
+               return -1;
+
+       sdp_uuid16_create(&svclass, SERIAL_PORT_SVCLASS_ID);
+       srch = sdp_list_append(NULL, &svclass);
+
+       attr = SDP_ATTR_PROTO_DESC_LIST;
+       attrs = sdp_list_append(NULL, &attr);
+
+       err = sdp_service_search_attr_req(sdp, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+       if (err)
+               return -1;
+
+       for (; rsp; rsp = rsp->next) {
+               sdp_record_t *rec = (sdp_record_t *) rsp->data;
+               sdp_list_t *protos;
+
+               if (!sdp_get_access_protos(rec, &protos)) {
+                       uint8_t ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+                       if (ch > 0) {
+                               *channel = ch;
+                               return 0;
+                       }
+               }
+       }
+
+       return -1;
+}
diff --git a/cups/spp.c b/cups/spp.c
new file mode 100644 (file)
index 0000000..d906ed2
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "cups.h"
+
+int spp_print(bdaddr_t *src, bdaddr_t *dst, uint8_t channel, int fd, int copies, const char *cups_class)
+{
+       struct sockaddr_rc addr;
+       unsigned char buf[2048];
+       int i, sk, err, len;
+
+       if ((sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
+               perror("ERROR: Can't create socket");
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, src);
+       addr.rc_channel = 0;
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("ERROR: Can't bind socket");
+               close(sk);
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, dst);
+       addr.rc_channel = channel;
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("ERROR: Can't connect to device");
+               close(sk);
+               if (cups_class)
+                       return CUPS_BACKEND_FAILED;
+               else
+                       return CUPS_BACKEND_RETRY;
+       }
+
+       fputs("STATE: -connecting-to-device\n", stderr);
+
+       /* Ignore SIGTERM signals if printing from stdin */
+       if (fd == 0) {
+#ifdef HAVE_SIGSET
+               sigset(SIGTERM, SIG_IGN);
+#elif defined(HAVE_SIGACTION)
+               memset(&action, 0, sizeof(action));
+               sigemptyset(&action.sa_mask);
+               action.sa_handler = SIG_IGN;
+               sigaction(SIGTERM, &action, NULL);
+#else
+               signal(SIGTERM, SIG_IGN);
+#endif /* HAVE_SIGSET */
+       }
+
+       for (i = 0; i < copies; i++) {
+
+               if (fd != 0) {
+                       fprintf(stderr, "PAGE: 1 1\n");
+                       lseek(fd, 0, SEEK_SET);
+               }
+
+               while ((len = read(fd, buf, sizeof(buf))) > 0) {
+                       err = write(sk, buf, len);
+                       if (err < 0) {
+                               perror("ERROR: Error writing to device");
+                               close(sk);
+                               return CUPS_BACKEND_FAILED;
+                       }
+               }
+
+       }
+
+       close(sk);
+
+       return CUPS_BACKEND_OK;
+}
diff --git a/depcomp b/depcomp
new file mode 100755 (executable)
index 0000000..bd0ac08
--- /dev/null
+++ b/depcomp
@@ -0,0 +1,688 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2011-12-04.11; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010,
+# 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+  '')
+     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+  depmode     Dependency tracking mode.
+  source      Source file read by `PROGRAMS ARGS'.
+  object      Object file output by `PROGRAMS ARGS'.
+  DEPDIR      directory where to store dependencies.
+  depfile     Dependency file to output.
+  tmpdepfile  Temporary file to use when outputting dependencies.
+  libtool     Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v | --v*)
+    echo "depcomp $scriptversion"
+    exit $?
+    ;;
+esac
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+  echo "depcomp: Variables source, object and depmode must be set" 1>&2
+  exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+  sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags.  We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write.  Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+  # HP compiler uses -M and no extra arg.
+  gccflag=-M
+  depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+   # This is just like dashmstdout with a different argument.
+   dashmflag=-xM
+   depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+   # This is just like msvisualcpp but w/o cygpath translation.
+   # Just convert the backslash-escaped backslashes to single forward
+   # slashes to satisfy depend.m4
+   cygpath_u='sed s,\\\\,/,g'
+   depmode=msvisualcpp
+fi
+
+if test "$depmode" = msvc7msys; then
+   # This is just like msvc7 but w/o cygpath translation.
+   # Just convert the backslash-escaped backslashes to single forward
+   # slashes to satisfy depend.m4
+   cygpath_u='sed s,\\\\,/,g'
+   depmode=msvc7
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff.  Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am.  Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+  for arg
+  do
+    case $arg in
+    -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+    *)  set fnord "$@" "$arg" ;;
+    esac
+    shift # fnord
+    shift # $arg
+  done
+  "$@"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  mv "$tmpdepfile" "$depfile"
+  ;;
+
+gcc)
+## There are various ways to get dependency output from gcc.  Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+##   up in a subdir.  Having to rename by hand is ugly.
+##   (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+##   -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+##   than renaming).
+  if test -z "$gccflag"; then
+    gccflag=-MD,
+  fi
+  "$@" -Wp,"$gccflag$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+  sed -e 's/^[^:]*: / /' \
+      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header).  We avoid this by adding
+## dummy dependencies for each header file.  Too bad gcc doesn't do
+## this for us directly.
+  tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'.  On the theory
+## that the space means something, we add a space to the output as
+## well.  hp depmode also adds that space, but also prefixes the VPATH
+## to the object.  Take care to not repeat it in the output.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
+      | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+sgi)
+  if test "$libtool" = yes; then
+    "$@" "-Wp,-MDupdate,$tmpdepfile"
+  else
+    "$@" -MDupdate "$tmpdepfile"
+  fi
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+
+  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
+    echo "$object : \\" > "$depfile"
+
+    # Clip off the initial element (the dependent).  Don't try to be
+    # clever and replace this with sed code, as IRIX sed won't handle
+    # lines with more than a fixed number of characters (4096 in
+    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
+    # the IRIX cc adds comments like `#:fec' to the end of the
+    # dependency line.
+    tr ' ' '
+' < "$tmpdepfile" \
+    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+    tr '
+' ' ' >> "$depfile"
+    echo >> "$depfile"
+
+    # The second pass generates a dummy entry for each header file.
+    tr ' ' '
+' < "$tmpdepfile" \
+   | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+   >> "$depfile"
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+aix)
+  # The C for AIX Compiler uses -M and outputs the dependencies
+  # in a .u file.  In older versions, this file always lives in the
+  # current directory.  Also, the AIX compiler puts `$object:' at the
+  # start of each line; $object doesn't have directory information.
+  # Version 6 uses the directory in both cases.
+  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+  test "x$dir" = "x$object" && dir=
+  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$base.u
+    tmpdepfile3=$dir.libs/$base.u
+    "$@" -Wc,-M
+  else
+    tmpdepfile1=$dir$base.u
+    tmpdepfile2=$dir$base.u
+    tmpdepfile3=$dir$base.u
+    "$@" -M
+  fi
+  stat=$?
+
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+    exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  if test -f "$tmpdepfile"; then
+    # Each line is of the form `foo.o: dependent.h'.
+    # Do two passes, one to just change these to
+    # `$object: dependent.h' and one to simply `dependent.h:'.
+    sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+    # That's a tab and a space in the [].
+    sed -e 's,^.*\.[a-z]*:[     ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+icc)
+  # Intel's C compiler understands `-MD -MF file'.  However on
+  #    icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+  # ICC 7.0 will fill foo.d with something like
+  #    foo.o: sub/foo.c
+  #    foo.o: sub/foo.h
+  # which is wrong.  We want:
+  #    sub/foo.o: sub/foo.c
+  #    sub/foo.o: sub/foo.h
+  #    sub/foo.c:
+  #    sub/foo.h:
+  # ICC 7.1 will output
+  #    foo.o: sub/foo.c sub/foo.h
+  # and will wrap long lines using \ :
+  #    foo.o: sub/foo.c ... \
+  #     sub/foo.h ... \
+  #     ...
+
+  "$@" -MD -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each line is of the form `foo.o: dependent.h',
+  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+  # Do two passes, one to just change these to
+  # `$object: dependent.h' and one to simply `dependent.h:'.
+  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
+    sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp2)
+  # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+  # compilers, which have integrated preprocessors.  The correct option
+  # to use with these is +Maked; it writes dependencies to a file named
+  # 'foo.d', which lands next to the object file, wherever that
+  # happens to be.
+  # Much of this is similar to the tru64 case; see comments there.
+  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+  test "x$dir" = "x$object" && dir=
+  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+  if test "$libtool" = yes; then
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir.libs/$base.d
+    "$@" -Wc,+Maked
+  else
+    tmpdepfile1=$dir$base.d
+    tmpdepfile2=$dir$base.d
+    "$@" +Maked
+  fi
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+     rm -f "$tmpdepfile1" "$tmpdepfile2"
+     exit $stat
+  fi
+
+  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+  do
+    test -f "$tmpdepfile" && break
+  done
+  if test -f "$tmpdepfile"; then
+    sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
+    # Add `dependent.h:' lines.
+    sed -ne '2,${
+              s/^ *//
+              s/ \\*$//
+              s/$/:/
+              p
+            }' "$tmpdepfile" >> "$depfile"
+  else
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile" "$tmpdepfile2"
+  ;;
+
+tru64)
+   # The Tru64 compiler uses -MD to generate dependencies as a side
+   # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+   # dependencies in `foo.d' instead, so we check for that too.
+   # Subdirectories are respected.
+   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+   test "x$dir" = "x$object" && dir=
+   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+   if test "$libtool" = yes; then
+      # With Tru64 cc, shared objects can also be used to make a
+      # static library.  This mechanism is used in libtool 1.4 series to
+      # handle both shared and static libraries in a single compilation.
+      # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
+      #
+      # With libtool 1.5 this exception was removed, and libtool now
+      # generates 2 separate objects for the 2 libraries.  These two
+      # compilations output dependencies in $dir.libs/$base.o.d and
+      # in $dir$base.o.d.  We have to check for both files, because
+      # one of the two compilations can be disabled.  We should prefer
+      # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+      # automatically cleaned when .libs/ is deleted, while ignoring
+      # the former would cause a distcleancheck panic.
+      tmpdepfile1=$dir.libs/$base.lo.d   # libtool 1.4
+      tmpdepfile2=$dir$base.o.d          # libtool 1.5
+      tmpdepfile3=$dir.libs/$base.o.d    # libtool 1.5
+      tmpdepfile4=$dir.libs/$base.d      # Compaq CCC V6.2-504
+      "$@" -Wc,-MD
+   else
+      tmpdepfile1=$dir$base.o.d
+      tmpdepfile2=$dir$base.d
+      tmpdepfile3=$dir$base.d
+      tmpdepfile4=$dir$base.d
+      "$@" -MD
+   fi
+
+   stat=$?
+   if test $stat -eq 0; then :
+   else
+      rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+      exit $stat
+   fi
+
+   for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+   do
+     test -f "$tmpdepfile" && break
+   done
+   if test -f "$tmpdepfile"; then
+      sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+      # That's a tab and a space in the [].
+      sed -e 's,^.*\.[a-z]*:[   ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+   else
+      echo "#dummy" > "$depfile"
+   fi
+   rm -f "$tmpdepfile"
+   ;;
+
+msvc7)
+  if test "$libtool" = yes; then
+    showIncludes=-Wc,-showIncludes
+  else
+    showIncludes=-showIncludes
+  fi
+  "$@" $showIncludes > "$tmpdepfile"
+  stat=$?
+  grep -v '^Note: including file: ' "$tmpdepfile"
+  if test "$stat" = 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  # The first sed program below extracts the file names and escapes
+  # backslashes for cygpath.  The second sed program outputs the file
+  # name when reading, but also accumulates all include files in the
+  # hold buffer in order to output them again at the end.  This only
+  # works with sed implementations that can handle large buffers.
+  sed < "$tmpdepfile" -n '
+/^Note: including file:  *\(.*\)/ {
+  s//\1/
+  s/\\/\\\\/g
+  p
+}' | $cygpath_u | sort -u | sed -n '
+s/ /\\ /g
+s/\(.*\)/      \1 \\/p
+s/.\(.*\) \\/\1:/
+H
+$ {
+  s/.*/        /
+  G
+  p
+}' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvc7msys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+#nosideeffect)
+  # This comment above is used by automake to tell side-effect
+  # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout, regardless of -o.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove `-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  test -z "$dashmflag" && dashmflag=-M
+  # Require at least two characters before searching for `:'
+  # in the target name.  This is to cope with DOS-style filenames:
+  # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+  "$@" $dashmflag |
+    sed 's:^[  ]*[^: ][^:][^:]*\:[    ]*:'"$object"'\: :' > "$tmpdepfile"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+dashXmstdout)
+  # This case only exists to satisfy depend.m4.  It is never actually
+  # run, as this mode is specially recognized in the preamble.
+  exit 1
+  ;;
+
+makedepend)
+  "$@" || exit $?
+  # Remove any Libtool call
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+  # X makedepend
+  shift
+  cleared=no eat=no
+  for arg
+  do
+    case $cleared in
+    no)
+      set ""; shift
+      cleared=yes ;;
+    esac
+    if test $eat = yes; then
+      eat=no
+      continue
+    fi
+    case "$arg" in
+    -D*|-I*)
+      set fnord "$@" "$arg"; shift ;;
+    # Strip any option that makedepend may not understand.  Remove
+    # the object too, otherwise makedepend will parse it as a source file.
+    -arch)
+      eat=yes ;;
+    -*|$object)
+      ;;
+    *)
+      set fnord "$@" "$arg"; shift ;;
+    esac
+  done
+  obj_suffix=`echo "$object" | sed 's/^.*\././'`
+  touch "$tmpdepfile"
+  ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+  rm -f "$depfile"
+  # makedepend may prepend the VPATH from the source file name to the object.
+  # No need to regex-escape $object, excess matching of '.' is harmless.
+  sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
+  sed '1,2d' "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile" "$tmpdepfile".bak
+  ;;
+
+cpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove `-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  "$@" -E |
+    sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+       -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+    sed '$ s: \\$::' > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  cat < "$tmpdepfile" >> "$depfile"
+  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvisualcpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test "X$1" != 'X--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  IFS=" "
+  for arg
+  do
+    case "$arg" in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+       set fnord "$@"
+       shift
+       shift
+       ;;
+    *)
+       set fnord "$@" "$arg"
+       shift
+       shift
+       ;;
+    esac
+  done
+  "$@" -E 2>/dev/null |
+  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::     \1 \\:p' >> "$depfile"
+  echo "       " >> "$depfile"
+  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvcmsys)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+none)
+  exec "$@"
+  ;;
+
+*)
+  echo "Unknown depmode $depmode" 1>&2
+  exit 1
+  ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/deviceinfo/deviceinfo.c b/deviceinfo/deviceinfo.c
new file mode 100644 (file)
index 0000000..8c3af93
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "gattrib.h"
+#include "attio.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "log.h"
+#include "deviceinfo.h"
+
+struct deviceinfo {
+       struct btd_device       *dev;           /* Device reference */
+       GAttrib                 *attrib;        /* GATT connection */
+       guint                   attioid;        /* Att watcher id */
+       struct att_range        *svc_range;     /* DeviceInfo range */
+       GSList                  *chars;         /* Characteristics */
+};
+
+static GSList *servers = NULL;
+
+struct characteristic {
+       struct gatt_char        attr;   /* Characteristic */
+       struct deviceinfo       *d;     /* deviceinfo where the char belongs */
+};
+
+static void deviceinfo_free(gpointer user_data)
+{
+       struct deviceinfo *d = user_data;
+
+       if (d->attioid > 0)
+               btd_device_remove_attio_callback(d->dev, d->attioid);
+
+       if (d->attrib != NULL)
+               g_attrib_unref(d->attrib);
+
+       g_slist_free_full(d->chars, g_free);
+
+       btd_device_unref(d->dev);
+       g_free(d->svc_range);
+       g_free(d);
+}
+
+static gint cmp_device(gconstpointer a, gconstpointer b)
+{
+       const struct deviceinfo *d = a;
+       const struct btd_device *dev = b;
+
+       if (dev == d->dev)
+               return 0;
+
+       return -1;
+}
+
+static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct characteristic *ch = user_data;
+       uint8_t value[ATT_MAX_MTU];
+       int vlen;
+
+       if (status != 0) {
+               error("Error reading PNP_ID value: %s", att_ecode2str(status));
+               return;
+       }
+
+       if (!dec_read_resp(pdu, len, value, &vlen)) {
+               error("Error reading PNP_ID: Protocol error");
+               return;
+       }
+
+       if (vlen < 7) {
+               error("Error reading PNP_ID: Invalid pdu length received");
+               return;
+       }
+
+       device_set_pnpid(ch->d->dev, value[0], att_get_u16(&value[1]),
+                               att_get_u16(&value[3]), att_get_u16(&value[5]));
+}
+
+static void process_deviceinfo_char(struct characteristic *ch)
+{
+       if (g_strcmp0(ch->attr.uuid, PNPID_UUID) == 0)
+               gatt_read_char(ch->d->attrib, ch->attr.value_handle, 0,
+                                                       read_pnpid_cb, ch);
+}
+
+static void configure_deviceinfo_cb(GSList *characteristics, guint8 status,
+                                                       gpointer user_data)
+{
+       struct deviceinfo *d = user_data;
+       GSList *l;
+
+       if (status != 0) {
+               error("Discover deviceinfo characteristics: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       for (l = characteristics; l; l = l->next) {
+               struct gatt_char *c = l->data;
+               struct characteristic *ch;
+
+               ch = g_new0(struct characteristic, 1);
+               ch->attr.handle = c->handle;
+               ch->attr.properties = c->properties;
+               ch->attr.value_handle = c->value_handle;
+               memcpy(ch->attr.uuid, c->uuid, MAX_LEN_UUID_STR + 1);
+               ch->d = d;
+
+               d->chars = g_slist_append(d->chars, ch);
+
+               process_deviceinfo_char(ch);
+       }
+}
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+       struct deviceinfo *d = user_data;
+
+       d->attrib = g_attrib_ref(attrib);
+
+       gatt_discover_char(d->attrib, d->svc_range->start, d->svc_range->end,
+                                       NULL, configure_deviceinfo_cb, d);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+       struct deviceinfo *d = user_data;
+
+       g_attrib_unref(d->attrib);
+       d->attrib = NULL;
+}
+
+int deviceinfo_register(struct btd_device *device, struct gatt_primary *prim)
+{
+       struct deviceinfo *d;
+
+       d = g_new0(struct deviceinfo, 1);
+       d->dev = btd_device_ref(device);
+       d->svc_range = g_new0(struct att_range, 1);
+       d->svc_range->start = prim->range.start;
+       d->svc_range->end = prim->range.end;
+
+       servers = g_slist_prepend(servers, d);
+
+       d->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+                                               attio_disconnected_cb, d);
+       return 0;
+}
+
+void deviceinfo_unregister(struct btd_device *device)
+{
+       struct deviceinfo *d;
+       GSList *l;
+
+       l = g_slist_find_custom(servers, device, cmp_device);
+       if (l == NULL)
+               return;
+
+       d = l->data;
+       servers = g_slist_remove(servers, d);
+
+       deviceinfo_free(d);
+}
diff --git a/deviceinfo/deviceinfo.h b/deviceinfo/deviceinfo.h
new file mode 100644 (file)
index 0000000..7a804a5
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int deviceinfo_register(struct btd_device *device, struct gatt_primary *prim);
+void deviceinfo_unregister(struct btd_device *device);
diff --git a/deviceinfo/main.c b/deviceinfo/main.c
new file mode 100644 (file)
index 0000000..82ecc82
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include "plugin.h"
+#include "manager.h"
+#include "hcid.h"
+#include "log.h"
+
+static int deviceinfo_init(void)
+{
+       if (!main_opts.gatt_enabled) {
+               error("DIS cannot start: GATT is disabled");
+               return -ENOTSUP;
+       }
+
+       return deviceinfo_manager_init();
+}
+
+static void deviceinfo_exit(void)
+{
+       deviceinfo_manager_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(deviceinfo, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                                       deviceinfo_init, deviceinfo_exit)
diff --git a/deviceinfo/manager.c b/deviceinfo/manager.c
new file mode 100644 (file)
index 0000000..1d59918
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <glib.h>
+#include <errno.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "deviceinfo.h"
+#include "manager.h"
+
+static gint primary_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct gatt_primary *prim = a;
+       const char *uuid = b;
+
+       return g_strcmp0(prim->uuid, uuid);
+}
+
+static int deviceinfo_driver_probe(struct btd_device *device, GSList *uuids)
+{
+       struct gatt_primary *prim;
+       GSList *primaries, *l;
+
+       primaries = btd_device_get_primaries(device);
+
+       l = g_slist_find_custom(primaries, DEVICE_INFORMATION_UUID,
+                                                       primary_uuid_cmp);
+       if (l == NULL)
+               return -EINVAL;
+
+       prim = l->data;
+
+       return deviceinfo_register(device, prim);
+}
+
+static void deviceinfo_driver_remove(struct btd_device *device)
+{
+       deviceinfo_unregister(device);
+}
+
+static struct btd_device_driver deviceinfo_device_driver = {
+       .name   = "deviceinfo-driver",
+       .uuids  = BTD_UUIDS(DEVICE_INFORMATION_UUID),
+       .probe  = deviceinfo_driver_probe,
+       .remove = deviceinfo_driver_remove
+};
+
+int deviceinfo_manager_init(void)
+{
+       return btd_register_device_driver(&deviceinfo_device_driver);
+}
+
+void deviceinfo_manager_exit(void)
+{
+       btd_unregister_device_driver(&deviceinfo_device_driver);
+}
diff --git a/deviceinfo/manager.h b/deviceinfo/manager.h
new file mode 100644 (file)
index 0000000..0f742ca
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int deviceinfo_manager_init(void);
+void deviceinfo_manager_exit(void);
diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
new file mode 100644 (file)
index 0000000..dccb6bf
--- /dev/null
@@ -0,0 +1,287 @@
+BlueZ D-Bus Adapter API description
+***********************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2006  Johan Hedberg <johan.hedberg@nokia.com>
+Copyright (C) 2005-2006  Claudio Takahasi <claudio.takahasi@indt.org.br>
+Copyright (C) 2006-2007  Luiz von Dentz <luiz.dentz@indt.org.br>
+
+
+Adapter hierarchy
+=================
+
+Service                org.bluez
+Interface      org.bluez.Adapter
+Object path    [variable prefix]/{hci0,hci1,...}
+
+Methods                dict GetProperties()
+
+                       Returns all properties for the adapter. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.bluez.Error.NotReady
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed a read-write are changeable.
+                       On success this will emit a PropertyChanged signal.
+
+                       Possible Errors: org.bluez.Error.InvalidArguments
+
+               void RequestSession()
+
+                       This method requests a client session that provides
+                       operational Bluetooth. A possible mode change must be
+                       confirmed by the user via the agent.
+
+                       Clients may request multiple sessions. All sessions
+                       are released when adapter's mode is changed to off
+                       state.
+
+                       Possible Errors: org.bluez.Error.Rejected
+
+               void ReleaseSession()
+
+                       Release a previously requested session. It sets
+                       adapter to the mode in use on the moment of session
+                       request.
+
+                       SetProperty method call changes adapter's mode
+                       persistently, such that session release will not
+                       modify it.
+
+                       Possible Errors: org.bluez.Error.DoesNotExist
+
+               void StartDiscovery()
+
+                       This method starts the device discovery session. This
+                       includes an inquiry procedure and remote device name
+                       resolving. Use StopDiscovery to release the sessions
+                       acquired.
+
+                       This process will start emitting DeviceFound and
+                       PropertyChanged "Discovering" signals.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                        org.bluez.Error.Failed
+
+               void StopDiscovery()
+
+                       This method will cancel any previous StartDiscovery
+                       transaction.
+
+                       Note that a discovery procedure is shared between all
+                       discovery sessions thus calling StopDiscovery will only
+                       release a single session.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                        org.bluez.Error.Failed
+                                        org.bluez.Error.NotAuthorized
+
+               object FindDevice(string address)
+
+                       Returns the object path of device for given address.
+                       The device object needs to be first created via
+                       CreateDevice or CreatePairedDevice.
+
+                       Possible Errors: org.bluez.Error.DoesNotExist
+                                        org.bluez.Error.InvalidArguments
+
+               array{object} ListDevices() {deprecated}
+
+                       Returns list of device object paths.
+                       This method is deprecated, instead use the Devices
+                       Property to get the list of devices object paths.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.Failed
+                                        org.bluez.Error.OutOfMemory
+
+               object CreateDevice(string address)
+
+                       Creates a new object path for a remote device. This
+                       method will connect to the remote device and retrieve
+                       all SDP records.
+
+                       If the object for the remote device already exists
+                       this method will fail.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.Failed
+
+               object CreatePairedDevice(string address, object agent,
+                                                       string capability)
+
+                       Creates a new object path for a remote device. This
+                       method will connect to the remote device and retrieve
+                       all SDP records and then initiate the pairing.
+
+                       If previously CreateDevice was used successfully,
+                       this method will only initiate the pairing.
+
+                       Compared to CreateDevice this method will fail if
+                       the pairing already exists, but not if the object
+                       path already has been created. This allows applications
+                       to use CreateDevice first and the if needed use
+                       CreatePairedDevice to initiate pairing.
+
+                       The agent object path is assumed to reside within the
+                       process (D-Bus connection instance) that calls this
+                       method. No separate registration procedure is needed
+                       for it and it gets automatically released once the
+                       pairing operation is complete.
+
+                       The capability parameter is the same as for the
+                       RegisterAgent method.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.Failed
+
+               void CancelDeviceCreation(string address)
+
+                       Aborts either a CreateDevice call or a
+                       CreatePairedDevice call.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotInProgress
+
+               void RemoveDevice(object device)
+
+                       This removes the remote device object at the given
+                       path. It will remove also the pairing information.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.Failed
+
+               void RegisterAgent(object agent, string capability)
+
+                       This registers the adapter wide agent.
+
+                       The object path defines the path of the agent
+                       that will be called when user input is needed.
+
+                       If an application disconnects from the bus all
+                       of its registered agents will be removed.
+
+                       The capability parameter can have the values
+                       "DisplayOnly", "DisplayYesNo", "KeyboardOnly",
+                       "NoInputNoOutput" and "KeyboardDisplay" which reflects
+                       the input and output capabilities of the agent. If an
+                       empty string is used it will fallback to
+                       "DisplayYesNo".
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.AlreadyExists
+
+               void UnregisterAgent(object agent)
+
+                       This unregisters the agent that has been previously
+                       registered. The object path parameter must match the
+                       same value that has been used on registration.
+
+                       Possible errors: org.bluez.Error.DoesNotExist
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+               DeviceFound(string address, dict values)
+
+                       This signal will be sent every time an inquiry result
+                       has been found by the service daemon. In general they
+                       only appear during a device discovery.
+
+                       The dictionary can contain basically the same values
+                       that are returned by the GetProperties method
+                       from the org.bluez.Device interface. In addition there
+                       can be values for the RSSI, the TX power level and
+                       Broadcaster role.
+
+               DeviceDisappeared(string address)
+
+                       This signal will be sent when an inquiry session for
+                       a periodic discovery finishes and previously found
+                       devices are no longer in range or visible.
+
+               DeviceCreated(object device)
+
+                       Parameter is object path of created device.
+
+               DeviceRemoved(object device)
+
+                       Parameter is object path of removed device.
+
+Properties     string Address [readonly]
+
+                       The Bluetooth device address.
+
+               string Name [readwrite]
+
+                       The Bluetooth friendly name. This value can be
+                       changed and a PropertyChanged signal will be emitted.
+
+               uint32 Class [readonly]
+
+                       The Bluetooth class of device.
+
+               boolean Powered [readwrite]
+
+                       Switch an adapter on or off. This will also set the
+                       appropriate connectable state.
+
+               boolean Discoverable [readwrite]
+
+                       Switch an adapter to discoverable or non-discoverable
+                       to either make it visible or hide it. This is a global
+                       setting and should only be used by the settings
+                       application.
+
+                       If the DiscoverableTimeout is set to a non-zero
+                       value then the system will set this value back to
+                       false after the timer expired.
+
+                       In case the adapter is switched off, setting this
+                       value will fail.
+
+                       When changing the Powered property the new state of
+                       this property will be updated via a PropertyChanged
+                       signal.
+
+               boolean Pairable [readwrite]
+
+                       Switch an adapter to pairable or non-pairable. This is
+                       a global setting and should only be used by the
+                       settings application.
+
+                       Note that this property only affects incoming pairing
+                       requests.
+
+               uint32 PairableTimeout [readwrite]
+
+                       The pairable timeout in seconds. A value of zero
+                       means that the timeout is disabled and it will stay in
+                       pairable mode forever.
+
+               uint32 DiscoverableTimeout [readwrite]
+
+                       The discoverable timeout in seconds. A value of zero
+                       means that the timeout is disabled and it will stay in
+                       discoverable/limited mode forever.
+
+                       The default value for the discoverable timeout should
+                       be 180 seconds (3 minutes).
+
+               boolean Discovering [readonly]
+
+                       Indicates that a device discovery procedure is active.
+
+               array{object} Devices [readonly]
+
+                       List of device object paths.
+
+               array{string} UUIDs [readonly]
+
+                       List of 128-bit UUIDs that represents the available
+                       local services.
diff --git a/doc/agent-api.txt b/doc/agent-api.txt
new file mode 100644 (file)
index 0000000..5c8d4d2
--- /dev/null
@@ -0,0 +1,123 @@
+BlueZ D-Bus Agent API description
+**********************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2006  Johan Hedberg <johan.hedberg@nokia.com>
+
+
+Agent hierarchy
+===============
+
+Service                unique name
+Interface      org.bluez.Agent
+Object path    freely definable
+
+Methods                void Release()
+
+                       This method gets called when the service daemon
+                       unregisters the agent. An agent can use it to do
+                       cleanup tasks. There is no need to unregister the
+                       agent, because when this method gets called it has
+                       already been unregistered.
+
+               string RequestPinCode(object device)
+
+                       This method gets called when the service daemon
+                       needs to get the passkey for an authentication.
+
+                       The return value should be a string of 1-16 characters
+                       length. The string can be alphanumeric.
+
+                       Possible errors: org.bluez.Error.Rejected
+                                        org.bluez.Error.Canceled
+
+               uint32 RequestPasskey(object device)
+
+                       This method gets called when the service daemon
+                       needs to get the passkey for an authentication.
+
+                       The return value should be a numeric value
+                       between 0-999999.
+
+                       Possible errors: org.bluez.Error.Rejected
+                                        org.bluez.Error.Canceled
+
+               void DisplayPasskey(object device, uint32 passkey, uint8 entered)
+
+                       This method gets called when the service daemon
+                       needs to display a passkey for an authentication.
+
+                       The entered parameter indicates the number of already
+                       typed keys on the remote side.
+
+                       An empty reply should be returned. When the passkey
+                       needs no longer to be displayed, the Cancel method
+                       of the agent will be called.
+
+                       During the pairing process this method might be
+                       called multiple times to update the entered value.
+
+                       Note that the passkey will always be a 6-digit number,
+                       so the display should be zero-padded at the start if
+                       the value contains less than 6 digits.
+
+               void DisplayPinCode(object device, string pincode)
+
+                       This method gets called when the service daemon
+                       needs to display a pincode for an authentication.
+
+                       An empty reply should be returned. When the pincode
+                       needs no longer to be displayed, the Cancel method
+                       of the agent will be called.
+
+                       If this method is not implemented the RequestPinCode
+                       method will be used instead.
+
+                       This is used during the pairing process of keyboards
+                       that don't support Bluetooth 2.1 Secure Simple Pairing,
+                       in contrast to DisplayPasskey which is used for those
+                       that do.
+
+                       This method will only ever be called once since
+                       older keyboards do not support typing notification.
+
+                       Note that the PIN will always be a 6-digit number,
+                       zero-padded to 6 digits. This is for harmony with
+                       the later specification.
+
+               void RequestConfirmation(object device, uint32 passkey)
+
+                       This method gets called when the service daemon
+                       needs to confirm a passkey for an authentication.
+
+                       To confirm the value it should return an empty reply
+                       or an error in case the passkey is invalid.
+
+                       Note that the passkey will always be a 6-digit number,
+                       so the display should be zero-padded at the start if
+                       the value contains less than 6 digits.
+
+                       Possible errors: org.bluez.Error.Rejected
+                                        org.bluez.Error.Canceled
+
+               void Authorize(object device, string uuid)
+
+                       This method gets called when the service daemon
+                       needs to authorize a connection/service request.
+
+                       Possible errors: org.bluez.Error.Rejected
+                                        org.bluez.Error.Canceled
+
+               void ConfirmModeChange(string mode)
+
+                       This method gets called if a mode change is requested
+                       that needs to be confirmed by the user. An example
+                       would be leaving flight mode.
+
+                       Possible errors: org.bluez.Error.Rejected
+                                        org.bluez.Error.Canceled
+
+               void Cancel()
+
+                       This method gets called to indicate that the agent
+                       request failed before a reply was returned.
diff --git a/doc/assigned-numbers.txt b/doc/assigned-numbers.txt
new file mode 100644 (file)
index 0000000..cda934c
--- /dev/null
@@ -0,0 +1,23 @@
+RFCOMM Channels
+===============
+
+Since there are a limited amount of possible RFCOMM channels (1-31)
+they've been pre-allocated for currently known profiles in order to
+avoid conflicts.
+
+Profile                Channel
+-----------------------
+DUN            1
+HFP HF         7
+OPP            9
+FTP            10
+BIP            11
+HSP AG         12
+HFP AG         13
+SYNCH (IrMC)   14
+PBAP           15
+MAP            16
+SyncEvolution  19
+PC/Ovi Suite   24
+SyncML Client  25
+SyncML Server  26
diff --git a/doc/attribute-api.txt b/doc/attribute-api.txt
new file mode 100644 (file)
index 0000000..98d7f30
--- /dev/null
@@ -0,0 +1,164 @@
+BlueZ D-Bus Attribute API description
+*************************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+
+Service details
+---------------
+
+One service object path for every remote SDP record or service in the
+attribute database. One service object path for every local SDP record
+or service from attribute database.
+
+Local services are children of the adapter object path. Remote services
+are children of the remote device object path. This doesn't solve the
+problem where local attributes can have different instances based on
+the remote device.
+
+In general the idea is to also represent SDP records as services so that
+new style application can just use the service interfaces to retrieve the
+needed information. That way the usage of SDP and GATT would be mostly
+fully transparent and a differentiation becomes unimportant in the future.
+
+A service consists of some generic service information and a set of
+characteristics. All characteristic are presented as object path as well.
+
+
+Local Service hierarchy
+=======================
+
+Service                org.bluez
+Interface      org.bluez.Service
+               org.bluez.Characteristic
+Object path    [prefix]/{hci0}/{service0, service1, ...}
+
+Methods
+
+Properties
+
+
+Device Service hierarchy
+========================
+
+Service                org.bluez
+Interface      org.bluez.Characteristic
+Object path    [prefix]/{hci0}/{device0}/{service0, service1, ...}
+               [prefix]/{hci0}/{device1}/{service0, service1, ...}
+
+Methods        dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       Properties section for the available properties.
+
+               array{object} DiscoverCharacteristics()
+
+                       Discover all characteristics that belongs in this service.
+                       When it returns all the characteristics paths will be
+                       already registered. It will return the characteristics paths
+                       as soon as they are discovered. After that it will try to
+                       read all values.
+
+               RegisterCharacteristicsWatcher(object agent)
+
+                       Register a watcher to monitor characteristic changes.
+
+                       A watcher will be registered for this service and will
+                       notify about any changed characteristics in the service.
+                       This also notifies about any included characteristics.
+
+               UnregisterCharacteristicsWatcher(object agent)
+
+                       Unregister a watcher.
+
+Properties     string Name (mandatory) [readonly]
+
+                       General name of service
+
+               string Description (optional) [readonly]
+
+                       Description of service
+
+               string UUID (mandatory) [readonly]
+
+                       UUID of service. Service class value for SDP and GATT
+                       UUID for attribute based services.
+
+               array{object} Characteristics [readonly]
+
+                       This list contains the characteristics owned by this
+                       specific service and other characteristics from service
+                       includes. That way no complicated service includes array
+                       is needed.
+
+
+Device Characteristic hierarchy
+===============================
+
+Service                org.bluez
+Interface      org.bluez.Characteristic
+Object path    [prefix]/{hci0}/{device0}/{service0}/{characteristic0,...}
+               [prefix]/{hci0}/{device0}/{service1}/{characteristic0,...}
+
+Methods                dict GetProperties()
+
+                       Returns all properties for the characteristic. See the
+                       properties section for available properties.
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       read-write properties can be changed. On success
+                       this will emit a PropertyChanged signal.
+
+                       Possible Errors: org.bluez.Error.InvalidArguments
+
+Properties     string UUID [readonly]
+
+                       UUID128 of this characteristic.
+
+               string Name [readonly]
+
+                       Optional field containing a friendly name for the
+                       Characteristic UUID.
+
+               string Description [readonly]
+
+                       Textual optional characteristic descriptor describing
+                       the Characteristic Value.
+
+               struct Format [readonly]
+
+                       Optional Characteristic descriptor which defines the
+                       format of the Characteristic Value. For numeric
+                       values, the actual value can be value * 10^Exponent.
+                       NameSpace and Description are defined on the Assigned
+                       Number Specification.
+
+                         uint8  | Format: format of the value
+                         uint8  | Exponent: Field to determine how the value is
+                                | further formatted.
+                         uint16 | Unit: unit of the characteristic
+                         uint8  | NameSpace: Name space of description.
+                         uint16 | Description: Description of the characteristic defined
+                                | in a high layer profile.
+
+               array{byte} Value [readwrite]
+
+                       Raw value of the Characteristic Value attribute.
+
+               string Representation (of the binary Value) [readonly]
+
+                       Friendly representation of the Characteristic Value
+                       based on the format attribute.
+
+
+Characteristic Watcher hierarchy
+===============================
+
+Service                unique name
+Interface      org.bluez.Watcher
+Object path    freely definable
+
+Methods                void ValueChanged(object characteristic, array{byte})
+
+                       New raw value of the Characteristic Value attribute.
diff --git a/doc/audio-api.txt b/doc/audio-api.txt
new file mode 100644 (file)
index 0000000..02291fd
--- /dev/null
@@ -0,0 +1,458 @@
+BlueZ D-Bus Audio API description
+*********************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2007  Johan Hedberg <johan.hedberg@nokia.com>
+Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+
+Audio hierarchy
+===============
+
+Service                org.bluez
+Interface      org.bluez.Audio
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This is a generic audio interface that abstracts the different audio profiles.
+
+Methods                void Connect()
+
+                       Connect all supported audio profiles on the device.
+
+               void Disconnect()
+
+                       Disconnect all audio profiles on the device
+
+               dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       properties section for available properties.
+
+Signals                void PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string State
+
+                       Possible values: "disconnected", "connecting",
+                       "connected"
+
+                       "disconnected" -> "connecting"
+                               Either an incoming or outgoing connection
+                               attempt ongoing.
+
+                       "connecting" -> "disconnected"
+                               Connection attempt failed
+
+                       "connecting" -> "connected"
+                               Successfully connected
+
+                       "connected" -> "disconnected"
+                               Disconnected from the remote device
+
+Headset hierarchy
+=================
+
+Service                org.bluez
+Interface      org.bluez.Headset
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods                void Connect()
+
+                       Connect to the HSP/HFP service on the remote device.
+
+               void Disconnect()
+
+                       Disconnect from the HSP/HFP service on the remote
+                       device.
+
+               boolean IsConnected() {deprecated}
+
+                       Returns TRUE if there is a active connection to the
+                       HSP/HFP connection on the remote device.
+
+               void IndicateCall()
+
+                       Indicate an incoming call on the headset
+                       connected to the stream. Will continue to
+                       ring the headset about every 3 seconds.
+
+               void CancelCall()
+
+                       Cancel the incoming call indication.
+
+               void Play() {deprecated}
+
+                       Open the audio connection to the headset.
+
+               void Stop()
+
+                       Close the audio connection.
+
+               boolean IsPlaying() {deprecated}
+
+                       Returns true if an audio connection to the headset
+                       is active.
+
+               uint16 GetSpeakerGain() {deprecated}
+
+                       Returns the current speaker gain if available,
+                       otherwise returns the error NotAvailable.
+
+               uint16 GetMicrophoneGain() {deprecated}
+
+                       Returns the current microphone gain if available,
+                       otherwise returns the error NotAvailable.
+
+               void SetSpeakerGain(uint16 gain) {deprecated}
+
+                       Changes the current speaker gain if possible.
+
+               void SetMicrophoneGain(uint16 gain) {deprecated}
+
+                       Changes the current speaker gain if possible.
+
+               dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.bluez.Error.InvalidArguments
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed a read-write are changeable.
+                       On success this will emit a PropertyChanged signal.
+
+                       Possible Errors: org.bluez.Error.DoesNotExist
+                                        org.bluez.Error.InvalidArguments
+
+Signals                void AnswerRequested()
+
+                       Sent when the answer button is pressed on the headset
+
+               void Connected() {deprecated}
+
+                       Sent when the device has been connected to.
+
+               void Disconnected() {deprecated}
+
+                       Sent when the device has been disconnected from.
+
+               void Stopped() {deprecated}
+
+                       Sent when the audio connection is closed
+
+               void Playing() {deprecated}
+
+                       Sent when the audio connection is opened
+
+               void SpeakerGainChanged(uint16 gain) {deprecated}
+
+                       The speaker gain changed.
+
+               void MicrophoneGainChanged(uint16 gain) {deprecated}
+
+                       The microphone gain changed.
+
+               PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+properties     string State [readonly]
+
+                       Possible values: "disconnected", "connecting",
+                       "connected", "playing"
+
+                       "disconnected" -> "connecting"
+                               Either an incoming or outgoing connection
+                               attempt ongoing.
+
+                       "connecting" -> "disconnected"
+                               Connection attempt failed
+
+                       "connecting" -> "connected"
+                               Successfully connected
+
+                       "connected" -> "playing"
+                               SCO audio connection successfully opened
+
+                       "playing" -> "connected"
+                               SCO audio connection closed
+
+                       "connected" -> "disconnected"
+                       "playing" -> "disconnected"
+                               Disconnected from the remote device
+
+               boolean Connected [readonly]
+
+                       Indicates if there is a active connection to the
+                       HSP/HFP connection on the remote device.
+
+               boolean Playing  [readonly]
+
+                       Indicates if an audio connection to the headset
+                       is active.
+
+               uint16 SpeakerGain  [readwrite]
+
+                       The speaker gain when available.
+
+               uint16 MicrophoneGain  [readwrite]
+
+                       The speaker gain when available.
+
+
+AudioSink hierarchy
+===================
+
+Service                org.bluez
+Interface      org.bluez.AudioSink
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods                void Connect()
+
+                       Connect and setup a stream to a A2DP sink on the
+                       remote device.
+
+               void Disconnect()
+
+                       Disconnect from the remote device.
+
+               boolean IsConnected() {deprecated}
+
+                       Returns TRUE if a stream is setup to a A2DP sink on
+                       the remote device.
+
+               dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.bluez.Error.InvalidArguments
+
+Signals                void Connected() {deprecated}
+
+                       Sent when a successful connection has been made to the
+                       remote A2DP Sink
+
+               void Disconnected() {deprecated}
+
+                       Sent when the device has been disconnected from.
+
+               void Playing() {deprecated}
+
+                       Sent when a stream with remote device is started.
+
+               void Stopped() {deprecated}
+
+                       Sent when a stream with remote device is suspended.
+
+               PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+properties     string State [readonly]
+
+                       Possible values: "disconnected", "connecting",
+                       "connected", "playing"
+
+                       "disconnected" -> "connecting"
+                               Either an incoming or outgoing connection
+                               attempt ongoing.
+
+                       "connecting" -> "disconnected"
+                               Connection attempt failed
+
+                       "connecting" -> "connected"
+                               Successfully connected
+
+                       "connected" -> "playing"
+                               Audio stream active
+
+                       "playing" -> "connected"
+                               Audio stream suspended
+
+                       "connected" -> "disconnected"
+                       "playing" -> "disconnected"
+                               Disconnected from the remote device
+
+               boolean Connected [readonly]
+
+                       Indicates if a stream is setup to a A2DP sink on
+                       the remote device.
+
+               boolean Playing  [readonly]
+
+                       Indicates if a stream is active to a A2DP sink on
+                       the remote device.
+
+AudioSource hierarchy
+=====================
+
+Service                org.bluez
+Interface      org.bluez.AudioSource
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods                void Connect()
+
+                       Connect and setup a stream to a A2DP source on the
+                       remote device.
+
+               void Disconnect()
+
+                       Disconnect from the remote device.
+
+               dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.bluez.Error.InvalidArguments
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+properties     string State [readonly]
+
+                       Possible values: "disconnected", "connecting",
+                       "connected", "playing"
+
+                       "disconnected" -> "connecting"
+                               Either an incoming or outgoing connection
+                               attempt ongoing.
+
+                       "connecting" -> "disconnected"
+                               Connection attempt failed
+
+                       "connecting" -> "connected"
+                               Successfully connected
+
+                       "connected" -> "playing"
+                               Audio stream active
+
+                       "playing" -> "connected"
+                               Audio stream suspended
+
+                       "connected" -> "disconnected"
+                       "playing" -> "disconnected"
+                               Disconnected from the remote device
+
+
+HeadsetGateway hierarchy
+========================
+
+Service                org.bluez
+Interface      org.bluez.HeadsetGateway
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This interface is available for remote devices which can function in the Audio
+Gateway role of the HFP profiles.
+
+Methods                void Connect()
+
+                       Connect to the AG service on the remote device.
+
+               void Disconnect()
+
+                       Disconnect from the AG service on the remote device
+
+               void AnswerCall()
+
+                       It has to called only after Ring signal received.
+
+               void TerminateCall()
+
+                       Terminate call which is running or reject an incoming
+                       call. This has nothing with any 3-way situation incl.
+                       RaH. Just plain old PDH.
+
+               void Call(string number)
+
+                       Dial a number 'number'. No number processing is done
+                       thus if AG would reject to dial it don't blame me :)
+
+               string GetOperatorName()
+
+                       Find out the name of the currently selected network
+                       operator by AG.
+
+               void SendDTMF(string digits)
+
+                       Will send each digit in the 'digits' sequentially. Would
+                       send nothing if there is non-dtmf digit.
+
+               string GetSubscriberNumber()
+
+                       Get the voicecall subscriber number of AG
+
+               dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       properties section for available properties.
+
+Signals                void Ring(string number)
+
+                       Someone's calling from 'number'.
+                       Caller number is provided as received from AG.
+
+               void CallTerminated()
+
+                       Call failed to set up. It means that we tried to call
+                       someone or someone tried to call us but call was not
+                       accepted.
+
+               void CallStarted()
+
+                       Call set up successfully.
+
+               void CallEnded()
+
+                       Call was started and now ended. In contrast with
+                       CallTerminated where call didn't started
+
+               PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+properties     boolean Connected [readonly]
+
+                       Indicates if there is an active connection to the
+                       AG service on the remote device.
+
+               uint16 RegistrationStatus [readonly]
+
+                       Service availability indicator of AG, where:
+                       0 implies no service. No Home/Roam network available.
+                       1 implies presence of service. Home/Roam network
+                       available.
+
+               uint16 SignalStrength [readonly]
+
+                       Signal strength indicator of AG, the value ranges from
+                       0 to 5.
+
+               uint16 RoamingStatus [readonly]
+
+                       Roaming status indicator of AG, where:
+                       0 means roaming is not active
+                       1 means a roaming is active
+
+               uint16 BatteryCharge [readonly]
+
+                       Battery Charge indicator of AG, the value ranges from
+                       0 to 5.
+
+               uint16 SpeakerGain  [readonly]
+
+                       The speaker gain when available.
+
+               uint16 MicrophoneGain  [readonly]
+
+                       The speaker gain when available.
diff --git a/doc/control-api.txt b/doc/control-api.txt
new file mode 100644 (file)
index 0000000..eacfbcd
--- /dev/null
@@ -0,0 +1,44 @@
+BlueZ D-Bus Control API description
+***********************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2007-2008  David Stockwell <dstockwell@frequency-one.com>
+
+
+Control hierarchy
+=================
+
+Service                org.bluez
+Interface      org.bluez.Control
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods                boolean IsConnected() {deprecated}
+
+                       Returns True if connected, otherwise FALSE.
+
+               dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       properties section for available properties.
+
+               void VolumeUp()
+
+                       Adjust remote volume one step up
+
+               void VolumeDown()
+
+                       Adjust remote volume one step down
+
+Signals                Connected() {deprecated}
+
+                       Sent when a successful AVRCP connection has been made
+                       to the remote device.
+
+               Disconnected() {deprecated}
+
+                       Sent when the AVRCP connection to the remote device
+                       has been disconnected.
+
+Properties
+
+               boolean Connected [readonly]
diff --git a/doc/device-api.txt b/doc/device-api.txt
new file mode 100644 (file)
index 0000000..4a170e4
--- /dev/null
@@ -0,0 +1,215 @@
+BlueZ D-Bus Device API description
+**********************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2006  Johan Hedberg <johan.hedberg@nokia.com>
+Copyright (C) 2005-2006  Claudio Takahasi <claudio.takahasi@indt.org.br>
+Copyright (C) 2006-2007  Luiz von Dentz <luiz.dentz@indt.org.br>
+
+
+Device hierarchy
+================
+
+Service                org.bluez
+Interface      org.bluez.Device
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods                dict GetProperties()
+
+                       Returns all properties for the device. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.bluez.Error.DoesNotExist
+                                        org.bluez.Error.InvalidArguments
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed a read-write are changeable.
+                       On success this will emit a PropertyChanged signal.
+
+                       Possible Errors: org.bluez.Error.DoesNotExist
+                                        org.bluez.Error.InvalidArguments
+
+               dict DiscoverServices(string pattern)
+
+                       This method starts the service discovery to retrieve
+                       remote service records. The pattern parameter can
+                       be used to specify specific UUIDs. And empty string
+                       will look for the public browse group.
+
+                       The return value is a dictionary with the record
+                       handles as keys and the service record in XML format
+                       as values. The key is uint32 and the value a string
+                       for this dictionary.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                        org.bluez.Error.Failed
+                                        org.bluez.Error.InProgress
+
+               void CancelDiscovery()
+
+                       This method will cancel any previous DiscoverServices
+                       transaction.
+
+                       Possible errors: org.bluez.Error.NotReady
+                                        org.bluez.Error.Failed
+                                        org.bluez.Error.NotAuthorized
+
+               void Disconnect()
+
+                       This method disconnects a specific remote device by
+                       terminating the low-level ACL connection. The use of
+                       this method should be restricted to administrator
+                       use.
+
+                       A DisconnectRequested signal will be sent and the
+                       actual disconnection will only happen 2 seconds later.
+                       This enables upper-level applications to terminate
+                       their connections gracefully before the ACL connection
+                       is terminated.
+
+                       Possible errors: org.bluez.Error.NotConnected
+
+               array{object} ListNodes()
+
+                       Returns list of device node object paths.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.Failed
+                                        org.bluez.Error.OutOfMemory
+
+               object CreateNode(string uuid)
+
+                       Creates a persistent device node binding with a
+                       remote device. The actual support for the specified
+                       UUID depends if the device driver has support for
+                       persistent binding. At the moment only RFCOMM TTY
+                       nodes are supported.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotSupported
+
+               void RemoveNode(object node)
+
+                       Removes a persistent device node binding.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.DoesNotExist
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+               DisconnectRequested()
+
+                       This signal will be sent when a low level
+                       disconnection to a remote device has been requested.
+                       The actual disconnection will happen 2 seconds later.
+
+               NodeCreated(object node)
+
+                       Parameter is object path of created device node.
+
+               NodeRemoved(object node)
+
+                       Parameter is object path of removed device node.
+
+Properties     string Address [readonly]
+
+                       The Bluetooth device address of the remote device.
+
+               string Name [readonly]
+
+                       The Bluetooth remote name. This value can not be
+                       changed. Use the Alias property instead.
+
+               uint16 Vendor [readonly]
+
+                       Vendor unique numeric identifier.
+
+               uint16 VendorSource [readonly]
+
+                       Vendor source numeric identifier.
+
+               uint16 Product [readonly]
+
+                       Product unique numeric identifier.
+
+               uint16 Version [readonly]
+
+                       Version unique numeric identifier.
+
+               string Icon [readonly]
+
+                       Proposed icon name according to the freedesktop.org
+                       icon naming specification.
+
+               uint32 Class [readonly]
+
+                       The Bluetooth class of device of the remote device.
+
+               array{string} UUIDs [readonly]
+
+                       List of 128-bit UUIDs that represents the available
+                       remote services.
+
+               array{object} Services [readonly]
+
+                       List of characteristics based services.
+
+               boolean Paired [readonly]
+
+                       Indicates if the remote device is paired.
+
+               boolean Connected [readonly]
+
+                       Indicates if the remote device is currently connected.
+                       A PropertyChanged signal indicate changes to this
+                       status.
+
+               boolean Trusted [readwrite]
+
+                       Indicates if the remote is seen as trusted. This
+                       setting can be changed by the application.
+
+               boolean Blocked [readwrite]
+
+                       If set to true any incoming connections from the
+                       device will be immediately rejected. Any device
+                       drivers will also be removed and no new ones will
+                       be probed as long as the device is blocked.
+
+               string Alias [readwrite]
+
+                       The name alias for the remote device. The alias can
+                       be used to have a different friendly name for the
+                       remote device.
+
+                       In case no alias is set, it will return the remote
+                       device name. Setting an empty string as alias will
+                       convert it back to the remote device name.
+
+                       When resetting the alias with an empty string, the
+                       emitted PropertyChanged signal will show the remote
+                       name again.
+
+               array{object} Nodes [readonly]
+
+                       List of device node object paths.
+
+               object Adapter [readonly]
+
+                       The object path of the adapter the device belongs to.
+
+               boolean LegacyPairing [readonly]
+
+                       Set to true if the device only supports the pre-2.1
+                       pairing mechanism. This property is useful in the
+                       Adapter.DeviceFound signal to anticipate whether
+                       legacy or simple pairing will occur.
+
+                       Note that this property can exhibit false-positives
+                       in the case of Bluetooth 2.1 (or newer) devices that
+                       have disabled Extended Inquiry Response support.
diff --git a/doc/health-api.txt b/doc/health-api.txt
new file mode 100644 (file)
index 0000000..7a000cb
--- /dev/null
@@ -0,0 +1,166 @@
+BlueZ D-Bus Health API description
+**********************************
+
+       Santiago Carot-Nemesio <sancane@gmail.com>
+       José Antonio Santos-Cadenas <santoscadenas@gmail.com>
+       Elvis Pfützenreuter <epx@signove.com>
+
+Health Device Profile hierarchy
+===============================
+
+Service                org.bluez
+Interface      org.bluez.HealthManager
+Object path    /org/bluez/
+
+Methods:
+
+       object  CreateApplication(dict config)
+
+               Returns the path of the new registered application.
+
+               Dict is defined as below:
+               {
+                       "DataType": uint16, (mandatory)
+                       "Role" : ("Source" or "Sink"), (mandatory)
+                       "Description" : string, (optional)
+                       "ChannelType" : ("Reliable" or "Streaming")
+                                               (just for Sources, optional)
+               }
+
+               Application will be closed by the call or implicitly when the
+               programs leaves the bus.
+
+               Possible errors: org.bluez.Error.InvalidArguments
+
+       void    DestroyApplication(object application)
+
+               Closes the HDP application identified by the object path. Also
+               application will be closed if the process that started it leaves
+               the bus. Only the creator of the application will be able to
+               destroy it.
+
+               Possible errors: org.bluez.Error.InvalidArguments
+                               org.bluez.Error.NotFound
+                               org.bluez.Error.NotAllowed
+
+--------------------------------------------------------------------------------
+
+Service                org.bluez
+Interface      org.bluez.HealthDevice
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods:
+
+       dict GetProperties()
+
+               Returns all properties for the interface. See the properties
+               section for available properties.
+
+               Posible errors: org.bluez.Error.NotAllowed
+
+       Boolean Echo()
+
+               Sends an echo petition to the remote service. Returns True if
+               response matches with the buffer sent. If some error is detected
+               False value is returned.
+
+               Possible errors: org.bluez.Error.InvalidArguments
+                               org.bluez.Error.OutOfRange
+
+       object CreateChannel(object application, string configuration)
+
+               Creates a new data channel.
+               The configuration should indicate the channel quality of
+               service using one of this values "Reliable", "Streaming", "Any".
+
+               Returns the object path that identifies the data channel that
+               is already connected.
+
+               Possible errors: org.bluez.Error.InvalidArguments
+                               org.bluez.Error.HealthError
+
+       void DestroyChannel(object channel)
+
+               Destroys the data channel object. Only the creator of the
+               channel or the creator of the HealthApplication that received
+               the data channel will be able to destroy it.
+
+               Possible errors: org.bluez.Error.InvalidArguments
+                               org.bluez.Error.NotFound
+                               org.bluez.Error.NotAllowed
+
+Signals:
+
+       void ChannelConnected(object channel)
+
+               This signal is launched when a new data channel is created or
+               when a known data channel is reconnected.
+
+       void ChannelDeleted(object channel)
+
+               This signal is launched when a data channel is deleted.
+
+               After this signal the data channel path will not be valid and
+               its path can be reused for future data channels.
+
+       void PropertyChanged(string name, variant value)
+
+               This signal indicates a changed value of the given property.
+
+Properties:
+
+       object MainChannel [readonly]
+
+               The first reliable channel opened. It is needed by upper
+               applications in order to send specific protocol data units. The
+               first reliable can change after a reconnection.
+
+--------------------------------------------------------------------------------
+
+Service                org.bluez
+Interface      org.bluez.HealthChannel
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/chanZZZ
+
+Only the process that created the data channel or the creator of the
+HealthApplication that received it will be able to call this methods.
+
+Methods:
+
+       dict GetProperties()
+
+               Returns all properties for the interface. See the properties
+               section for available properties.
+
+               Possible errors: org.bluez.Error.NotAllowed
+
+       fd Acquire()
+
+               Returns the file descriptor for this data channel. If the data
+               channel is not connected it will also reconnect.
+
+               Possible errors: org.bluez.Error.NotConnected
+                               org.bluez.Error.NotAllowed
+
+       void Release()
+
+               Releases the fd. Application should also need to close() it.
+
+               Possible errors: org.bluez.Error.NotAcquired
+                               org.bluez.Error.NotAllowed
+
+Properties:
+
+       string Type [readonly]
+
+               The quality of service of the data channel. ("Reliable" or
+               "Streaming")
+
+       object Device [readonly]
+
+               Identifies the Remote Device that is connected with. Maps with
+               a HealthDevice object.
+
+       object Application [readonly]
+
+               Identifies the HealthApplication to which this channel is
+               related to (which indirectly defines its role and data type).
diff --git a/doc/hfp-api.txt b/doc/hfp-api.txt
new file mode 100644 (file)
index 0000000..fad89ae
--- /dev/null
@@ -0,0 +1,84 @@
+Gateway hierarchy
+========================
+
+Service                org.bluez
+Interface      org.bluez.HandsfreeGateway
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+This interface is available for remote devices which can function in the Audio
+Gateway role of the HFP profiles.  It is intended to be used with external
+telephony stacks / handlers of the HFP protocol.
+
+Methods                void Connect()
+
+                       Connect to the AG service on the remote device.
+
+               void Disconnect()
+
+                       Disconnect from the AG service on the remote device
+
+               dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       properties section for available properties.
+
+               void RegisterAgent(object path)
+
+                       The object path defines the path the of the agent
+                       that will be called when a new Handsfree connection
+                       is established.
+
+                       If an application disconnects from the bus all of its
+                       registered agents will be removed.
+
+               void UnregisterAgent(object path)
+
+                       This unregisters the agent that has been previously
+                       registered. The object path parameter must match the
+                       same value that has been used on registration.
+
+                       Possible Errors: org.bluez.Error.Failed
+                                        org.bluez.Error.InvalidArguments
+
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     string State [readonly]
+
+                       Indicates the state of the connection.  Possible
+                       values are:
+                               "disconnected"
+                               "connecting"
+                               "connected"
+                               "playing"
+
+HandsfreeAgent hierarchy
+===============
+
+Service         unique name
+Interface       org.bluez.HandsfreeAgent
+Object path     freely definable
+
+Methods                void NewConnection(filedescriptor fd, uint16 version)
+
+                       This method gets called whenever a new handsfree
+                       connection has been established.  The objectpath
+                       contains the object path of the remote device.
+
+                       The agent should only return successfully once the
+                       establishment of the service level connection (SLC)
+                       has been completed.  In the case of Handsfree this
+                       means that BRSF exchange has been performed and
+                       necessary initialization has been done.
+
+                       Possible Errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.Failed
+
+               void Release()
+
+                       This method gets called whenever the service daemon
+                       unregisters the agent or whenever the Adapter where
+                       the HandsfreeAgent registers itself is removed.
diff --git a/doc/input-api.txt b/doc/input-api.txt
new file mode 100644 (file)
index 0000000..7c3a4b2
--- /dev/null
@@ -0,0 +1,44 @@
+BlueZ D-Bus Input API description
+*********************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+
+
+Input hierarchy
+===============
+
+Service                org.bluez
+Interface      org.bluez.Input
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods                void Connect()
+
+                       Connect to the input device.
+
+                       Possible errors: org.bluez.Error.AlreadyConnected
+                                        org.bluez.Error.ConnectionAttemptFailed
+
+               void Disconnect()
+
+                       Disconnect from the input device.
+
+                       To abort a connection attempt in case of errors or
+                       timeouts in the client it is fine to call this method.
+
+                       Possible errors: org.bluez.Error.Failed
+
+               dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.bluez.Error.InvalidArguments
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean Connected [readonly]
+
+                       Indicates if the device is connected.
diff --git a/doc/manager-api.txt b/doc/manager-api.txt
new file mode 100644 (file)
index 0000000..d2c1caf
--- /dev/null
@@ -0,0 +1,74 @@
+BlueZ D-Bus Manager API description
+***********************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2006  Johan Hedberg <johan.hedberg@nokia.com>
+Copyright (C) 2005-2006  Claudio Takahasi <claudio.takahasi@indt.org.br>
+Copyright (C) 2006-2007  Luiz von Dentz <luiz.dentz@indt.org.br>
+
+
+Manager hierarchy
+=================
+
+Service                org.bluez
+Interface      org.bluez.Manager
+Object path    /
+
+Methods                dict GetProperties()
+
+                       Returns all global properties. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.bluez.Error.DoesNotExist
+                                        org.bluez.Error.InvalidArguments
+
+               object DefaultAdapter()
+
+                       Returns object path for the default adapter.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NoSuchAdapter
+
+               object FindAdapter(string pattern)
+
+                       Returns object path for the specified adapter. Valid
+                       patterns are "hci0" or "00:11:22:33:44:55".
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NoSuchAdapter
+
+               array{object} ListAdapters() {deprecated}
+
+                       Returns list of adapter object paths under /org/bluez.
+                       This method is deprecated, instead use the Adapters
+                       Property to get the list of adapter object paths.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.Failed
+                                        org.bluez.Error.OutOfMemory
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+               AdapterAdded(object adapter)
+
+                       Parameter is object path of added adapter.
+
+               AdapterRemoved(object adapter)
+
+                       Parameter is object path of removed adapter.
+
+               DefaultAdapterChanged(object adapter)
+
+                       Parameter is object path of the new default adapter.
+
+                       In case all adapters are removed this signal will not
+                       be emitted. The AdapterRemoved signal has to be used
+                       to detect that no default adapter is selected or
+                       available anymore.
+
+Properties     array{object} Adapters [readonly]
+
+                       List of adapter object paths.
diff --git a/doc/media-api.txt b/doc/media-api.txt
new file mode 100644 (file)
index 0000000..4446439
--- /dev/null
@@ -0,0 +1,351 @@
+BlueZ D-Bus Media API description
+*********************************
+
+Media hierarchy
+===============
+
+Service                org.bluez
+Interface      org.bluez.Media
+Object path    [variable prefix]/{hci0,hci1,...}
+
+Methods                void RegisterEndpoint(object endpoint, dict properties)
+
+                       Register a local end point to sender, the sender can
+                       register as many end points as it likes.
+
+                       Note: If the sender disconnects the end points are
+                       automatically unregistered.
+
+                       possible properties:
+
+                               string UUID:
+
+                                       UUID of the profile which the endpoint
+                                       is for.
+
+                               byte Codec:
+
+                                       Assigned number of codec that the
+                                       endpoint implements. The values should
+                                       match the profile specification which
+                                       is indicated by the UUID.
+
+                               array{byte} Capabilities:
+
+                                       Capabilities blob, it is used as it is
+                                       so the size and byte order must match.
+
+                       Possible Errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotSupported - emitted
+                                        when interface for the end-point is
+                                        disabled.
+
+               void UnregisterEndpoint(object endpoint)
+
+                       Unregister sender end point.
+
+               void RegisterPlayer(object player, dict properties,
+                                                               dict metadata)
+
+                       Register a media player object to sender, the sender
+                       can register as many objets as it likes.
+
+                       Note: If the sender disconnects its objects are
+                       automatically unregistered.
+
+                       Properties:
+
+                               string Equalizer:
+
+                                       Possible values: "off" or "on"
+
+                               string Repeat:
+
+                                       Possible values: "off", "singletrack",
+                                                       "alltracks" or "group"
+
+                               string Shuffle:
+
+                                       Possible values: "off", "alltracks" or
+                                                       "group"
+
+                               string Scan:
+
+                                       Possible values: "off", "alltracks" or
+                                                       "group"
+
+                               string Status:
+
+                                       Possible values: "playing", "stopped",
+                                                       "paused",
+                                                       "forward-seek",
+                                                       "reverse-seek" or
+                                                       "error"
+
+                               uint32 Position
+
+                                       Playback position in milliseconds
+
+                       Metadata:
+
+                               string Title:
+
+                                       Track title name
+
+                               string Artist:
+
+                                       Track artist name
+
+                               string Album:
+
+                                       Track album name
+
+                               string Genre:
+
+                                       Track genre name
+
+                               uint32 NumberOfTracks:
+
+                                       Number of tracks in total
+
+                               uint32 Number:
+
+                                       Track number
+
+                               uint32 Duration:
+
+                                       Track duration in milliseconds
+
+                       Possible Errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotSupported
+
+               void UnregisterPlayer(object player)
+
+                       Unregister sender media player.
+
+MediaPlayer hierarchy
+=====================
+
+Service                unique name
+Interface      org.bluez.MediaPlayer
+Object path    freely definable
+
+Methods                void SetProperty(string property, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed as read-write can be changed.
+
+                       On success this will emit a PropertyChanged signal.
+
+               void Release()
+
+                       This method gets called when the service daemon
+                       unregisters the player which can then perform
+                       cleanup tasks. There is no need to unregister the
+                       player, because when this method gets called it has
+                       already been unregistered.
+
+Signals                PropertyChanged(string setting, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+               TrackChanged(dict metadata)
+
+                       This signal indicates that current track has changed.
+                       All available metadata for the new track shall be set
+                       at once in the metadata argument. Metadata cannot be
+                       updated in parts, otherwise it will be interpreted as
+                       multiple track changes.
+
+                       Possible values:
+
+                               string Title:
+
+                                       Track title name
+
+                               string Artist:
+
+                                       Track artist name
+
+                               string Album:
+
+                                       Track album name
+
+                               string Genre:
+
+                                       Track genre name
+
+                               uint32 NumberOfTracks:
+
+                                       Number of tracks in total
+
+                               uint32 Number:
+
+                                       Track number
+
+                               uint32 Duration:
+
+                                       Track duration in milliseconds
+
+Properties     string Equalizer [readwrite]
+
+                       Possible values: "off" or "on"
+
+               string Repeat [readwrite]
+
+                       Possible values: "off", "singletrack", "alltracks" or
+                                       "group"
+
+               string Shuffle [readwrite]
+
+                       Possible values: "off", "alltracks" or "group"
+
+               string Scan [readwrite]
+
+                       Possible values: "off", "alltracks" or "group"
+
+               string Status [readonly]
+
+                       Possible status: "playing", "stopped", "paused",
+                                       "forward-seek", "reverse-seek" or
+                                       "error"
+
+               uint32 Position [readonly]
+
+                       Playback position in milliseconds. Changing the
+                       position may generate additional events that will be
+                       sent to the remote device. When position is 0 it means
+                       the track is starting and when it's greater than or
+                       equal to track's duration the track has ended. Note
+                       that even if duration is not available in metadata it's
+                       possible to signal its end by setting position to the
+                       maximum uint32 value.
+
+MediaEndpoint hierarchy
+=======================
+
+Service                unique name
+Interface      org.bluez.MediaEndpoint
+Object path    freely definable
+
+Methods                void SetConfiguration(object transport, dict properties)
+
+                       Set configuration for the transport.
+
+               array{byte} SelectConfiguration(array{byte} capabilities)
+
+                       Select preferable configuration from the supported
+                       capabilities.
+
+                       Returns a configuration which can be used to setup
+                       a transport.
+
+                       Note: There is no need to cache the selected
+                       configuration since on success the configuration is
+                       send back as parameter of SetConfiguration.
+
+               void ClearConfiguration(object transport)
+
+                       Clear transport configuration.
+
+               void Release()
+
+                       This method gets called when the service daemon
+                       unregisters the endpoint. An endpoint can use it to do
+                       cleanup tasks. There is no need to unregister the
+                       endpoint, because when this method gets called it has
+                       already been unregistered.
+
+MediaTransport hierarchy
+========================
+
+Service                org.bluez
+Interface      org.bluez.MediaTransport
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/fdX
+
+Methods                dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       properties section for available properties.
+
+               fd, uint16, uint16 Acquire(string accesstype)
+
+                       Acquire transport file descriptor and the MTU for read
+                       and write respectively.
+
+                       possible accesstype:
+
+                               "r" : Read only access
+
+                               "w" : Write only access
+
+                               "rw": Read and write access
+
+               void Release(string accesstype)
+
+                       Releases file descriptor.
+
+               void SetProperty(string name, variant value)
+
+                       Changes the value of the specified property. Only
+                       properties that are listed a read-write can be changed.
+
+                       On success this will emit a PropertyChanged signal.
+
+Signals                void PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     object Device [readonly]
+
+                       Device object which the transport is connected to.
+
+               string UUID [readonly]
+
+                       UUID of the profile which the transport is for.
+
+               byte Codec [readonly]
+
+                       Assigned number of codec that the transport support.
+                       The values should match the profile specification which
+                       is indicated by the UUID.
+
+               array{byte} Configuration [readonly]
+
+                       Configuration blob, it is used as it is so the size and
+                       byte order must match.
+
+               uint16 Delay [readwrite]
+
+                       Optional. Transport delay in 1/10 of millisecond, this
+                       property is only writeable when the transport was
+                       acquired by the sender.
+
+               boolean NREC [readwrite]
+
+                       Optional. Indicates if echo cancelling and noise
+                       reduction functions are active in the transport, this
+                       property is only writeable when the transport was
+                       acquired by the sender.
+
+               boolean InbandRingtone [readwrite]
+
+                       Optional. Indicates if the transport support sending
+                       ringtones, this property is only writeable when the
+                       transport was acquired by the sender.
+
+               string Routing [readonly]
+
+                       Optional. Indicates where is the transport being routed
+
+                       Possible Values: "HCI" or "PCM"
+
+               uint16 Volume [readwrite]
+
+                       Optional. Indicates volume level of the transport,
+                       this property is only writeable when the transport was
+                       acquired by the sender.
+
+                       Possible Values: 0-127
diff --git a/doc/network-api.txt b/doc/network-api.txt
new file mode 100644 (file)
index 0000000..4dd3e58
--- /dev/null
@@ -0,0 +1,88 @@
+BlueZ D-Bus Network API description
+***********************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+
+
+Network hierarchy
+=================
+
+Service                org.bluez
+Interface      org.bluez.Network
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods                string Connect(string uuid)
+
+                       Connect to the network device and return the network
+                       interface name. Examples of the interface name are
+                       bnep0, bnep1 etc.
+
+                       uuid can be either one of "gn", "panu" or "nap" (case
+                       insensitive) or a traditional string representation of
+                       UUID or a hexadecimal number.
+
+                       The connection will be closed and network device
+                       released either upon calling Disconnect() or when
+                       the client disappears from the message bus.
+
+                       Possible errors: org.bluez.Error.AlreadyConnected
+                                        org.bluez.Error.ConnectionAttemptFailed
+
+               void Disconnect()
+
+                       Disconnect from the network device.
+
+                       To abort a connection attempt in case of errors or
+                       timeouts in the client it is fine to call this method.
+
+                       Possible errors: org.bluez.Error.Failed
+
+               dict GetProperties()
+
+                       Returns all properties for the interface. See the
+                       properties section for available properties.
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean Connected [readonly]
+
+                       Indicates if the device is connected.
+
+               string Interface [readonly]
+
+                       Indicates the network interface name when available.
+
+               string UUID [readonly]
+
+                       Indicates the connection role when available.
+
+
+Network server hierarchy
+========================
+
+Service                org.bluez
+Interface      org.bluez.NetworkServer
+Object path    /org/bluez/{hci0,hci1,...}
+
+Methods                void Register(string uuid, string bridge)
+
+                       Register server for the provided UUID. Every new
+                       connection to this server will be added the bridge
+                       interface.
+
+                       Valid UUIDs are "gn", "panu" or "nap".
+
+                       Initially no network server SDP is provided. Only
+                       after this method a SDP record will be available
+                       and the BNEP server will be ready for incoming
+                       connections.
+
+               void Unregister(string uuid)
+
+                       Unregister the server for provided UUID.
+
+                       All servers will be automatically unregistered when
+                       the calling application terminates.
diff --git a/doc/sap-api.txt b/doc/sap-api.txt
new file mode 100644 (file)
index 0000000..b8b7253
--- /dev/null
@@ -0,0 +1,34 @@
+BlueZ D-Bus Sim Access Profile API description
+***********************************
+
+Copyright (C) 2010 ST-Ericsson SA
+
+
+Sim Access Profile hierarchy
+============================
+
+Service                org.bluez
+Interface      org.bluez.SimAccess
+Object path    [variable prefix]/{hci0,hci1,...}
+
+Methods                void Disconnect()
+
+                       Disconnects SAP client from the server.
+
+                       Possible errors: org.bluez.Error.Failed
+
+               dict GetProperties()
+
+                       Return all properties for the interface. See the
+                       properties section for available properties.
+
+                       Possible Errors: org.bluez.Error.Failed
+
+Signals                PropertyChanged(string name, variant value)
+
+                       This signal indicates a changed value of the given
+                       property.
+
+Properties     boolean Connected [readonly]
+
+                       Indicates if SAP client is connected to the server.
diff --git a/doc/serial-api.txt b/doc/serial-api.txt
new file mode 100644 (file)
index 0000000..0bdbdcd
--- /dev/null
@@ -0,0 +1,163 @@
+BlueZ D-Bus Serial API description
+**********************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+
+
+Serial hierarchy
+================
+
+Service                org.bluez
+Interface      org.bluez.Serial
+Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods                string Connect(string pattern)
+
+                       Connects to a specific RFCOMM based service on a
+                       remote device and then creates a RFCOMM TTY
+                       device for it. The RFCOMM TTY device is returned.
+
+                       Possible patterns: UUID 128 bit as string
+                                          Profile short names, e.g: spp, dun
+                                          RFCOMM channel as string, 1-30
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.InProgress
+                                        org.bluez.Error.ConnectionAttemptFailed
+                                        org.bluez.Error.NotSupported
+
+Methods                fd ConnectFD(string pattern) [experimental]
+
+                       Connects to a specific RFCOMM based service on a
+                       remote device and returns a file descriptor to talk
+                        with this device.
+
+                       Possible patterns: UUID 128 bit as string
+                                          Profile short names, e.g: spp, dun
+                                          RFCOMM channel as string, 1-30
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.InProgress
+                                        org.bluez.Error.ConnectionAttemptFailed
+                                        org.bluez.Error.NotSupported
+
+
+               void Disconnect(string device)
+
+                       Disconnect a RFCOMM TTY device that has been
+                       created by Connect method.
+
+                       To abort a connection attempt in case of errors or
+                       timeouts in the client it is fine to call this method.
+
+                       In that case one of patterns of the Connect method should
+                       be supplied instead of the TTY device.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.DoesNotExist
+
+Serial Proxy Manager hierarchy [experimental]
+=============================================
+
+Service                org.bluez
+Interface      org.bluez.SerialProxyManager
+Object path    [variable prefix]/{hci0,hci1,...}
+
+Methods                array{string} ListProxies()
+
+                       Returns an array of the object path strings of
+                       all the proxies created for the adapter.
+
+               string CreateProxy(string pattern, string address)
+
+                       Possible patterns: UUID 128 bit as string
+                                          Profile short names, e.g: spp, dun
+                                          RFCOMM channel as string, 1-30
+
+                       Address is the path to the TTY or Unix socket to be used.
+                       Only one proxy per address (TTY or Unix socket)
+                       is allowed.
+
+                       The object path of created proxy is returned.
+                       On success this will emit a ProxyCreated signal.
+
+                       Possible Errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.AlreadyExists
+                                        org.bluez.Error.Failed
+
+               void RemoveProxy(string path)
+
+                       This removes the proxy object at the given path.
+                       On success this will emit a ProxyRemoved signal.
+
+                       Possible Errors: org.bluez.Error.DoesNotExist
+                                        org.bluez.Error.NotAuthorized
+
+Signals                ProxyCreated(string path)
+
+                       This signal indicates a proxy was created.
+                       Parameter is object path of created proxy.
+
+               ProxyRemoved(string path)
+
+                       This signal indicates a proxy was removed.
+                       Parameter is object path of removed proxy.
+
+Serial Proxy hierarchy [experimental]
+=====================================
+
+Service                org.bluez
+Interface      org.bluez.SerialProxy
+Object path    [variable prefix]/{hci0,hci1,...}/{proxy0,proxy1,...}
+
+Methods                void Enable()
+
+                       Starts to listen to the TTY or Unix socket, allocates
+                       a RFCOMM channel and add record to the server.
+
+                       Possible errors: org.bluez.Error.Failed
+
+               void Disable()
+
+                       Stops to listen to the TTY or Unix socket, shutdown
+                       the RFCOMM channel allocated for the proxy, and remove
+                       record from the server.
+
+                       Possible errors: org.bluez.Error.Failed
+
+               dict GetInfo()
+
+                       Returns all properties for the proxy. See the
+                       properties section for available properties.
+
+               void SetSerialParameters(string rate, uint8 data, uint8 stop,
+                       string parity)
+
+                       Configures serial communication setting baud rate,
+                       data bits, stop bits and parity.
+
+                       Doesn't allow change TTY settings if it is open.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotAuthorized
+
+Properties     string uuid [readonly]
+
+                       128-bit UUID that represents the available remote service.
+
+               string address [readonly]
+
+                       Address is the path to the TTY or Unix socket name used,
+                       set when the proxy was created.
+
+               uint8 channel [readonly]
+
+                       RFCOMM channel.
+
+               boolean enabled [readonly]
+
+                       Indicates if the proxy is currently enabled.
+
+               boolean connected [readonly]
+
+                       Indicates if the proxy is currently connected.
diff --git a/doc/service-api.txt b/doc/service-api.txt
new file mode 100644 (file)
index 0000000..5c8c7f3
--- /dev/null
@@ -0,0 +1,62 @@
+BlueZ D-Bus Adapter API description
+***********************************
+
+Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+Copyright (C) 2005-2006  Johan Hedberg <johan.hedberg@nokia.com>
+Copyright (C) 2005-2006  Claudio Takahasi <claudio.takahasi@indt.org.br>
+Copyright (C) 2006-2007  Luiz von Dentz <luiz.dentz@indt.org.br>
+
+
+Service hierarchy
+=================
+
+Service                org.bluez
+Interface      org.bluez.Service
+Object path    [variable prefix]/{hci0,hci1,...}
+
+Methods                uint32 AddRecord(string record)
+
+                       Adds a new service record from the XML description
+                       and returns the assigned record handle.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.Failed
+
+               void UpdateRecord(uint32 handle, string record)
+
+                       Updates a given service record provided in the
+                       XML format.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotAvailable
+                                        org.bluez.Error.Failed
+
+               void RemoveRecord(uint32 handle)
+
+                       Remove a service record identified by its handle.
+
+                       It is only possible to remove service records that
+                       where added by the current connection.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotAuthorized
+                                        org.bluez.Error.DoesNotExist
+                                        org.bluez.Error.Failed
+
+               void RequestAuthorization(string address, uint32 handle)
+
+                       Request an authorization for an incoming connection
+                       for a specific service record. The service record
+                       needs to be registered via AddRecord first.
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotAuthorized
+                                        org.bluez.Error.DoesNotExist
+                                        org.bluez.Error.Failed
+
+               void CancelAuthorization()
+
+                       Possible errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotAuthorized
+                                        org.bluez.Error.DoesNotExist
+                                        org.bluez.Error.Failed
diff --git a/doc/version.xml.in b/doc/version.xml.in
new file mode 100644 (file)
index 0000000..d78bda9
--- /dev/null
@@ -0,0 +1 @@
+@VERSION@
diff --git a/emulator/btdev.c b/emulator/btdev.c
new file mode 100644 (file)
index 0000000..7d4517a
--- /dev/null
@@ -0,0 +1,1076 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bt.h"
+#include "btdev.h"
+
+#define le16_to_cpu(val) (val)
+#define cpu_to_le16(val) (val)
+
+struct btdev {
+       struct btdev *conn;
+
+       btdev_send_func send_handler;
+       void *send_data;
+
+        uint16_t manufacturer;
+        uint8_t  version;
+       uint16_t revision;
+       uint8_t  commands[64];
+       uint8_t  features[8];
+       uint16_t acl_mtu;
+       uint16_t acl_max_pkt;
+       uint8_t  country_code;
+       uint8_t  bdaddr[6];
+       uint8_t  le_features[8];
+       uint8_t  le_states[8];
+
+       uint16_t default_link_policy;
+       uint8_t  event_mask[8];
+       uint8_t  event_filter;
+       uint8_t  name[248];
+       uint8_t  dev_class[3];
+       uint16_t voice_setting;
+       uint16_t conn_accept_timeout;
+       uint16_t page_timeout;
+       uint8_t  scan_enable;
+       uint8_t  auth_enable;
+       uint8_t  inquiry_mode;
+       uint8_t  afh_assess_mode;
+       uint8_t  ext_inquiry_fec;
+       uint8_t  ext_inquiry_rsp[240];
+       uint8_t  simple_pairing_mode;
+       uint8_t  le_supported;
+       uint8_t  le_simultaneous;
+       uint8_t  le_event_mask[8];
+};
+
+#define MAX_BTDEV_ENTRIES 16
+
+static struct btdev *btdev_list[MAX_BTDEV_ENTRIES] = { };
+
+static inline int add_btdev(struct btdev *btdev)
+{
+       int i, index = -1;
+
+       for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+               if (btdev_list[i] == NULL) {
+                       index = i;
+                       btdev_list[index] = btdev;
+                       break;
+               }
+       }
+
+       return index;
+}
+
+static inline int del_btdev(struct btdev *btdev)
+{
+       int i, index = -1;
+
+       for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+               if (btdev_list[i] == btdev) {
+                       index = i;
+                       btdev_list[index] = NULL;
+                       break;
+               }
+       }
+
+       return index;
+}
+
+static inline struct btdev *find_btdev_by_bdaddr(const uint8_t *bdaddr)
+{
+       int i;
+
+       for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+               if (btdev_list[i] && !memcmp(btdev_list[i]->bdaddr, bdaddr, 6))
+                       return btdev_list[i];
+       }
+
+       return NULL;
+}
+
+static void hexdump(const unsigned char *buf, uint16_t len)
+{
+       static const char hexdigits[] = "0123456789abcdef";
+       char str[68];
+       uint16_t i;
+
+       if (!len)
+               return;
+
+       for (i = 0; i < len; i++) {
+               str[((i % 16) * 3) + 0] = hexdigits[buf[i] >> 4];
+               str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf];
+               str[((i % 16) * 3) + 2] = ' ';
+               str[(i % 16) + 49] = isprint(buf[i]) ? buf[i] : '.';
+
+               if ((i + 1) % 16 == 0) {
+                       str[47] = ' ';
+                       str[48] = ' ';
+                       str[65] = '\0';
+                       printf("%-12c%s\n", ' ', str);
+                       str[0] = ' ';
+               }
+       }
+
+       if (i % 16 > 0) {
+               uint16_t j;
+               for (j = (i % 16); j < 16; j++) {
+                       str[(j * 3) + 0] = ' ';
+                       str[(j * 3) + 1] = ' ';
+                       str[(j * 3) + 2] = ' ';
+                       str[j + 49] = ' ';
+               }
+               str[47] = ' ';
+               str[48] = ' ';
+               str[65] = '\0';
+               printf("%-12c%s\n", ' ', str);
+       }
+}
+
+static void get_bdaddr(uint16_t id, uint8_t *bdaddr)
+{
+       bdaddr[0] = id & 0xff;
+       bdaddr[1] = id >> 8;
+       bdaddr[2] = 0x00;
+       bdaddr[3] = 0x01;
+       bdaddr[4] = 0xaa;
+       bdaddr[5] = 0x00;
+}
+
+struct btdev *btdev_create(uint16_t id)
+{
+       struct btdev *btdev;
+
+       btdev = malloc(sizeof(*btdev));
+       if (!btdev)
+               return NULL;
+
+       memset(btdev, 0, sizeof(*btdev));
+
+       btdev->manufacturer = 63;
+       btdev->version = 0x06;
+       btdev->revision = 0x0000;
+
+       btdev->features[0] |= 0x04;     /* Encryption */
+       btdev->features[0] |= 0x20;     /* Role switch */
+       btdev->features[0] |= 0x80;     /* Sniff mode */
+       btdev->features[1] |= 0x08;     /* SCO link */
+       btdev->features[3] |= 0x40;     /* RSSI with inquiry results */
+       btdev->features[3] |= 0x80;     /* Extended SCO link */
+       btdev->features[4] |= 0x08;     /* AFH capable slave */
+       btdev->features[4] |= 0x10;     /* AFH classification slave */
+       btdev->features[4] |= 0x40;     /* LE Supported */
+       btdev->features[5] |= 0x02;     /* Sniff subrating */
+       btdev->features[5] |= 0x04;     /* Pause encryption */
+       btdev->features[5] |= 0x08;     /* AFH capable master */
+       btdev->features[5] |= 0x10;     /* AFH classification master */
+       btdev->features[6] |= 0x01;     /* Extended Inquiry Response */
+       btdev->features[6] |= 0x02;     /* Simultaneous LE and BR/EDR */
+       btdev->features[6] |= 0x08;     /* Secure Simple Pairing */
+       btdev->features[6] |= 0x10;     /* Encapsulated PDU */
+       btdev->features[6] |= 0x20;     /* Erroneous Data Reporting */
+       btdev->features[6] |= 0x40;     /* Non-flushable Packet Boundary Flag */
+       btdev->features[7] |= 0x01;     /* Link Supervision Timeout Event */
+       btdev->features[7] |= 0x02;     /* Inquiry TX Power Level */
+       btdev->features[7] |= 0x80;     /* Extended features */
+
+       btdev->acl_mtu = 192;
+       btdev->acl_max_pkt = 1;
+
+       btdev->country_code = 0x00;
+
+       get_bdaddr(id, btdev->bdaddr);
+
+       add_btdev(btdev);
+
+       return btdev;
+}
+
+void btdev_destroy(struct btdev *btdev)
+{
+       if (!btdev)
+               return;
+
+       del_btdev(btdev);
+
+       free(btdev);
+}
+
+void btdev_set_send_handler(struct btdev *btdev, btdev_send_func handler,
+                                                       void *user_data)
+{
+       if (!btdev)
+               return;
+
+       btdev->send_handler = handler;
+       btdev->send_data = user_data;
+}
+
+static void send_packet(struct btdev *btdev, const void *data, uint16_t len)
+{
+       if (!btdev->send_handler)
+               return;
+
+       btdev->send_handler(data, len, btdev->send_data);
+}
+
+static void send_event(struct btdev *btdev, uint8_t event,
+                                               const void *data, uint8_t len)
+{
+       struct bt_hci_evt_hdr *hdr;
+       uint16_t pkt_len;
+       void *pkt_data;
+
+       pkt_len = 1 + sizeof(*hdr) + len;
+
+       pkt_data = malloc(pkt_len);
+       if (!pkt_data)
+               return;
+
+       ((uint8_t *) pkt_data)[0] = BT_H4_EVT_PKT;
+
+       hdr = pkt_data + 1;
+       hdr->evt = event;
+       hdr->plen = len;
+
+       if (len > 0)
+               memcpy(pkt_data + 1 + sizeof(*hdr), data, len);
+
+       send_packet(btdev, pkt_data, pkt_len);
+
+       free(pkt_data);
+}
+
+static void cmd_complete(struct btdev *btdev, uint16_t opcode,
+                                               const void *data, uint8_t len)
+{
+       struct bt_hci_evt_hdr *hdr;
+       struct bt_hci_evt_cmd_complete *cc;
+       uint16_t pkt_len;
+       void *pkt_data;
+
+       pkt_len = 1 + sizeof(*hdr) + sizeof(*cc) + len;
+
+       pkt_data = malloc(pkt_len);
+       if (!pkt_data)
+               return;
+
+       ((uint8_t *) pkt_data)[0] = BT_H4_EVT_PKT;
+
+       hdr = pkt_data + 1;
+       hdr->evt = BT_HCI_EVT_CMD_COMPLETE;
+       hdr->plen = sizeof(*cc) + len;
+
+       cc = pkt_data + 1 + sizeof(*hdr);
+       cc->ncmd = 0x01;
+       cc->opcode = cpu_to_le16(opcode);
+
+       if (len > 0)
+               memcpy(pkt_data + 1 + sizeof(*hdr) + sizeof(*cc), data, len);
+
+       send_packet(btdev, pkt_data, pkt_len);
+
+       free(pkt_data);
+}
+
+static void cmd_status(struct btdev *btdev, uint8_t status, uint16_t opcode)
+{
+       struct bt_hci_evt_cmd_status cs;
+
+       cs.status = status;
+       cs.ncmd = 0x01;
+       cs.opcode = cpu_to_le16(opcode);
+
+       send_event(btdev, BT_HCI_EVT_CMD_STATUS, &cs, sizeof(cs));
+}
+
+static void num_completed_packets(struct btdev *btdev)
+{
+       if (btdev->conn) {
+               struct bt_hci_evt_num_completed_packets ncp;
+
+               ncp.num_handles = 1;
+               ncp.handle = cpu_to_le16(42);
+               ncp.count = cpu_to_le16(1);
+
+               send_event(btdev, BT_HCI_EVT_NUM_COMPLETED_PACKETS,
+                                                       &ncp, sizeof(ncp));
+       }
+}
+
+static void inquiry_complete(struct btdev *btdev, uint8_t status)
+{
+       struct bt_hci_evt_inquiry_complete ic;
+       int i;
+
+       for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+               if (!btdev_list[i] || btdev_list[i] == btdev)
+                       continue;
+
+               if (!(btdev_list[i]->scan_enable & 0x02))
+                       continue;
+
+               if (btdev->inquiry_mode == 0x02 &&
+                                       btdev_list[i]->ext_inquiry_rsp[0]) {
+                       struct bt_hci_evt_ext_inquiry_result ir;
+
+                       ir.num_resp = 0x01;
+                       memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+                       memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+                       ir.rssi = -60;
+                       memcpy(ir.data, btdev_list[i]->ext_inquiry_rsp, 240);
+
+                       send_event(btdev, BT_HCI_EVT_EXT_INQUIRY_RESULT,
+                                                       &ir, sizeof(ir));
+                       continue;
+               }
+
+               if (btdev->inquiry_mode > 0x00) {
+                       struct bt_hci_evt_inquiry_result_with_rssi ir;
+
+                       ir.num_resp = 0x01;
+                       memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+                       memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+                       ir.rssi = -60;
+
+                       send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI,
+                                                       &ir, sizeof(ir));
+               } else {
+                       struct bt_hci_evt_inquiry_result ir;
+
+                       ir.num_resp = 0x01;
+                       memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+                       memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+
+                       send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT,
+                                                       &ir, sizeof(ir));
+               }
+        }
+
+       ic.status = status;
+
+       send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
+}
+
+static void conn_complete(struct btdev *btdev,
+                                       const uint8_t *bdaddr, uint8_t status)
+{
+       struct bt_hci_evt_conn_complete cc;
+
+       if (!status) {
+               struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+               btdev->conn = remote;
+               remote->conn = btdev;
+
+               cc.status = status;
+               memcpy(cc.bdaddr, btdev->bdaddr, 6);
+               cc.encr_mode = 0x00;
+
+               cc.handle = cpu_to_le16(42);
+               cc.link_type = 0x01;
+
+               send_event(remote, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
+
+               cc.handle = cpu_to_le16(42);
+               cc.link_type = 0x01;
+       } else {
+               cc.handle = cpu_to_le16(0x0000);
+               cc.link_type = 0x01;
+       }
+
+       cc.status = status;
+       memcpy(cc.bdaddr, bdaddr, 6);
+       cc.encr_mode = 0x00;
+
+       send_event(btdev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
+}
+
+static void conn_request(struct btdev *btdev, const uint8_t *bdaddr)
+{
+       struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+       if (remote) {
+               if (remote->scan_enable & 0x01) {
+                       struct bt_hci_evt_conn_request cr;
+
+                       memcpy(cr.bdaddr, btdev->bdaddr, 6);
+                       memcpy(cr.dev_class, btdev->dev_class, 3);
+                       cr.link_type = 0x01;
+
+                       send_event(remote, BT_HCI_EVT_CONN_REQUEST,
+                                                       &cr, sizeof(cr));
+               } else
+                       conn_complete(btdev, bdaddr, BT_HCI_ERR_PAGE_TIMEOUT);
+       } else
+               conn_complete(btdev, bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+}
+
+static void disconnect_complete(struct btdev *btdev, uint16_t handle,
+                                                       uint8_t reason)
+{
+       struct bt_hci_evt_disconnect_complete dc;
+       struct btdev *remote;
+
+       if (!btdev) {
+               dc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+               dc.handle = cpu_to_le16(handle);
+               dc.reason = 0x00;
+
+               send_event(btdev, BT_HCI_EVT_DISCONNECT_COMPLETE,
+                                                       &dc, sizeof(dc));
+               return;
+       }
+
+       dc.status = BT_HCI_ERR_SUCCESS;
+       dc.handle = cpu_to_le16(handle);
+       dc.reason = reason;
+
+       remote = btdev->conn;
+
+       btdev->conn = NULL;
+       remote->conn = NULL;
+
+       send_event(btdev, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
+       send_event(remote, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
+}
+
+static void name_request_complete(struct btdev *btdev,
+                                       const uint8_t *bdaddr, uint8_t status)
+{
+        struct bt_hci_evt_remote_name_req_complete nc;
+
+       nc.status = status;
+       memcpy(nc.bdaddr, bdaddr, 6);
+       memset(nc.name, 0, 248);
+
+       if (!status) {
+               struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+               if (remote)
+                       memcpy(nc.name, remote->name, 248);
+               else
+                       nc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+       }
+
+       send_event(btdev, BT_HCI_EVT_REMOTE_NAME_REQUEST_COMPLETE,
+                                                       &nc, sizeof(nc));
+}
+
+static void remote_features_complete(struct btdev *btdev, uint16_t handle)
+{
+       struct bt_hci_evt_remote_features_complete rfc;
+
+       if (btdev->conn) {
+               rfc.status = BT_HCI_ERR_SUCCESS;
+               rfc.handle = cpu_to_le16(handle);
+               memcpy(rfc.features, btdev->conn->features, 8);
+       } else {
+               rfc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+               rfc.handle = cpu_to_le16(handle);
+               memset(rfc.features, 0, 8);
+       }
+
+       send_event(btdev, BT_HCI_EVT_REMOTE_FEATURES_COMPLETE,
+                                                       &rfc, sizeof(rfc));
+}
+
+static void remote_ext_features_complete(struct btdev *btdev, uint16_t handle,
+                                                               uint8_t page)
+{
+       struct bt_hci_evt_remote_ext_features_complete refc;
+
+       if (btdev->conn && page < 0x02) {
+               refc.handle = cpu_to_le16(handle);
+               refc.page = page;
+               refc.max_page = 0x01;
+
+               switch (page) {
+               case 0x00:
+                       refc.status = BT_HCI_ERR_SUCCESS;
+                       memcpy(refc.features, btdev->conn->features, 8);
+                       break;
+               case 0x01:
+                       refc.status = BT_HCI_ERR_SUCCESS;
+                       memset(refc.features, 0, 8);
+                       break;
+               default:
+                       refc.status = BT_HCI_ERR_INVALID_PARAMETERS;
+                       memset(refc.features, 0, 8);
+                       break;
+               }
+       } else {
+               refc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+               refc.handle = cpu_to_le16(handle);
+               refc.page = page;
+               refc.max_page = 0x01;
+               memset(refc.features, 0, 8);
+       }
+
+       send_event(btdev, BT_HCI_EVT_REMOTE_EXT_FEATURES_COMPLETE,
+                                                       &refc, sizeof(refc));
+}
+
+static void remote_version_complete(struct btdev *btdev, uint16_t handle)
+{
+       struct bt_hci_evt_remote_version_complete rvc;
+
+       if (btdev->conn) {
+               rvc.status = BT_HCI_ERR_SUCCESS;
+               rvc.handle = cpu_to_le16(handle);
+               rvc.lmp_ver = btdev->conn->version;
+               rvc.manufacturer = cpu_to_le16(btdev->conn->manufacturer);
+               rvc.lmp_subver = cpu_to_le16(btdev->conn->revision);
+       } else {
+               rvc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+               rvc.handle = cpu_to_le16(handle);
+               rvc.lmp_ver = 0x00;
+               rvc.manufacturer = cpu_to_le16(0);
+               rvc.lmp_subver = cpu_to_le16(0);
+       }
+
+       send_event(btdev, BT_HCI_EVT_REMOTE_VERSION_COMPLETE,
+                                                       &rvc, sizeof(rvc));
+}
+
+static void process_cmd(struct btdev *btdev, const void *data, uint16_t len)
+{
+       const struct bt_hci_cmd_hdr *hdr = data;
+       const struct bt_hci_cmd_create_conn *cc;
+       const struct bt_hci_cmd_disconnect *dc;
+       const struct bt_hci_cmd_create_conn_cancel *ccc;
+       const struct bt_hci_cmd_accept_conn_request *acr;
+       const struct bt_hci_cmd_reject_conn_request *rcr;
+       const struct bt_hci_cmd_remote_name_request *rnr;
+       const struct bt_hci_cmd_remote_name_request_cancel *rnrc;
+       const struct bt_hci_cmd_read_remote_features *rrf;
+       const struct bt_hci_cmd_read_remote_ext_features *rref;
+       const struct bt_hci_cmd_read_remote_version *rrv;
+       const struct bt_hci_cmd_write_default_link_policy *wdlp;
+       const struct bt_hci_cmd_set_event_mask *sem;
+       const struct bt_hci_cmd_set_event_filter *sef;
+       const struct bt_hci_cmd_write_local_name *wln;
+       const struct bt_hci_cmd_write_conn_accept_timeout *wcat;
+       const struct bt_hci_cmd_write_page_timeout *wpt;
+       const struct bt_hci_cmd_write_scan_enable *wse;
+       const struct bt_hci_cmd_write_auth_enable *wae;
+       const struct bt_hci_cmd_write_class_of_dev *wcod;
+       const struct bt_hci_cmd_write_voice_setting *wvs;
+       const struct bt_hci_cmd_write_inquiry_mode *wim;
+       const struct bt_hci_cmd_write_afh_assess_mode *waam;
+       const struct bt_hci_cmd_write_ext_inquiry_rsp *weir;
+       const struct bt_hci_cmd_write_simple_pairing_mode *wspm;
+       const struct bt_hci_cmd_write_le_host_supported *wlhs;
+       const struct bt_hci_cmd_le_set_event_mask *lsem;
+       struct bt_hci_rsp_read_default_link_policy rdlp;
+       struct bt_hci_rsp_read_stored_link_key rslk;
+       struct bt_hci_rsp_write_stored_link_key wslk;
+       struct bt_hci_rsp_delete_stored_link_key dslk;
+       struct bt_hci_rsp_read_local_name rln;
+       struct bt_hci_rsp_read_conn_accept_timeout rcat;
+       struct bt_hci_rsp_read_page_timeout rpt;
+       struct bt_hci_rsp_read_scan_enable rse;
+       struct bt_hci_rsp_read_auth_enable rae;
+       struct bt_hci_rsp_read_class_of_dev rcod;
+       struct bt_hci_rsp_read_voice_setting rvs;
+       struct bt_hci_rsp_read_inquiry_mode rim;
+       struct bt_hci_rsp_read_afh_assess_mode raam;
+       struct bt_hci_rsp_read_ext_inquiry_rsp reir;
+       struct bt_hci_rsp_read_simple_pairing_mode rspm;
+       struct bt_hci_rsp_read_inquiry_rsp_tx_power rirtp;
+       struct bt_hci_rsp_read_le_host_supported rlhs;
+       struct bt_hci_rsp_read_local_version rlv;
+       struct bt_hci_rsp_read_local_commands rlc;
+       struct bt_hci_rsp_read_local_features rlf;
+       struct bt_hci_rsp_read_local_ext_features rlef;
+       struct bt_hci_rsp_read_buffer_size rbs;
+       struct bt_hci_rsp_read_country_code rcc;
+       struct bt_hci_rsp_read_bd_addr rba;
+       struct bt_hci_rsp_read_data_block_size rdbs;
+       struct bt_hci_rsp_le_read_buffer_size lrbs;
+       struct bt_hci_rsp_le_read_local_features lrlf;
+       struct bt_hci_rsp_le_read_supported_states lrss;
+       uint16_t opcode;
+       uint8_t status, page;
+
+       if (len < sizeof(*hdr))
+               return;
+
+       opcode = le16_to_cpu(hdr->opcode);
+
+       switch (opcode) {
+       case BT_HCI_CMD_INQUIRY:
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               inquiry_complete(btdev, BT_HCI_ERR_SUCCESS);
+               break;
+
+       case BT_HCI_CMD_INQUIRY_CANCEL:
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_CREATE_CONN:
+               cc = data + sizeof(*hdr);
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               conn_request(btdev, cc->bdaddr);
+               break;
+
+       case BT_HCI_CMD_DISCONNECT:
+               dc = data + sizeof(*hdr);
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               disconnect_complete(btdev, le16_to_cpu(dc->handle), dc->reason);
+               break;
+
+       case BT_HCI_CMD_CREATE_CONN_CANCEL:
+               ccc = data + sizeof(*hdr);
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               conn_complete(btdev, ccc->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+               break;
+
+       case BT_HCI_CMD_ACCEPT_CONN_REQUEST:
+               acr = data + sizeof(*hdr);
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               conn_complete(btdev, acr->bdaddr, BT_HCI_ERR_SUCCESS);
+               break;
+
+       case BT_HCI_CMD_REJECT_CONN_REQUEST:
+               rcr = data + sizeof(*hdr);
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               conn_complete(btdev, rcr->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+               break;
+
+       case BT_HCI_CMD_REMOTE_NAME_REQUEST:
+               rnr = data + sizeof(*hdr);
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               name_request_complete(btdev, rnr->bdaddr, BT_HCI_ERR_SUCCESS);
+               break;
+
+       case BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL:
+               rnrc = data + sizeof(*hdr);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               name_request_complete(btdev, rnrc->bdaddr,
+                                               BT_HCI_ERR_UNKNOWN_CONN_ID);
+               break;
+
+       case BT_HCI_CMD_READ_REMOTE_FEATURES:
+               rrf = data + sizeof(*hdr);
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               remote_features_complete(btdev, le16_to_cpu(rrf->handle));
+               break;
+
+       case BT_HCI_CMD_READ_REMOTE_EXT_FEATURES:
+               rref = data + sizeof(*hdr);
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               remote_ext_features_complete(btdev, le16_to_cpu(rref->handle),
+                                                               rref->page);
+               break;
+
+       case BT_HCI_CMD_READ_REMOTE_VERSION:
+               rrv = data + sizeof(*hdr);
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               remote_version_complete(btdev, le16_to_cpu(rrv->handle));
+               break;
+
+       case BT_HCI_CMD_READ_DEFAULT_LINK_POLICY:
+               rdlp.status = BT_HCI_ERR_SUCCESS;
+               rdlp.policy = cpu_to_le16(btdev->default_link_policy);
+               cmd_complete(btdev, opcode, &rdlp, sizeof(rdlp));
+               break;
+
+       case BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY:
+               wdlp = data + sizeof(*hdr);
+               btdev->default_link_policy = le16_to_cpu(wdlp->policy);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_SET_EVENT_MASK:
+               sem = data + sizeof(*hdr);
+               memcpy(btdev->event_mask, sem->mask, 8);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_RESET:
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_SET_EVENT_FILTER:
+               sef = data + sizeof(*hdr);
+               btdev->event_filter = sef->type;
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_STORED_LINK_KEY:
+               rslk.status = BT_HCI_ERR_SUCCESS;
+               rslk.max_num_keys = cpu_to_le16(0);
+               rslk.num_keys = cpu_to_le16(0);
+               cmd_complete(btdev, opcode, &rslk, sizeof(rslk));
+               break;
+
+       case BT_HCI_CMD_WRITE_STORED_LINK_KEY:
+               wslk.status = BT_HCI_ERR_SUCCESS;
+               wslk.num_keys = 0;
+               cmd_complete(btdev, opcode, &wslk, sizeof(wslk));
+               break;
+
+       case BT_HCI_CMD_DELETE_STORED_LINK_KEY:
+               dslk.status = BT_HCI_ERR_SUCCESS;
+               dslk.num_keys = cpu_to_le16(0);
+               cmd_complete(btdev, opcode, &dslk, sizeof(dslk));
+               break;
+
+       case BT_HCI_CMD_WRITE_LOCAL_NAME:
+               wln = data + sizeof(*hdr);
+               memcpy(btdev->name, wln->name, 248);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_LOCAL_NAME:
+               rln.status = BT_HCI_ERR_SUCCESS;
+               memcpy(rln.name, btdev->name, 248);
+               cmd_complete(btdev, opcode, &rln, sizeof(rln));
+               break;
+
+       case BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT:
+               rcat.status = BT_HCI_ERR_SUCCESS;
+               rcat.timeout = cpu_to_le16(btdev->conn_accept_timeout);
+               cmd_complete(btdev, opcode, &rcat, sizeof(rcat));
+               break;
+
+       case BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT:
+               wcat = data + sizeof(*hdr);
+               btdev->conn_accept_timeout = le16_to_cpu(wcat->timeout);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_PAGE_TIMEOUT:
+               rpt.status = BT_HCI_ERR_SUCCESS;
+               rpt.timeout = cpu_to_le16(btdev->page_timeout);
+               cmd_complete(btdev, opcode, &rpt, sizeof(rpt));
+               break;
+
+       case BT_HCI_CMD_WRITE_PAGE_TIMEOUT:
+               wpt = data + sizeof(*hdr);
+               btdev->page_timeout = le16_to_cpu(wpt->timeout);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_SCAN_ENABLE:
+               rse.status = BT_HCI_ERR_SUCCESS;
+               rse.enable = btdev->scan_enable;
+               cmd_complete(btdev, opcode, &rse, sizeof(rse));
+               break;
+
+       case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+               wse = data + sizeof(*hdr);
+               btdev->scan_enable = wse->enable;
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_AUTH_ENABLE:
+               rae.status = BT_HCI_ERR_SUCCESS;
+               rae.enable = btdev->auth_enable;
+               cmd_complete(btdev, opcode, &rae, sizeof(rae));
+               break;
+
+       case BT_HCI_CMD_WRITE_AUTH_ENABLE:
+               wae = data + sizeof(*hdr);
+               btdev->auth_enable = wae->enable;
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_CLASS_OF_DEV:
+               rcod.status = BT_HCI_ERR_SUCCESS;
+               memcpy(rcod.dev_class, btdev->dev_class, 3);
+               cmd_complete(btdev, opcode, &rcod, sizeof(rcod));
+               break;
+
+       case BT_HCI_CMD_WRITE_CLASS_OF_DEV:
+               wcod = data + sizeof(*hdr);
+               memcpy(btdev->dev_class, wcod->dev_class, 3);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_VOICE_SETTING:
+               rvs.status = BT_HCI_ERR_SUCCESS;
+               rvs.setting = cpu_to_le16(btdev->voice_setting);
+               cmd_complete(btdev, opcode, &rvs, sizeof(rvs));
+               break;
+
+       case BT_HCI_CMD_WRITE_VOICE_SETTING:
+               wvs = data + sizeof(*hdr);
+               btdev->voice_setting = le16_to_cpu(wvs->setting);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_INQUIRY_MODE:
+               rim.status = BT_HCI_ERR_SUCCESS;
+               rim.mode = btdev->inquiry_mode;
+               cmd_complete(btdev, opcode, &rim, sizeof(rim));
+               break;
+
+       case BT_HCI_CMD_WRITE_INQUIRY_MODE:
+               wim = data + sizeof(*hdr);
+               btdev->inquiry_mode = wim->mode;
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_AFH_ASSESS_MODE:
+               raam.status = BT_HCI_ERR_SUCCESS;
+               raam.mode = btdev->afh_assess_mode;
+               cmd_complete(btdev, opcode, &raam, sizeof(raam));
+               break;
+
+       case BT_HCI_CMD_WRITE_AFH_ASSESS_MODE:
+               waam = data + sizeof(*hdr);
+               btdev->afh_assess_mode = waam->mode;
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_EXT_INQUIRY_RSP:
+               reir.status = BT_HCI_ERR_SUCCESS;
+               reir.fec = btdev->ext_inquiry_fec;
+               memcpy(reir.data, btdev->ext_inquiry_rsp, 240);
+               cmd_complete(btdev, opcode, &reir, sizeof(reir));
+               break;
+
+       case BT_HCI_CMD_WRITE_EXT_INQUIRY_RSP:
+               weir = data + sizeof(*hdr);
+               btdev->ext_inquiry_fec = weir->fec;
+               memcpy(btdev->ext_inquiry_rsp, weir->data, 240);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE:
+               rspm.status = BT_HCI_ERR_SUCCESS;
+               rspm.mode = btdev->simple_pairing_mode;
+               cmd_complete(btdev, opcode, &rspm, sizeof(rspm));
+               break;
+
+       case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+               wspm = data + sizeof(*hdr);
+               btdev->simple_pairing_mode = wspm->mode;
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_INQUIRY_RSP_TX_POWER:
+               rirtp.status = BT_HCI_ERR_SUCCESS;
+               rirtp.level = 0;
+               cmd_complete(btdev, opcode, &rirtp, sizeof(rirtp));
+               break;
+
+       case BT_HCI_CMD_READ_LE_HOST_SUPPORTED:
+               rlhs.status = BT_HCI_ERR_SUCCESS;
+               rlhs.supported = btdev->le_supported;
+               rlhs.simultaneous = btdev->le_simultaneous;
+               cmd_complete(btdev, opcode, &rlhs, sizeof(rlhs));
+               break;
+
+       case BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED:
+               wlhs = data + sizeof(*hdr);
+               btdev->le_supported = wlhs->supported;
+               btdev->le_simultaneous = wlhs->simultaneous;
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_LOCAL_VERSION:
+               rlv.status = BT_HCI_ERR_SUCCESS;
+               rlv.hci_ver = btdev->version;
+               rlv.hci_rev = cpu_to_le16(btdev->revision);
+               rlv.lmp_ver = btdev->version;
+               rlv.manufacturer = cpu_to_le16(btdev->manufacturer);
+               rlv.lmp_subver = cpu_to_le16(btdev->revision);
+               cmd_complete(btdev, opcode, &rlv, sizeof(rlv));
+               break;
+
+       case BT_HCI_CMD_READ_LOCAL_COMMANDS:
+               rlc.status = BT_HCI_ERR_SUCCESS;
+               memcpy(rlc.commands, btdev->commands, 64);
+               cmd_complete(btdev, opcode, &rlc, sizeof(rlc));
+               break;
+
+       case BT_HCI_CMD_READ_LOCAL_FEATURES:
+               rlf.status = BT_HCI_ERR_SUCCESS;
+               memcpy(rlf.features, btdev->features, 8);
+               cmd_complete(btdev, opcode, &rlf, sizeof(rlf));
+               break;
+
+       case BT_HCI_CMD_READ_LOCAL_EXT_FEATURES:
+               page = ((const uint8_t *) data)[sizeof(*hdr)];
+               switch (page) {
+               case 0x00:
+                       rlef.status = BT_HCI_ERR_SUCCESS;
+                       rlef.page = 0x00;
+                       rlef.max_page = 0x01;
+                       memcpy(rlef.features, btdev->features, 8);
+                       break;
+               case 0x01:
+                       rlef.status = BT_HCI_ERR_SUCCESS;
+                       rlef.page = 0x01;
+                       rlef.max_page = 0x01;
+                       memset(rlef.features, 0, 8);
+                       if (btdev->simple_pairing_mode)
+                               rlef.features[0] |= 0x01;
+                       if (btdev->le_supported)
+                               rlef.features[0] |= 0x02;
+                       if (btdev->le_simultaneous)
+                               rlef.features[0] |= 0x04;
+                       break;
+               default:
+                       rlef.status = BT_HCI_ERR_INVALID_PARAMETERS;
+                       rlef.page = page;
+                       rlef.max_page = 0x01;
+                       memset(rlef.features, 0, 8);
+                       break;
+               }
+               cmd_complete(btdev, opcode, &rlef, sizeof(rlef));
+               break;
+
+       case BT_HCI_CMD_READ_BUFFER_SIZE:
+               rbs.status = BT_HCI_ERR_SUCCESS;
+               rbs.acl_mtu = cpu_to_le16(btdev->acl_mtu);
+               rbs.sco_mtu = 0;
+               rbs.acl_max_pkt = cpu_to_le16(btdev->acl_max_pkt);
+               rbs.sco_max_pkt = cpu_to_le16(0);
+               cmd_complete(btdev, opcode, &rbs, sizeof(rbs));
+               break;
+
+       case BT_HCI_CMD_READ_COUNTRY_CODE:
+               rcc.status = BT_HCI_ERR_SUCCESS;
+               rcc.code = btdev->country_code;
+               cmd_complete(btdev, opcode, &rcc, sizeof(rcc));
+               break;
+
+       case BT_HCI_CMD_READ_BD_ADDR:
+               rba.status = BT_HCI_ERR_SUCCESS;
+               memcpy(rba.bdaddr, btdev->bdaddr, 6);
+               cmd_complete(btdev, opcode, &rba, sizeof(rba));
+               break;
+
+       case BT_HCI_CMD_READ_DATA_BLOCK_SIZE:
+               rdbs.status = BT_HCI_ERR_SUCCESS;
+               rdbs.max_acl_len = cpu_to_le16(btdev->acl_mtu);
+               rdbs.block_len = cpu_to_le16(btdev->acl_mtu);
+               rdbs.num_blocks = cpu_to_le16(btdev->acl_max_pkt);
+               cmd_complete(btdev, opcode, &rdbs, sizeof(rdbs));
+               break;
+
+       case BT_HCI_CMD_LE_SET_EVENT_MASK:
+               lsem = data + sizeof(*hdr);
+               memcpy(btdev->le_event_mask, lsem->mask, 8);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_LE_READ_BUFFER_SIZE:
+               lrbs.status = BT_HCI_ERR_SUCCESS;
+               lrbs.le_mtu = cpu_to_le16(btdev->acl_mtu);
+               lrbs.le_max_pkt = btdev->acl_max_pkt;
+               cmd_complete(btdev, opcode, &lrbs, sizeof(lrbs));
+               break;
+
+       case BT_HCI_CMD_LE_READ_LOCAL_FEATURES:
+               lrlf.status = BT_HCI_ERR_SUCCESS;
+               memcpy(lrlf.features, btdev->le_features, 8);
+               cmd_complete(btdev, opcode, &lrlf, sizeof(lrlf));
+               break;
+
+       case BT_HCI_CMD_LE_SET_SCAN_PARAMETERS:
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_LE_SET_SCAN_ENABLE:
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_LE_READ_SUPPORTED_STATES:
+               lrss.status = BT_HCI_ERR_SUCCESS;
+               memcpy(lrss.states, btdev->le_states, 8);
+               cmd_complete(btdev, opcode, &lrss, sizeof(lrss));
+               break;
+
+       default:
+               printf("Unsupported command 0x%4.4x\n", opcode);
+               hexdump(data, len);
+               cmd_status(btdev, BT_HCI_ERR_UNKNOWN_COMMAND, opcode);
+               break;
+       }
+}
+
+void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len)
+{
+       uint8_t pkt_type;
+
+       if (!btdev)
+               return;
+
+       if (len < 1)
+               return;
+
+       pkt_type = ((const uint8_t *) data)[0];
+
+       switch (pkt_type) {
+       case BT_H4_CMD_PKT:
+               process_cmd(btdev, data + 1, len - 1);
+               break;
+       case BT_H4_ACL_PKT:
+               if (btdev->conn)
+                       send_packet(btdev->conn, data, len);
+               num_completed_packets(btdev);
+               break;
+       default:
+               printf("Unsupported packet 0x%2.2x\n", pkt_type);
+               break;
+       }
+}
diff --git a/emulator/btdev.h b/emulator/btdev.h
new file mode 100644 (file)
index 0000000..7b211a2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+
+typedef void (*btdev_send_func) (const void *data, uint16_t len,
+                                                       void *user_data);
+
+struct btdev;
+
+struct btdev *btdev_create(uint16_t id);
+void btdev_destroy(struct btdev *btdev);
+
+void btdev_set_send_handler(struct btdev *btdev, btdev_send_func handler,
+                                                       void *user_data);
+
+void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len);
diff --git a/emulator/main.c b/emulator/main.c
new file mode 100644 (file)
index 0000000..125460d
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include "mainloop.h"
+#include "server.h"
+#include "vhci.h"
+
+static void signal_callback(int signum, void *user_data)
+{
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               mainloop_quit();
+               break;
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       struct vhci *vhci;
+       struct server *server;
+       sigset_t mask;
+
+       mainloop_init();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       vhci = vhci_open(VHCI_TYPE_BREDR, 0x23);
+       if (!vhci) {
+               fprintf(stderr, "Failed to open Virtual HCI device\n");
+               return 1;
+       }
+
+       server = server_open_unix("/tmp/bt-server-bredr", 0x42);
+       if (!server) {
+               fprintf(stderr, "Failed to open server channel\n");
+               vhci_close(vhci);
+               return 1;
+       }
+
+       return mainloop_run();
+}
diff --git a/emulator/server.c b/emulator/server.c
new file mode 100644 (file)
index 0000000..1ff9904
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#include "mainloop.h"
+#include "btdev.h"
+#include "server.h"
+
+struct server {
+       uint16_t id;
+       int fd;
+};
+
+struct client {
+       int fd;
+       struct btdev *btdev;
+       uint8_t *pkt_data;
+       uint8_t pkt_type;
+       uint16_t pkt_expect;
+       uint16_t pkt_len;
+       uint16_t pkt_offset;
+};
+
+static void server_destroy(void *user_data)
+{
+       struct server *server = user_data;
+
+       close(server->fd);
+
+       free(server);
+}
+
+static void client_destroy(void *user_data)
+{
+       struct client *client = user_data;
+
+       btdev_destroy(client->btdev);
+
+       close(client->fd);
+
+       free(client);
+}
+
+static void client_write_callback(const void *data, uint16_t len,
+                                                       void *user_data)
+{
+       struct client *client = user_data;
+       ssize_t written;
+
+       written = send(client->fd, data, len, MSG_DONTWAIT);
+       if (written < 0)
+               return;
+}
+
+static void client_read_callback(int fd, uint32_t events, void *user_data)
+{
+       struct client *client = user_data;
+       static uint8_t buf[4096];
+       uint8_t *ptr = buf;
+       ssize_t len;
+       uint16_t count;
+
+       if (events & (EPOLLERR | EPOLLHUP))
+               return;
+
+again:
+       len = recv(fd, buf + client->pkt_offset,
+                       sizeof(buf) - client->pkt_offset, MSG_DONTWAIT);
+       if (len < 0) {
+               if (errno == EAGAIN)
+                       goto again;
+               return;
+       }
+
+       count = client->pkt_offset + len;
+
+       while (count > 0) {
+               hci_command_hdr *cmd_hdr;
+
+               if (!client->pkt_data) {
+                       client->pkt_type = ptr[0];
+
+                       switch (client->pkt_type) {
+                       case HCI_COMMAND_PKT:
+                               if (count < HCI_COMMAND_HDR_SIZE + 1) {
+                                       client->pkt_offset += len;
+                                       return;
+                               }
+                               cmd_hdr = (hci_command_hdr *) (ptr + 1);
+                               client->pkt_expect = HCI_COMMAND_HDR_SIZE +
+                                                       cmd_hdr->plen + 1;
+                               client->pkt_data = malloc(client->pkt_expect);
+                               client->pkt_len = 0;
+                               break;
+                       default:
+                               printf("packet error\n");
+                               return;
+                       }
+
+                       client->pkt_offset = 0;
+               }
+
+               if (count >= client->pkt_expect) {
+                       memcpy(client->pkt_data + client->pkt_len,
+                                               ptr, client->pkt_expect);
+                       ptr += client->pkt_expect;
+                       count -= client->pkt_expect;
+
+                       btdev_receive_h4(client->btdev, client->pkt_data,
+                                       client->pkt_len + client->pkt_expect);
+
+                       free(client->pkt_data);
+                       client->pkt_data = NULL;
+               } else {
+                       memcpy(client->pkt_data + client->pkt_len, ptr, count);
+                       client->pkt_len += count;
+                       client->pkt_expect -= count;
+                       count = 0;
+               }
+       }
+}
+
+static int accept_client(int fd)
+{
+       struct sockaddr_un addr;
+       socklen_t len;
+       int nfd;
+
+       memset(&addr, 0, sizeof(addr));
+       len = sizeof(addr);
+
+       if (getsockname(fd, (struct sockaddr *) &addr, &len) < 0) {
+               perror("Failed to get socket name");
+               return -1;
+       }
+
+       printf("Request for %s\n", addr.sun_path);
+
+       nfd = accept(fd, (struct sockaddr *) &addr, &len);
+       if (nfd < 0) {
+               perror("Failed to accept client socket");
+               return -1;
+       }
+
+       return nfd;
+}
+
+static void server_accept_callback(int fd, uint32_t events, void *user_data)
+{
+       struct server *server = user_data;
+       struct client *client;
+
+       if (events & (EPOLLERR | EPOLLHUP))
+               return;
+
+       client = malloc(sizeof(*client));
+       if (!client)
+               return;
+
+       memset(client, 0, sizeof(*client));
+
+       client->fd = accept_client(server->fd);
+       if (client->fd < 0) {
+               free(client);
+               return;
+       }
+
+       client->btdev = btdev_create(server->id);
+       if (!client->btdev) {
+               close(client->fd);
+               free(client);
+               return;
+       }
+
+       btdev_set_send_handler(client->btdev, client_write_callback, client);
+
+       if (mainloop_add_fd(client->fd, EPOLLIN, client_read_callback,
+                                               client, client_destroy) < 0) {
+               btdev_destroy(client->btdev);
+               close(client->fd);
+               free(client);
+       }
+}
+
+static int open_server(const char *path)
+{
+       struct sockaddr_un addr;
+       int fd;
+
+       unlink(path);
+
+       fd = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (fd < 0) {
+               perror("Failed to open server socket");
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strcpy(addr.sun_path, path);
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Failed to bind server socket");
+               close(fd);
+               return -1;
+       }
+
+       if (listen(fd, 5) < 0) {
+               perror("Failed to listen server socket");
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+struct server *server_open_unix(const char *path, uint16_t id)
+{
+       struct server *server;
+
+       server = malloc(sizeof(*server));
+       if (!server)
+               return NULL;
+
+       memset(server, 0, sizeof(*server));
+       server->id = id;
+
+       server->fd = open_server(path);
+       if (server->fd < 0) {
+               free(server);
+               return NULL;
+       }
+
+       if (mainloop_add_fd(server->fd, EPOLLIN, server_accept_callback,
+                                               server, server_destroy) < 0) {
+               close(server->fd);
+               free(server);
+               return NULL;
+       }
+
+       return server;
+}
+
+void server_close(struct server *server)
+{
+       if (!server)
+               return;
+
+       mainloop_remove_fd(server->fd);
+}
diff --git a/emulator/server.h b/emulator/server.h
new file mode 100644 (file)
index 0000000..836db5f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+
+struct server;
+
+struct server *server_open_unix(const char *path, uint16_t id);
+void server_close(struct server *server);
diff --git a/emulator/vhci.c b/emulator/vhci.c
new file mode 100644 (file)
index 0000000..940e562
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mainloop.h"
+#include "btdev.h"
+#include "vhci.h"
+
+struct vhci {
+       enum vhci_type type;
+       int fd;
+       struct btdev *btdev;
+};
+
+static void vhci_destroy(void *user_data)
+{
+       struct vhci *vhci = user_data;
+
+       btdev_destroy(vhci->btdev);
+
+       close(vhci->fd);
+
+       free(vhci);
+}
+
+static void vhci_write_callback(const void *data, uint16_t len, void *user_data)
+{
+       struct vhci *vhci = user_data;
+       ssize_t written;
+
+       written = write(vhci->fd, data, len);
+       if (written < 0)
+               return;
+}
+
+static void vhci_read_callback(int fd, uint32_t events, void *user_data)
+{
+       struct vhci *vhci = user_data;
+       unsigned char buf[4096];
+       ssize_t len;
+
+       if (events & (EPOLLERR | EPOLLHUP))
+               return;
+
+       len = read(vhci->fd, buf, sizeof(buf));
+       if (len < 0)
+               return;
+
+       btdev_receive_h4(vhci->btdev, buf, len);
+}
+
+struct vhci *vhci_open(enum vhci_type type, uint16_t id)
+{
+       struct vhci *vhci;
+
+       switch (type) {
+       case VHCI_TYPE_BREDR:
+               break;
+       case VHCI_TYPE_AMP:
+               return NULL;
+       }
+
+       vhci = malloc(sizeof(*vhci));
+       if (!vhci)
+               return NULL;
+
+       memset(vhci, 0, sizeof(*vhci));
+       vhci->type = type;
+
+       vhci->fd = open("/dev/vhci", O_RDWR | O_NONBLOCK);
+       if (vhci->fd < 0) {
+               free(vhci);
+               return NULL;
+       }
+
+       vhci->btdev = btdev_create(id);
+       if (!vhci->btdev) {
+               close(vhci->fd);
+               free(vhci);
+               return NULL;
+       }
+
+       btdev_set_send_handler(vhci->btdev, vhci_write_callback, vhci);
+
+       if (mainloop_add_fd(vhci->fd, EPOLLIN, vhci_read_callback,
+                                               vhci, vhci_destroy) < 0) {
+               btdev_destroy(vhci->btdev);
+               close(vhci->fd);
+               free(vhci);
+               return NULL;
+       }
+
+       return vhci;
+}
+
+void vhci_close(struct vhci *vhci)
+{
+       if (!vhci)
+               return;
+
+       mainloop_remove_fd(vhci->fd);
+}
diff --git a/emulator/vhci.h b/emulator/vhci.h
new file mode 100644 (file)
index 0000000..4abb183
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+
+enum vhci_type {
+       VHCI_TYPE_BREDR = 0,
+       VHCI_TYPE_AMP   = 1,
+};
+
+struct vhci;
+
+struct vhci *vhci_open(enum vhci_type type, uint16_t id);
+void vhci_close(struct vhci *vhci);
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
new file mode 100644 (file)
index 0000000..0a8a27c
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __GDBUS_H
+#define __GDBUS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+typedef void (* GDBusWatchFunction) (DBusConnection *connection,
+                                                       void *user_data);
+
+typedef gboolean (* GDBusSignalFunction) (DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+                                                       DBusError *error);
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+                                                       DBusError *error);
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+                                                       DBusError *error);
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+                               GDBusWatchFunction function,
+                               void *user_data, DBusFreeFunction destroy);
+
+typedef void (* GDBusDestroyFunction) (void *user_data);
+
+typedef DBusMessage * (* GDBusMethodFunction) (DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+typedef guint32 GDBusPendingReply;
+
+typedef void (* GDBusSecurityFunction) (DBusConnection *connection,
+                                               const char *action,
+                                               gboolean interaction,
+                                               GDBusPendingReply pending);
+
+typedef enum {
+       G_DBUS_METHOD_FLAG_DEPRECATED = (1 << 0),
+       G_DBUS_METHOD_FLAG_NOREPLY    = (1 << 1),
+       G_DBUS_METHOD_FLAG_ASYNC      = (1 << 2),
+} GDBusMethodFlags;
+
+typedef enum {
+       G_DBUS_SIGNAL_FLAG_DEPRECATED = (1 << 0),
+} GDBusSignalFlags;
+
+typedef enum {
+       G_DBUS_PROPERTY_FLAG_DEPRECATED = (1 << 0),
+} GDBusPropertyFlags;
+
+typedef enum {
+       G_DBUS_SECURITY_FLAG_DEPRECATED        = (1 << 0),
+       G_DBUS_SECURITY_FLAG_BUILTIN           = (1 << 1),
+       G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION = (1 << 2),
+} GDBusSecurityFlags;
+
+typedef struct {
+       const char *name;
+       const char *signature;
+} GDBusArgInfo;
+
+typedef struct {
+       const char *name;
+       GDBusMethodFunction function;
+       GDBusMethodFlags flags;
+       unsigned int privilege;
+       const GDBusArgInfo *in_args;
+       const GDBusArgInfo *out_args;
+} GDBusMethodTable;
+
+typedef struct {
+       const char *name;
+       GDBusSignalFlags flags;
+       const GDBusArgInfo *args;
+} GDBusSignalTable;
+
+typedef struct {
+       const char *name;
+       const char *type;
+       GDBusPropertyFlags flags;
+} GDBusPropertyTable;
+
+typedef struct {
+       unsigned int privilege;
+       const char *action;
+       GDBusSecurityFlags flags;
+       GDBusSecurityFunction function;
+} GDBusSecurityTable;
+
+#define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } }
+
+#define GDBUS_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function
+
+#define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_ASYNC
+
+#define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \
+       .name = _name, \
+       .in_args = _in_args, \
+       .out_args = _out_args, \
+       .function = _function, \
+       .flags = G_DBUS_METHOD_FLAG_NOREPLY
+
+#define GDBUS_SIGNAL(_name, _args) \
+       .name = _name, \
+       .args = _args
+
+#define GDBUS_DEPRECATED_SIGNAL(_name, _args) \
+       .name = _name, \
+       .args = _args, \
+       .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+                                       const char *path, const char *name,
+                                       const GDBusMethodTable *methods,
+                                       const GDBusSignalTable *signals,
+                                       const GDBusPropertyTable *properties,
+                                       void *user_data,
+                                       GDBusDestroyFunction destroy);
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+                                       const char *path, const char *name);
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security);
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security);
+
+void g_dbus_pending_success(DBusConnection *connection,
+                                       GDBusPendingReply pending);
+void g_dbus_pending_error(DBusConnection *connection,
+                               GDBusPendingReply pending,
+                               const char *name, const char *format, ...)
+                                       __attribute__((format(printf, 4, 5)));
+void g_dbus_pending_error_valist(DBusConnection *connection,
+                               GDBusPendingReply pending, const char *name,
+                                       const char *format, va_list args);
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+                                               const char *format, ...)
+                                       __attribute__((format(printf, 3, 4)));
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+                                       const char *format, va_list args);
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...);
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+                                               int type, va_list args);
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message);
+gboolean g_dbus_send_reply(DBusConnection *connection,
+                               DBusMessage *message, int type, ...);
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+                               DBusMessage *message, int type, va_list args);
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, ...);
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, va_list args);
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction connect,
+                               GDBusWatchFunction disconnect,
+                               void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction function,
+                               void *user_data, GDBusDestroyFunction destroy);
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+                               const char *sender, const char *path,
+                               const char *interface, const char *member,
+                               GDBusSignalFunction function, void *user_data,
+                               GDBusDestroyFunction destroy);
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint tag);
+void g_dbus_remove_all_watches(DBusConnection *connection);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GDBUS_H */
diff --git a/gdbus/mainloop.c b/gdbus/mainloop.c
new file mode 100644 (file)
index 0000000..cff326f
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define DISPATCH_TIMEOUT  0
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct timeout_handler {
+       guint id;
+       DBusTimeout *timeout;
+};
+
+struct watch_info {
+       guint id;
+       DBusWatch *watch;
+       DBusConnection *conn;
+};
+
+struct disconnect_data {
+       GDBusWatchFunction function;
+       void *user_data;
+};
+
+static gboolean disconnected_signal(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct disconnect_data *dc_data = data;
+
+       error("Got disconnected from the system message bus");
+
+       dc_data->function(conn, dc_data->user_data);
+
+       dbus_connection_unref(conn);
+
+       return TRUE;
+}
+
+static gboolean message_dispatch(void *data)
+{
+       DBusConnection *conn = data;
+
+       dbus_connection_ref(conn);
+
+       /* Dispatch messages */
+       while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS);
+
+       dbus_connection_unref(conn);
+
+       return FALSE;
+}
+
+static inline void queue_dispatch(DBusConnection *conn,
+                                               DBusDispatchStatus status)
+{
+       if (status == DBUS_DISPATCH_DATA_REMAINS)
+               g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn);
+}
+
+static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct watch_info *info = data;
+       unsigned int flags = 0;
+       DBusDispatchStatus status;
+
+       dbus_connection_ref(info->conn);
+
+       if (cond & G_IO_IN)  flags |= DBUS_WATCH_READABLE;
+       if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
+       if (cond & G_IO_HUP) flags |= DBUS_WATCH_HANGUP;
+       if (cond & G_IO_ERR) flags |= DBUS_WATCH_ERROR;
+
+       dbus_watch_handle(info->watch, flags);
+
+       status = dbus_connection_get_dispatch_status(info->conn);
+       queue_dispatch(info->conn, status);
+
+       dbus_connection_unref(info->conn);
+
+       return TRUE;
+}
+
+static void watch_info_free(void *data)
+{
+       struct watch_info *info = data;
+
+       if (info->id > 0) {
+               g_source_remove(info->id);
+               info->id = 0;
+       }
+
+       dbus_connection_unref(info->conn);
+
+       g_free(info);
+}
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+       DBusConnection *conn = data;
+       GIOCondition cond = G_IO_HUP | G_IO_ERR;
+       GIOChannel *chan;
+       struct watch_info *info;
+       unsigned int flags;
+       int fd;
+
+       if (!dbus_watch_get_enabled(watch))
+               return TRUE;
+
+       info = g_new0(struct watch_info, 1);
+
+       fd = dbus_watch_get_unix_fd(watch);
+       chan = g_io_channel_unix_new(fd);
+
+       info->watch = watch;
+       info->conn = dbus_connection_ref(conn);
+
+       dbus_watch_set_data(watch, info, watch_info_free);
+
+       flags = dbus_watch_get_flags(watch);
+
+       if (flags & DBUS_WATCH_READABLE) cond |= G_IO_IN;
+       if (flags & DBUS_WATCH_WRITABLE) cond |= G_IO_OUT;
+
+       info->id = g_io_add_watch(chan, cond, watch_func, info);
+
+       g_io_channel_unref(chan);
+
+       return TRUE;
+}
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+       if (dbus_watch_get_enabled(watch))
+               return;
+
+       /* will trigger watch_info_free() */
+       dbus_watch_set_data(watch, NULL, NULL);
+}
+
+static void watch_toggled(DBusWatch *watch, void *data)
+{
+       /* Because we just exit on OOM, enable/disable is
+        * no different from add/remove */
+       if (dbus_watch_get_enabled(watch))
+               add_watch(watch, data);
+       else
+               remove_watch(watch, data);
+}
+
+static gboolean timeout_handler_dispatch(gpointer data)
+{
+       struct timeout_handler *handler = data;
+
+       handler->id = 0;
+
+       /* if not enabled should not be polled by the main loop */
+       if (!dbus_timeout_get_enabled(handler->timeout))
+               return FALSE;
+
+       dbus_timeout_handle(handler->timeout);
+
+       return FALSE;
+}
+
+static void timeout_handler_free(void *data)
+{
+       struct timeout_handler *handler = data;
+
+       if (handler->id > 0) {
+               g_source_remove(handler->id);
+               handler->id = 0;
+       }
+
+       g_free(handler);
+}
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+       int interval = dbus_timeout_get_interval(timeout);
+       struct timeout_handler *handler;
+
+       if (!dbus_timeout_get_enabled(timeout))
+               return TRUE;
+
+       handler = g_new0(struct timeout_handler, 1);
+
+       handler->timeout = timeout;
+
+       dbus_timeout_set_data(timeout, handler, timeout_handler_free);
+
+       handler->id = g_timeout_add(interval, timeout_handler_dispatch,
+                                                               handler);
+
+       return TRUE;
+}
+
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+       /* will trigger timeout_handler_free() */
+       dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+static void timeout_toggled(DBusTimeout *timeout, void *data)
+{
+       if (dbus_timeout_get_enabled(timeout))
+               add_timeout(timeout, data);
+       else
+               remove_timeout(timeout, data);
+}
+
+static void dispatch_status(DBusConnection *conn,
+                                       DBusDispatchStatus status, void *data)
+{
+       if (!dbus_connection_get_is_connected(conn))
+               return;
+
+       queue_dispatch(conn, status);
+}
+
+static inline void setup_dbus_with_main_loop(DBusConnection *conn)
+{
+       dbus_connection_set_watch_functions(conn, add_watch, remove_watch,
+                                               watch_toggled, conn, NULL);
+
+       dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
+                                               timeout_toggled, NULL, NULL);
+
+       dbus_connection_set_dispatch_status_function(conn, dispatch_status,
+                                                               NULL, NULL);
+}
+
+static gboolean setup_bus(DBusConnection *conn, const char *name,
+                                               DBusError *error)
+{
+       gboolean result;
+       DBusDispatchStatus status;
+
+       if (name != NULL) {
+               result = g_dbus_request_name(conn, name, error);
+
+               if (error != NULL) {
+                       if (dbus_error_is_set(error) == TRUE)
+                               return FALSE;
+               }
+
+               if (result == FALSE)
+                       return FALSE;
+       }
+
+       setup_dbus_with_main_loop(conn);
+
+       status = dbus_connection_get_dispatch_status(conn);
+       queue_dispatch(conn, status);
+
+       return TRUE;
+}
+
+DBusConnection *g_dbus_setup_bus(DBusBusType type, const char *name,
+                                                       DBusError *error)
+{
+       DBusConnection *conn;
+
+       conn = dbus_bus_get(type, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return NULL;
+       }
+
+       if (conn == NULL)
+               return NULL;
+
+       if (setup_bus(conn, name, error) == FALSE) {
+               dbus_connection_unref(conn);
+               return NULL;
+       }
+
+       return conn;
+}
+
+DBusConnection *g_dbus_setup_private(DBusBusType type, const char *name,
+                                                       DBusError *error)
+{
+       DBusConnection *conn;
+
+       conn = dbus_bus_get_private(type, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return NULL;
+       }
+
+       if (conn == NULL)
+               return NULL;
+
+       if (setup_bus(conn, name, error) == FALSE) {
+               dbus_connection_unref(conn);
+               return NULL;
+       }
+
+       return conn;
+}
+
+gboolean g_dbus_request_name(DBusConnection *connection, const char *name,
+                                                       DBusError *error)
+{
+       int result;
+
+       result = dbus_bus_request_name(connection, name,
+                                       DBUS_NAME_FLAG_DO_NOT_QUEUE, error);
+
+       if (error != NULL) {
+               if (dbus_error_is_set(error) == TRUE)
+                       return FALSE;
+       }
+
+       if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+               if (error != NULL)
+                       dbus_set_error(error, name, "Name already in use");
+
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean g_dbus_set_disconnect_function(DBusConnection *connection,
+                               GDBusWatchFunction function,
+                               void *user_data, DBusFreeFunction destroy)
+{
+       struct disconnect_data *dc_data;
+
+       dc_data = g_new0(struct disconnect_data, 1);
+
+       dc_data->function = function;
+       dc_data->user_data = user_data;
+
+       dbus_connection_set_exit_on_disconnect(connection, FALSE);
+
+       if (g_dbus_add_signal_watch(connection, NULL, NULL,
+                               DBUS_INTERFACE_LOCAL, "Disconnected",
+                               disconnected_signal, dc_data, g_free) == 0) {
+               error("Failed to add watch for D-Bus Disconnected signal");
+               g_free(dc_data);
+               return FALSE;
+       }
+
+       return TRUE;
+}
diff --git a/gdbus/object.c b/gdbus/object.c
new file mode 100644 (file)
index 0000000..900e7ab
--- /dev/null
@@ -0,0 +1,858 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+struct generic_data {
+       unsigned int refcount;
+       GSList *interfaces;
+       char *introspect;
+};
+
+struct interface_data {
+       char *name;
+       const GDBusMethodTable *methods;
+       const GDBusSignalTable *signals;
+       const GDBusPropertyTable *properties;
+       void *user_data;
+       GDBusDestroyFunction destroy;
+};
+
+struct security_data {
+       GDBusPendingReply pending;
+       DBusMessage *message;
+       const GDBusMethodTable *method;
+       void *iface_user_data;
+};
+
+static void print_arguments(GString *gstr, const GDBusArgInfo *args,
+                                               const char *direction)
+{
+       for (; args && args->name; args++) {
+               g_string_append_printf(gstr,
+                                       "\t\t\t<arg name=\"%s\" type=\"%s\"",
+                                       args->name, args->signature);
+
+               if (direction)
+                       g_string_append_printf(gstr,
+                                       " direction=\"%s\"/>\n", direction);
+               else
+                       g_string_append_printf(gstr, "/>\n");
+
+       }
+}
+
+static void generate_interface_xml(GString *gstr, struct interface_data *iface)
+{
+       const GDBusMethodTable *method;
+       const GDBusSignalTable *signal;
+
+       for (method = iface->methods; method && method->name; method++) {
+               gboolean deprecated = method->flags &
+                                               G_DBUS_METHOD_FLAG_DEPRECATED;
+               gboolean noreply = method->flags &
+                                               G_DBUS_METHOD_FLAG_NOREPLY;
+
+               if (!deprecated && !noreply &&
+                               !(method->in_args && method->in_args->name) &&
+                               !(method->out_args && method->out_args->name))
+                       g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
+                                                               method->name);
+               else {
+                       g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
+                                                               method->name);
+                       print_arguments(gstr, method->in_args, "in");
+                       print_arguments(gstr, method->out_args, "out");
+
+                       if (deprecated)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n");
+
+                       if (noreply)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n");
+
+                       g_string_append_printf(gstr, "\t\t</method>\n");
+               }
+       }
+
+       for (signal = iface->signals; signal && signal->name; signal++) {
+               gboolean deprecated = signal->flags &
+                                               G_DBUS_SIGNAL_FLAG_DEPRECATED;
+
+               if (!deprecated && !(signal->args && signal->args->name))
+                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
+                                                               signal->name);
+               else {
+                       g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
+                                                               signal->name);
+                       print_arguments(gstr, signal->args, NULL);
+
+                       if (deprecated)
+                               g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n");
+
+                       g_string_append_printf(gstr, "\t\t</signal>\n");
+               }
+       }
+}
+
+static void generate_introspection_xml(DBusConnection *conn,
+                               struct generic_data *data, const char *path)
+{
+       GSList *list;
+       GString *gstr;
+       char **children;
+       int i;
+
+       g_free(data->introspect);
+
+       gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+
+       g_string_append_printf(gstr, "<node>\n");
+
+       for (list = data->interfaces; list; list = list->next) {
+               struct interface_data *iface = list->data;
+
+               g_string_append_printf(gstr, "\t<interface name=\"%s\">\n",
+                                                               iface->name);
+
+               generate_interface_xml(gstr, iface);
+
+               g_string_append_printf(gstr, "\t</interface>\n");
+       }
+
+       if (!dbus_connection_list_registered(conn, path, &children))
+               goto done;
+
+       for (i = 0; children[i]; i++)
+               g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n",
+                                                               children[i]);
+
+       dbus_free_string_array(children);
+
+done:
+       g_string_append_printf(gstr, "</node>\n");
+
+       data->introspect = g_string_free(gstr, FALSE);
+}
+
+static DBusMessage *introspect(DBusConnection *connection,
+                               DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       DBusMessage *reply;
+
+       if (data->introspect == NULL)
+               generate_introspection_xml(connection, data,
+                                               dbus_message_get_path(message));
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
+                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusHandlerResult process_message(DBusConnection *connection,
+                       DBusMessage *message, const GDBusMethodTable *method,
+                                                       void *iface_user_data)
+{
+       DBusMessage *reply;
+
+       reply = method->function(connection, message, iface_user_data);
+
+       if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
+               if (reply != NULL)
+                       dbus_message_unref(reply);
+               return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
+               if (reply == NULL)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+       }
+
+       if (reply == NULL)
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+       dbus_connection_send(connection, reply, NULL);
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static GDBusPendingReply next_pending = 1;
+static GSList *pending_security = NULL;
+
+static const GDBusSecurityTable *security_table = NULL;
+
+void g_dbus_pending_success(DBusConnection *connection,
+                                       GDBusPendingReply pending)
+{
+       GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+               struct security_data *secdata = list->data;
+
+               if (secdata->pending != pending)
+                       continue;
+
+               pending_security = g_slist_remove(pending_security, secdata);
+
+               process_message(connection, secdata->message,
+                               secdata->method, secdata->iface_user_data);
+
+               dbus_message_unref(secdata->message);
+               g_free(secdata);
+               return;
+        }
+}
+
+void g_dbus_pending_error_valist(DBusConnection *connection,
+                               GDBusPendingReply pending, const char *name,
+                                       const char *format, va_list args)
+{
+       GSList *list;
+
+        for (list = pending_security; list; list = list->next) {
+               struct security_data *secdata = list->data;
+               DBusMessage *reply;
+
+               if (secdata->pending != pending)
+                       continue;
+
+               pending_security = g_slist_remove(pending_security, secdata);
+
+               reply = g_dbus_create_error_valist(secdata->message,
+                                                       name, format, args);
+               if (reply != NULL) {
+                       dbus_connection_send(connection, reply, NULL);
+                       dbus_message_unref(reply);
+               }
+
+               dbus_message_unref(secdata->message);
+               g_free(secdata);
+               return;
+        }
+}
+
+void g_dbus_pending_error(DBusConnection *connection,
+                               GDBusPendingReply pending,
+                               const char *name, const char *format, ...)
+{
+       va_list args;
+
+       va_start(args, format);
+
+       g_dbus_pending_error_valist(connection, pending, name, format, args);
+
+       va_end(args);
+}
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout);
+
+struct builtin_security_data {
+       DBusConnection *conn;
+       GDBusPendingReply pending;
+};
+
+static void builtin_security_result(dbus_bool_t authorized, void *user_data)
+{
+       struct builtin_security_data *data = user_data;
+
+       if (authorized == TRUE)
+               g_dbus_pending_success(data->conn, data->pending);
+       else
+               g_dbus_pending_error(data->conn, data->pending,
+                                               DBUS_ERROR_AUTH_FAILED, NULL);
+
+       g_free(data);
+}
+
+static void builtin_security_function(DBusConnection *conn,
+                                               const char *action,
+                                               gboolean interaction,
+                                               GDBusPendingReply pending)
+{
+       struct builtin_security_data *data;
+
+       data = g_new0(struct builtin_security_data, 1);
+       data->conn = conn;
+       data->pending = pending;
+
+       if (polkit_check_authorization(conn, action, interaction,
+                               builtin_security_result, data, 30000) < 0)
+               g_dbus_pending_error(conn, pending, NULL, NULL);
+}
+
+static gboolean check_privilege(DBusConnection *conn, DBusMessage *msg,
+                       const GDBusMethodTable *method, void *iface_user_data)
+{
+       const GDBusSecurityTable *security;
+
+       for (security = security_table; security && security->privilege;
+                                                               security++) {
+               struct security_data *secdata;
+               gboolean interaction;
+
+               if (security->privilege != method->privilege)
+                       continue;
+
+               secdata = g_new(struct security_data, 1);
+               secdata->pending = next_pending++;
+               secdata->message = dbus_message_ref(msg);
+               secdata->method = method;
+               secdata->iface_user_data = iface_user_data;
+
+               pending_security = g_slist_prepend(pending_security, secdata);
+
+               if (security->flags & G_DBUS_SECURITY_FLAG_ALLOW_INTERACTION)
+                       interaction = TRUE;
+               else
+                       interaction = FALSE;
+
+               if (!(security->flags & G_DBUS_SECURITY_FLAG_BUILTIN) &&
+                                                       security->function)
+                       security->function(conn, security->action,
+                                               interaction, secdata->pending);
+               else
+                       builtin_security_function(conn, security->action,
+                                               interaction, secdata->pending);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void generic_unregister(DBusConnection *connection, void *user_data)
+{
+       struct generic_data *data = user_data;
+
+       g_free(data->introspect);
+       g_free(data);
+}
+
+static struct interface_data *find_interface(GSList *interfaces,
+                                               const char *name)
+{
+       GSList *list;
+
+       if (name == NULL)
+               return NULL;
+
+       for (list = interfaces; list; list = list->next) {
+               struct interface_data *iface = list->data;
+               if (!strcmp(name, iface->name))
+                       return iface;
+       }
+
+       return NULL;
+}
+
+static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args,
+                                                       DBusMessage *message)
+{
+       const char *sig = dbus_message_get_signature(message);
+       const char *p = NULL;
+
+       for (; args && args->signature && *sig; args++) {
+               p = args->signature;
+
+               for (; *sig && *p; sig++, p++) {
+                       if (*p != *sig)
+                               return FALSE;
+               }
+       }
+
+       if (*sig || (p && *p) || (args && args->signature))
+               return FALSE;
+
+       return TRUE;
+}
+
+static DBusHandlerResult generic_message(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct generic_data *data = user_data;
+       struct interface_data *iface;
+       const GDBusMethodTable *method;
+       const char *interface;
+
+       interface = dbus_message_get_interface(message);
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       for (method = iface->methods; method &&
+                       method->name && method->function; method++) {
+               if (dbus_message_is_method_call(message, iface->name,
+                                                       method->name) == FALSE)
+                       continue;
+
+               if (g_dbus_args_have_signature(method->in_args,
+                                                       message) == FALSE)
+                       continue;
+
+               if (check_privilege(connection, message, method,
+                                               iface->user_data) == TRUE)
+                       return DBUS_HANDLER_RESULT_HANDLED;
+
+               return process_message(connection, message, method,
+                                                       iface->user_data);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable generic_table = {
+       .unregister_function    = generic_unregister,
+       .message_function       = generic_message,
+};
+
+static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
+{
+       struct generic_data *data = NULL;
+       char *parent_path, *slash;
+
+       parent_path = g_strdup(child_path);
+       slash = strrchr(parent_path, '/');
+       if (slash == NULL)
+               goto done;
+
+       if (slash == parent_path && parent_path[1] != '\0')
+               parent_path[1] = '\0';
+       else
+               *slash = '\0';
+
+       if (!strlen(parent_path))
+               goto done;
+
+       if (dbus_connection_get_object_path_data(conn, parent_path,
+                                                       (void *) &data) == FALSE) {
+               goto done;
+       }
+
+       invalidate_parent_data(conn, parent_path);
+
+       if (data == NULL)
+               goto done;
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+done:
+       g_free(parent_path);
+}
+
+static const GDBusMethodTable introspect_methods[] = {
+       { GDBUS_METHOD("Introspect", NULL,
+                       GDBUS_ARGS({ "xml", "s" }), introspect) },
+       { }
+};
+
+static void add_interface(struct generic_data *data, const char *name,
+                               const GDBusMethodTable *methods,
+                               const GDBusSignalTable *signals,
+                               const GDBusPropertyTable *properties,
+                               void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct interface_data *iface;
+
+       iface = g_new0(struct interface_data, 1);
+       iface->name = g_strdup(name);
+       iface->methods = methods;
+       iface->signals = signals;
+       iface->properties = properties;
+       iface->user_data = user_data;
+       iface->destroy = destroy;
+
+       data->interfaces = g_slist_append(data->interfaces, iface);
+}
+
+static struct generic_data *object_path_ref(DBusConnection *connection,
+                                                       const char *path)
+{
+       struct generic_data *data;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == TRUE) {
+               if (data != NULL) {
+                       data->refcount++;
+                       return data;
+               }
+       }
+
+       data = g_new0(struct generic_data, 1);
+       data->refcount = 1;
+
+       data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
+
+       if (!dbus_connection_register_object_path(connection, path,
+                                               &generic_table, data)) {
+               g_free(data->introspect);
+               g_free(data);
+               return NULL;
+       }
+
+       invalidate_parent_data(connection, path);
+
+       add_interface(data, DBUS_INTERFACE_INTROSPECTABLE,
+                       introspect_methods, NULL, NULL, data, NULL);
+
+       return data;
+}
+
+static gboolean remove_interface(struct generic_data *data, const char *name)
+{
+       struct interface_data *iface;
+
+       iface = find_interface(data->interfaces, name);
+       if (iface == NULL)
+               return FALSE;
+
+       data->interfaces = g_slist_remove(data->interfaces, iface);
+
+       if (iface->destroy)
+               iface->destroy(iface->user_data);
+
+       g_free(iface->name);
+       g_free(iface);
+
+       return TRUE;
+}
+
+static void object_path_unref(DBusConnection *connection, const char *path)
+{
+       struct generic_data *data = NULL;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == FALSE)
+               return;
+
+       if (data == NULL)
+               return;
+
+       data->refcount--;
+
+       if (data->refcount > 0)
+               return;
+
+       remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
+
+       invalidate_parent_data(connection, path);
+
+       dbus_connection_unregister_object_path(connection, path);
+}
+
+static gboolean check_signal(DBusConnection *conn, const char *path,
+                               const char *interface, const char *name,
+                               const GDBusArgInfo **args)
+{
+       struct generic_data *data = NULL;
+       struct interface_data *iface;
+       const GDBusSignalTable *signal;
+
+       *args = NULL;
+       if (!dbus_connection_get_object_path_data(conn, path,
+                                       (void *) &data) || data == NULL) {
+               error("dbus_connection_emit_signal: path %s isn't registered",
+                               path);
+               return FALSE;
+       }
+
+       iface = find_interface(data->interfaces, interface);
+       if (iface == NULL) {
+               error("dbus_connection_emit_signal: %s does not implement %s",
+                               path, interface);
+               return FALSE;
+       }
+
+       for (signal = iface->signals; signal && signal->name; signal++) {
+               if (!strcmp(signal->name, name)) {
+                       *args = signal->args;
+                       return TRUE;
+               }
+       }
+
+       error("No signal named %s on interface %s", name, interface);
+       return FALSE;
+}
+
+static dbus_bool_t emit_signal_valist(DBusConnection *conn,
+                                               const char *path,
+                                               const char *interface,
+                                               const char *name,
+                                               int first,
+                                               va_list var_args)
+{
+       DBusMessage *signal;
+       dbus_bool_t ret;
+       const GDBusArgInfo *args;
+
+       if (!check_signal(conn, path, interface, name, &args))
+               return FALSE;
+
+       signal = dbus_message_new_signal(path, interface, name);
+       if (signal == NULL) {
+               error("Unable to allocate new %s.%s signal", interface,  name);
+               return FALSE;
+       }
+
+       ret = dbus_message_append_args_valist(signal, first, var_args);
+       if (!ret)
+               goto fail;
+
+       if (g_dbus_args_have_signature(args, signal) == FALSE) {
+               error("%s.%s: expected signature'%s' but got '%s'",
+                               interface, name, args, signature);
+               ret = FALSE;
+               goto fail;
+       }
+
+       ret = dbus_connection_send(conn, signal, NULL);
+
+fail:
+       dbus_message_unref(signal);
+
+       return ret;
+}
+
+gboolean g_dbus_register_interface(DBusConnection *connection,
+                                       const char *path, const char *name,
+                                       const GDBusMethodTable *methods,
+                                       const GDBusSignalTable *signals,
+                                       const GDBusPropertyTable *properties,
+                                       void *user_data,
+                                       GDBusDestroyFunction destroy)
+{
+       struct generic_data *data;
+
+       data = object_path_ref(connection, path);
+       if (data == NULL)
+               return FALSE;
+
+       if (find_interface(data->interfaces, name)) {
+               object_path_unref(connection, path);
+               return FALSE;
+       }
+
+       add_interface(data, name, methods, signals,
+                       properties, user_data, destroy);
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+       return TRUE;
+}
+
+gboolean g_dbus_unregister_interface(DBusConnection *connection,
+                                       const char *path, const char *name)
+{
+       struct generic_data *data = NULL;
+
+       if (path == NULL)
+               return FALSE;
+
+       if (dbus_connection_get_object_path_data(connection, path,
+                                               (void *) &data) == FALSE)
+               return FALSE;
+
+       if (data == NULL)
+               return FALSE;
+
+       if (remove_interface(data, name) == FALSE)
+               return FALSE;
+
+       g_free(data->introspect);
+       data->introspect = NULL;
+
+       object_path_unref(connection, path);
+
+       return TRUE;
+}
+
+gboolean g_dbus_register_security(const GDBusSecurityTable *security)
+{
+       if (security_table != NULL)
+               return FALSE;
+
+       security_table = security;
+
+       return TRUE;
+}
+
+gboolean g_dbus_unregister_security(const GDBusSecurityTable *security)
+{
+       security_table = NULL;
+
+       return TRUE;
+}
+
+DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
+                                       const char *format, va_list args)
+{
+       char str[1024];
+
+       vsnprintf(str, sizeof(str), format, args);
+
+       return dbus_message_new_error(message, name, str);
+}
+
+DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
+                                               const char *format, ...)
+{
+       va_list args;
+       DBusMessage *reply;
+
+       va_start(args, format);
+
+       reply = g_dbus_create_error_valist(message, name, format, args);
+
+       va_end(args);
+
+       return reply;
+}
+
+DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
+                                               int type, va_list args)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       return reply;
+}
+
+DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
+{
+       va_list args;
+       DBusMessage *reply;
+
+       va_start(args, type);
+
+       reply = g_dbus_create_reply_valist(message, type, args);
+
+       va_end(args);
+
+       return reply;
+}
+
+gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
+{
+       dbus_bool_t result;
+
+       if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+               dbus_message_set_no_reply(message, TRUE);
+
+       result = dbus_connection_send(connection, message, NULL);
+
+       dbus_message_unref(message);
+
+       return result;
+}
+
+gboolean g_dbus_send_reply_valist(DBusConnection *connection,
+                               DBusMessage *message, int type, va_list args)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return FALSE;
+
+       if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
+               dbus_message_unref(reply);
+               return FALSE;
+       }
+
+       return g_dbus_send_message(connection, reply);
+}
+
+gboolean g_dbus_send_reply(DBusConnection *connection,
+                               DBusMessage *message, int type, ...)
+{
+       va_list args;
+       gboolean result;
+
+       va_start(args, type);
+
+       result = g_dbus_send_reply_valist(connection, message, type, args);
+
+       va_end(args);
+
+       return result;
+}
+
+gboolean g_dbus_emit_signal(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, ...)
+{
+       va_list args;
+       gboolean result;
+
+       va_start(args, type);
+
+       result = emit_signal_valist(connection, path, interface,
+                                                       name, type, args);
+
+       va_end(args);
+
+       return result;
+}
+
+gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
+                               const char *path, const char *interface,
+                               const char *name, int type, va_list args)
+{
+       return emit_signal_valist(connection, path, interface,
+                                                       name, type, args);
+}
diff --git a/gdbus/polkit.c b/gdbus/polkit.c
new file mode 100644 (file)
index 0000000..9e95fa3
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <dbus/dbus.h>
+
+#include <glib.h>
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout);
+
+static void add_dict_with_string_value(DBusMessageIter *iter,
+                                       const char *key, const char *str)
+{
+       DBusMessageIter dict, entry, value;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+       dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+                                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+                                       DBUS_TYPE_STRING_AS_STRING, &value);
+       dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &str);
+       dbus_message_iter_close_container(&entry, &value);
+
+       dbus_message_iter_close_container(&dict, &entry);
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_empty_string_dict(DBusMessageIter *iter)
+{
+       DBusMessageIter dict;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void add_arguments(DBusConnection *conn, DBusMessageIter *iter,
+                               const char *action, dbus_uint32_t flags)
+{
+       const char *busname = dbus_bus_get_unique_name(conn);
+       const char *kind = "system-bus-name";
+       const char *cancel = "";
+       DBusMessageIter subject;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+                                                       NULL, &subject);
+       dbus_message_iter_append_basic(&subject, DBUS_TYPE_STRING, &kind);
+       add_dict_with_string_value(&subject, "name", busname);
+       dbus_message_iter_close_container(iter, &subject);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &action);
+       add_empty_string_dict(iter);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &flags);
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &cancel);
+}
+
+static dbus_bool_t parse_result(DBusMessageIter *iter)
+{
+       DBusMessageIter result;
+       dbus_bool_t authorized, challenge;
+
+       dbus_message_iter_recurse(iter, &result);
+
+       dbus_message_iter_get_basic(&result, &authorized);
+       dbus_message_iter_get_basic(&result, &challenge);
+
+       return authorized;
+}
+
+struct authorization_data {
+       void (*function) (dbus_bool_t authorized, void *user_data);
+       void *user_data;
+};
+
+static void authorization_reply(DBusPendingCall *call, void *user_data)
+{
+       struct authorization_data *data = user_data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       dbus_bool_t authorized = FALSE;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
+               goto done;
+
+       if (dbus_message_has_signature(reply, "(bba{ss})") == FALSE)
+               goto done;
+
+       dbus_message_iter_init(reply, &iter);
+
+       authorized = parse_result(&iter);
+
+done:
+       if (data->function != NULL)
+               data->function(authorized, data->user_data);
+
+       dbus_message_unref(reply);
+
+       dbus_pending_call_unref(call);
+}
+
+#define AUTHORITY_DBUS "org.freedesktop.PolicyKit1"
+#define AUTHORITY_INTF "org.freedesktop.PolicyKit1.Authority"
+#define AUTHORITY_PATH "/org/freedesktop/PolicyKit1/Authority"
+
+int polkit_check_authorization(DBusConnection *conn,
+                               const char *action, gboolean interaction,
+                               void (*function) (dbus_bool_t authorized,
+                                                       void *user_data),
+                                               void *user_data, int timeout)
+{
+       struct authorization_data *data;
+       DBusMessage *msg;
+       DBusMessageIter iter;
+       DBusPendingCall *call;
+       dbus_uint32_t flags = 0x00000000;
+
+       if (conn == NULL)
+               return -EINVAL;
+
+       data = dbus_malloc0(sizeof(*data));
+       if (data == NULL)
+               return -ENOMEM;
+
+       msg = dbus_message_new_method_call(AUTHORITY_DBUS, AUTHORITY_PATH,
+                               AUTHORITY_INTF, "CheckAuthorization");
+       if (msg == NULL) {
+               dbus_free(data);
+               return -ENOMEM;
+       }
+
+       if (interaction == TRUE)
+               flags |= 0x00000001;
+
+       if (action == NULL)
+               action = "org.freedesktop.policykit.exec";
+
+       dbus_message_iter_init_append(msg, &iter);
+       add_arguments(conn, &iter, action, flags);
+
+       if (dbus_connection_send_with_reply(conn, msg,
+                                               &call, timeout) == FALSE) {
+               dbus_message_unref(msg);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       if (call == NULL) {
+               dbus_message_unref(msg);
+               dbus_free(data);
+               return -EIO;
+       }
+
+       data->function = function;
+       data->user_data = user_data;
+
+       dbus_pending_call_set_notify(call, authorization_reply,
+                                                       data, dbus_free);
+
+       dbus_message_unref(msg);
+
+       return 0;
+}
diff --git a/gdbus/watch.c b/gdbus/watch.c
new file mode 100644 (file)
index 0000000..9a716b0
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ *
+ *  D-Bus helper library
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "gdbus.h"
+
+#define info(fmt...)
+#define error(fmt...)
+#define debug(fmt...)
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data);
+
+static guint listener_id = 0;
+static GSList *listeners = NULL;
+
+struct service_data {
+       DBusConnection *conn;
+       DBusPendingCall *call;
+       char *name;
+       const char *owner;
+       guint id;
+       struct filter_callback *callback;
+};
+
+struct filter_callback {
+       GDBusWatchFunction conn_func;
+       GDBusWatchFunction disc_func;
+       GDBusSignalFunction signal_func;
+       GDBusDestroyFunction destroy_func;
+       struct service_data *data;
+       void *user_data;
+       guint id;
+};
+
+struct filter_data {
+       DBusConnection *connection;
+       DBusHandleMessageFunction handle_func;
+       char *name;
+       char *owner;
+       char *path;
+       char *interface;
+       char *member;
+       char *argument;
+       GSList *callbacks;
+       GSList *processed;
+       guint name_watch;
+       gboolean lock;
+       gboolean registered;
+};
+
+static struct filter_data *filter_data_find(DBusConnection *connection,
+                                                       const char *name,
+                                                       const char *owner,
+                                                       const char *path,
+                                                       const char *interface,
+                                                       const char *member,
+                                                       const char *argument)
+{
+       GSList *current;
+
+       for (current = listeners;
+                       current != NULL; current = current->next) {
+               struct filter_data *data = current->data;
+
+               if (connection != data->connection)
+                       continue;
+
+               if (name && data->name &&
+                               g_str_equal(name, data->name) == FALSE)
+                       continue;
+
+               if (owner && data->owner &&
+                               g_str_equal(owner, data->owner) == FALSE)
+                       continue;
+
+               if (path && data->path &&
+                               g_str_equal(path, data->path) == FALSE)
+                       continue;
+
+               if (interface && data->interface &&
+                               g_str_equal(interface, data->interface) == FALSE)
+                       continue;
+
+               if (member && data->member &&
+                               g_str_equal(member, data->member) == FALSE)
+                       continue;
+
+               if (argument && data->argument &&
+                               g_str_equal(argument, data->argument) == FALSE)
+                       continue;
+
+               return data;
+       }
+
+       return NULL;
+}
+
+static void format_rule(struct filter_data *data, char *rule, size_t size)
+{
+       const char *sender;
+       int offset;
+
+       offset = snprintf(rule, size, "type='signal'");
+       sender = data->name ? : data->owner;
+
+       if (sender)
+               offset += snprintf(rule + offset, size - offset,
+                               ",sender='%s'", sender);
+       if (data->path)
+               offset += snprintf(rule + offset, size - offset,
+                               ",path='%s'", data->path);
+       if (data->interface)
+               offset += snprintf(rule + offset, size - offset,
+                               ",interface='%s'", data->interface);
+       if (data->member)
+               offset += snprintf(rule + offset, size - offset,
+                               ",member='%s'", data->member);
+       if (data->argument)
+               snprintf(rule + offset, size - offset,
+                               ",arg0='%s'", data->argument);
+}
+
+static gboolean add_match(struct filter_data *data,
+                               DBusHandleMessageFunction filter)
+{
+       DBusError err;
+       char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+       format_rule(data, rule, sizeof(rule));
+       dbus_error_init(&err);
+
+       dbus_bus_add_match(data->connection, rule, &err);
+       if (dbus_error_is_set(&err)) {
+               error("Adding match rule \"%s\" failed: %s", rule,
+                               err.message);
+               dbus_error_free(&err);
+               return FALSE;
+       }
+
+       data->handle_func = filter;
+       data->registered = TRUE;
+
+       return TRUE;
+}
+
+static gboolean remove_match(struct filter_data *data)
+{
+       DBusError err;
+       char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
+
+       format_rule(data, rule, sizeof(rule));
+
+       dbus_error_init(&err);
+
+       dbus_bus_remove_match(data->connection, rule, &err);
+       if (dbus_error_is_set(&err)) {
+               error("Removing owner match rule for %s failed: %s",
+                               rule, err.message);
+               dbus_error_free(&err);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static struct filter_data *filter_data_get(DBusConnection *connection,
+                                       DBusHandleMessageFunction filter,
+                                       const char *sender,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *member,
+                                       const char *argument)
+{
+       struct filter_data *data;
+       const char *name = NULL, *owner = NULL;
+
+       if (filter_data_find(connection, NULL, NULL, NULL, NULL, NULL, NULL) == NULL) {
+               if (!dbus_connection_add_filter(connection,
+                                       message_filter, NULL, NULL)) {
+                       error("dbus_connection_add_filter() failed");
+                       return NULL;
+               }
+       }
+
+       if (sender == NULL)
+               goto proceed;
+
+       if (sender[0] == ':')
+               owner = sender;
+       else
+               name = sender;
+
+proceed:
+       data = filter_data_find(connection, name, owner, path, interface,
+                                       member, argument);
+       if (data)
+               return data;
+
+       data = g_new0(struct filter_data, 1);
+
+       data->connection = dbus_connection_ref(connection);
+       data->name = name ? g_strdup(name) : NULL;
+       data->owner = owner ? g_strdup(owner) : NULL;
+       data->path = g_strdup(path);
+       data->interface = g_strdup(interface);
+       data->member = g_strdup(member);
+       data->argument = g_strdup(argument);
+
+       if (!add_match(data, filter)) {
+               g_free(data);
+               return NULL;
+       }
+
+       listeners = g_slist_append(listeners, data);
+
+       return data;
+}
+
+static struct filter_callback *filter_data_find_callback(
+                                               struct filter_data *data,
+                                               guint id)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->id == id)
+                       return cb;
+       }
+       for (l = data->processed; l; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->id == id)
+                       return cb;
+       }
+
+       return NULL;
+}
+
+static void filter_data_free(struct filter_data *data)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l != NULL; l = l->next)
+               g_free(l->data);
+
+       g_slist_free(data->callbacks);
+       g_dbus_remove_watch(data->connection, data->name_watch);
+       g_free(data->name);
+       g_free(data->owner);
+       g_free(data->path);
+       g_free(data->interface);
+       g_free(data->member);
+       g_free(data->argument);
+       dbus_connection_unref(data->connection);
+       g_free(data);
+}
+
+static void filter_data_call_and_free(struct filter_data *data)
+{
+       GSList *l;
+
+       for (l = data->callbacks; l != NULL; l = l->next) {
+               struct filter_callback *cb = l->data;
+               if (cb->disc_func)
+                       cb->disc_func(data->connection, cb->user_data);
+               if (cb->destroy_func)
+                       cb->destroy_func(cb->user_data);
+               g_free(cb);
+       }
+
+       filter_data_free(data);
+}
+
+static struct filter_callback *filter_data_add_callback(
+                                               struct filter_data *data,
+                                               GDBusWatchFunction connect,
+                                               GDBusWatchFunction disconnect,
+                                               GDBusSignalFunction signal,
+                                               GDBusDestroyFunction destroy,
+                                               void *user_data)
+{
+       struct filter_callback *cb = NULL;
+
+       cb = g_new0(struct filter_callback, 1);
+
+       cb->conn_func = connect;
+       cb->disc_func = disconnect;
+       cb->signal_func = signal;
+       cb->destroy_func = destroy;
+       cb->user_data = user_data;
+       cb->id = ++listener_id;
+
+       if (data->lock)
+               data->processed = g_slist_append(data->processed, cb);
+       else
+               data->callbacks = g_slist_append(data->callbacks, cb);
+
+       return cb;
+}
+
+static void service_data_free(struct service_data *data)
+{
+       struct filter_callback *callback = data->callback;
+
+       dbus_connection_unref(data->conn);
+
+       if (data->call)
+               dbus_pending_call_unref(data->call);
+
+       if (data->id)
+               g_source_remove(data->id);
+
+       g_free(data->name);
+       g_free(data);
+
+       callback->data = NULL;
+}
+
+static gboolean filter_data_remove_callback(struct filter_data *data,
+                                               struct filter_callback *cb)
+{
+       DBusConnection *connection;
+
+       data->callbacks = g_slist_remove(data->callbacks, cb);
+       data->processed = g_slist_remove(data->processed, cb);
+
+       /* Cancel pending operations */
+       if (cb->data) {
+               if (cb->data->call)
+                       dbus_pending_call_cancel(cb->data->call);
+               service_data_free(cb->data);
+       }
+
+       if (cb->destroy_func)
+               cb->destroy_func(cb->user_data);
+
+       g_free(cb);
+
+       /* Don't remove the filter if other callbacks exist or data is lock
+        * processing callbacks */
+       if (data->callbacks || data->lock)
+               return TRUE;
+
+       if (data->registered && !remove_match(data))
+               return FALSE;
+
+       connection = dbus_connection_ref(data->connection);
+       listeners = g_slist_remove(listeners, data);
+       filter_data_free(data);
+
+       /* Remove filter if there are no listeners left for the connection */
+       data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+                                       NULL);
+       if (data == NULL)
+               dbus_connection_remove_filter(connection, message_filter,
+                                               NULL);
+
+       dbus_connection_unref(connection);
+
+       return TRUE;
+}
+
+static DBusHandlerResult signal_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data = user_data;
+       struct filter_callback *cb;
+
+       while (data->callbacks) {
+               cb = data->callbacks->data;
+
+               if (cb->signal_func && !cb->signal_func(connection, message,
+                                                       cb->user_data)) {
+                       filter_data_remove_callback(data, cb);
+                       continue;
+               }
+
+               /* Check if the watch was removed/freed by the callback
+                * function */
+               if (!g_slist_find(data->callbacks, cb))
+                       continue;
+
+               data->callbacks = g_slist_remove(data->callbacks, cb);
+               data->processed = g_slist_append(data->processed, cb);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void update_name_cache(const char *name, const char *owner)
+{
+       GSList *l;
+
+       for (l = listeners; l != NULL; l = l->next) {
+               struct filter_data *data = l->data;
+
+               if (g_strcmp0(data->name, name) != 0)
+                       continue;
+
+               g_free(data->owner);
+               data->owner = g_strdup(owner);
+       }
+}
+
+static const char *check_name_cache(const char *name)
+{
+       GSList *l;
+
+       for (l = listeners; l != NULL; l = l->next) {
+               struct filter_data *data = l->data;
+
+               if (g_strcmp0(data->name, name) != 0)
+                       continue;
+
+               return data->owner;
+       }
+
+       return NULL;
+}
+
+static DBusHandlerResult service_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data = user_data;
+       struct filter_callback *cb;
+       char *name, *old, *new;
+
+       if (!dbus_message_get_args(message, NULL,
+                               DBUS_TYPE_STRING, &name,
+                               DBUS_TYPE_STRING, &old,
+                               DBUS_TYPE_STRING, &new,
+                               DBUS_TYPE_INVALID)) {
+               error("Invalid arguments for NameOwnerChanged signal");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       update_name_cache(name, new);
+
+       while (data->callbacks) {
+               cb = data->callbacks->data;
+
+               if (*new == '\0') {
+                       if (cb->disc_func)
+                               cb->disc_func(connection, cb->user_data);
+               } else {
+                       if (cb->conn_func)
+                               cb->conn_func(connection, cb->user_data);
+               }
+
+               /* Check if the watch was removed/freed by the callback
+                * function */
+               if (!g_slist_find(data->callbacks, cb))
+                       continue;
+
+               /* Only auto remove if it is a bus name watch */
+               if (data->argument[0] == ':' &&
+                               (cb->conn_func == NULL || cb->disc_func == NULL)) {
+                       filter_data_remove_callback(data, cb);
+                       continue;
+               }
+
+               data->callbacks = g_slist_remove(data->callbacks, cb);
+               data->processed = g_slist_append(data->processed, cb);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult message_filter(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct filter_data *data;
+       const char *sender, *path, *iface, *member, *arg = NULL;
+
+       /* Only filter signals */
+       if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       sender = dbus_message_get_sender(message);
+       path = dbus_message_get_path(message);
+       iface = dbus_message_get_interface(message);
+       member = dbus_message_get_member(message);
+       dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
+
+       /* Sender is always bus name */
+       data = filter_data_find(connection, NULL, sender, path, iface, member,
+                                       arg);
+       if (data == NULL) {
+               error("Got %s.%s signal which has no listeners", iface, member);
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (data->handle_func) {
+               data->lock = TRUE;
+
+               data->handle_func(connection, message, data);
+
+               data->callbacks = data->processed;
+               data->processed = NULL;
+               data->lock = FALSE;
+       }
+
+       if (data->callbacks)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       remove_match(data);
+
+       listeners = g_slist_remove(listeners, data);
+       filter_data_free(data);
+
+       /* Remove filter if there no listener left for the connection */
+       data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
+                                       NULL);
+       if (data == NULL)
+               dbus_connection_remove_filter(connection, message_filter,
+                                               NULL);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean update_service(void *user_data)
+{
+       struct service_data *data = user_data;
+       struct filter_callback *cb = data->callback;
+
+       update_name_cache(data->name, data->owner);
+       if (cb->conn_func)
+               cb->conn_func(data->conn, cb->user_data);
+
+       service_data_free(data);
+
+       return FALSE;
+}
+
+static void service_reply(DBusPendingCall *call, void *user_data)
+{
+       struct service_data *data = user_data;
+       DBusMessage *reply;
+       DBusError err;
+
+       reply = dbus_pending_call_steal_reply(call);
+       if (reply == NULL)
+               return;
+
+       dbus_error_init(&err);
+
+       if (dbus_set_error_from_message(&err, reply))
+               goto fail;
+
+       if (dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_STRING, &data->owner,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               goto fail;
+
+       update_service(data);
+
+       goto done;
+
+fail:
+       error("%s", err.message);
+       dbus_error_free(&err);
+       service_data_free(data);
+done:
+       dbus_message_unref(reply);
+}
+
+static void check_service(DBusConnection *connection,
+                                       const char *name,
+                                       struct filter_callback *callback)
+{
+       DBusMessage *message;
+       struct service_data *data;
+
+       data = g_try_malloc0(sizeof(*data));
+       if (data == NULL) {
+               error("Can't allocate data structure");
+               return;
+       }
+
+       data->conn = dbus_connection_ref(connection);
+       data->name = g_strdup(name);
+       data->callback = callback;
+       callback->data = data;
+
+       data->owner = check_name_cache(name);
+       if (data->owner != NULL) {
+               data->id = g_idle_add(update_service, data);
+               return;
+       }
+
+       message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+                       DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner");
+       if (message == NULL) {
+               error("Can't allocate new message");
+               g_free(data);
+               return;
+       }
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &name,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                                       &data->call, -1) == FALSE) {
+               error("Failed to execute method call");
+               g_free(data);
+               goto done;
+       }
+
+       if (data->call == NULL) {
+               error("D-Bus connection not available");
+               g_free(data);
+               goto done;
+       }
+
+       dbus_pending_call_set_notify(data->call, service_reply, data, NULL);
+
+done:
+       dbus_message_unref(message);
+}
+
+guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction connect,
+                               GDBusWatchFunction disconnect,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+
+       if (name == NULL)
+               return 0;
+
+       data = filter_data_get(connection, service_filter, NULL, NULL,
+                               DBUS_INTERFACE_DBUS, "NameOwnerChanged",
+                               name);
+       if (data == NULL)
+               return 0;
+
+       cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy,
+                                       user_data);
+       if (cb == NULL)
+               return 0;
+
+       if (connect)
+               check_service(connection, name, cb);
+
+       return cb->id;
+}
+
+guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
+                               GDBusWatchFunction func,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       return g_dbus_add_service_watch(connection, name, NULL, func,
+                                                       user_data, destroy);
+}
+
+guint g_dbus_add_signal_watch(DBusConnection *connection,
+                               const char *sender, const char *path,
+                               const char *interface, const char *member,
+                               GDBusSignalFunction function, void *user_data,
+                               GDBusDestroyFunction destroy)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+
+       data = filter_data_get(connection, signal_filter, sender, path,
+                               interface, member, NULL);
+       if (data == NULL)
+               return 0;
+
+       cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
+                                       user_data);
+       if (cb == NULL)
+               return 0;
+
+       if (data->name != NULL && data->name_watch == 0)
+               data->name_watch = g_dbus_add_service_watch(connection,
+                                                       data->name, NULL,
+                                                       NULL, NULL, NULL);
+
+       return cb->id;
+}
+
+gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
+{
+       struct filter_data *data;
+       struct filter_callback *cb;
+       GSList *ldata;
+
+       if (id == 0)
+               return FALSE;
+
+       for (ldata = listeners; ldata; ldata = ldata->next) {
+               data = ldata->data;
+
+               cb = filter_data_find_callback(data, id);
+               if (cb) {
+                       filter_data_remove_callback(data, cb);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+void g_dbus_remove_all_watches(DBusConnection *connection)
+{
+       struct filter_data *data;
+
+       while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL,
+                                       NULL, NULL))) {
+               listeners = g_slist_remove(listeners, data);
+               filter_data_call_and_free(data);
+       }
+
+       dbus_connection_remove_filter(connection, message_filter, NULL);
+}
diff --git a/health/hdp.c b/health/hdp.c
new file mode 100644 (file)
index 0000000..2316204
--- /dev/null
@@ -0,0 +1,2247 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sdpd.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <bluetooth/l2cap.h>
+#include <gdbus.h>
+#include <dbus-common.h>
+#include <log.h>
+#include <error.h>
+#include <adapter.h>
+#include <device.h>
+#include <btio.h>
+
+#include "mcap_lib.h"
+#include "hdp_types.h"
+#include "hdp_util.h"
+#include "hdp.h"
+#include "mcap.h"
+
+#define ECHO_TIMEOUT   1 /* second */
+#define HDP_ECHO_LEN   15
+
+static DBusConnection *connection = NULL;
+
+static GSList *applications = NULL;
+static GSList *devices = NULL;
+static uint8_t next_app_id = HDP_MDEP_INITIAL;
+
+static GSList *adapters;
+
+static gboolean update_adapter(struct hdp_adapter *adapter);
+static struct hdp_device *create_health_device(DBusConnection *conn,
+                                               struct btd_device *device);
+static void free_echo_data(struct hdp_echo_data *edata);
+
+struct hdp_create_dc {
+       DBusConnection                  *conn;
+       DBusMessage                     *msg;
+       struct hdp_application          *app;
+       struct hdp_device               *dev;
+       uint8_t                         config;
+       uint8_t                         mdep;
+       guint                           ref;
+       mcap_mdl_operation_cb           cb;
+};
+
+struct hdp_tmp_dc_data {
+       DBusConnection                  *conn;
+       DBusMessage                     *msg;
+       struct hdp_channel              *hdp_chann;
+       guint                           ref;
+       mcap_mdl_operation_cb           cb;
+};
+
+struct hdp_echo_data {
+       gboolean                echo_done;      /* Is a echo was already done */
+       gpointer                buf;            /* echo packet sent */
+       uint                    tid;            /* echo timeout */
+};
+
+static struct hdp_channel *hdp_channel_ref(struct hdp_channel *chan)
+{
+       if (chan == NULL)
+               return NULL;
+
+       chan->ref++;
+
+       DBG("health_channel_ref(%p): ref=%d", chan, chan->ref);
+       return chan;
+}
+
+static void free_health_channel(struct hdp_channel *chan)
+{
+       if (chan->mdep == HDP_MDEP_ECHO) {
+               free_echo_data(chan->edata);
+               chan->edata = NULL;
+       }
+
+       mcap_mdl_unref(chan->mdl);
+       hdp_application_unref(chan->app);
+       health_device_unref(chan->dev);
+       g_free(chan->path);
+       g_free(chan);
+}
+
+static void hdp_channel_unref(struct hdp_channel *chan)
+{
+       if (chan == NULL)
+               return;
+
+       chan->ref --;
+       DBG("health_channel_unref(%p): ref=%d", chan, chan->ref);
+
+       if (chan->ref > 0)
+               return;
+
+       free_health_channel(chan);
+}
+
+static void free_hdp_create_dc(struct hdp_create_dc *dc_data)
+{
+       dbus_message_unref(dc_data->msg);
+       dbus_connection_unref(dc_data->conn);
+       hdp_application_unref(dc_data->app);
+       health_device_unref(dc_data->dev);
+
+       g_free(dc_data);
+}
+
+static struct hdp_create_dc *hdp_create_data_ref(struct hdp_create_dc *dc_data)
+{
+       dc_data->ref++;
+
+       DBG("hdp_create_data_ref(%p): ref=%d", dc_data, dc_data->ref);
+
+       return dc_data;
+}
+
+static void hdp_create_data_unref(struct hdp_create_dc *dc_data)
+{
+       dc_data->ref--;
+
+       DBG("hdp_create_data_unref(%p): ref=%d", dc_data, dc_data->ref);
+
+       if (dc_data->ref > 0)
+               return;
+
+       free_hdp_create_dc(dc_data);
+}
+
+static void free_hdp_conn_dc(struct hdp_tmp_dc_data *data)
+{
+       dbus_message_unref(data->msg);
+       dbus_connection_unref(data->conn);
+       hdp_channel_unref(data->hdp_chann);
+
+       g_free(data);
+}
+
+static struct hdp_tmp_dc_data *hdp_tmp_dc_data_ref(struct hdp_tmp_dc_data *data)
+{
+       data->ref++;
+
+       DBG("hdp_conn_data_ref(%p): ref=%d", data, data->ref);
+
+       return data;
+}
+
+static void hdp_tmp_dc_data_unref(struct hdp_tmp_dc_data *data)
+{
+       data->ref--;
+
+       DBG("hdp_conn_data_unref(%p): ref=%d", data, data->ref);
+
+       if (data->ref > 0)
+               return;
+
+       free_hdp_conn_dc(data);
+}
+
+static int cmp_app_id(gconstpointer a, gconstpointer b)
+{
+       const struct hdp_application *app = a;
+       const uint8_t *id = b;
+
+       return app->id - *id;
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+       const struct hdp_adapter *hdp_adapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (hdp_adapter->btd_adapter == adapter)
+               return 0;
+
+       return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+       const struct hdp_device *hdp_device = a;
+       const struct btd_device *device = b;
+
+       if (hdp_device->dev == device)
+               return 0;
+
+       return -1;
+}
+
+static gint cmp_dev_addr(gconstpointer a, gconstpointer dst)
+{
+       const struct hdp_device *device = a;
+       bdaddr_t addr;
+
+       device_get_address(device->dev, &addr, NULL);
+       return bacmp(&addr, dst);
+}
+
+static gint cmp_dev_mcl(gconstpointer a, gconstpointer mcl)
+{
+       const struct hdp_device *device = a;
+
+       if (mcl == device->mcl)
+               return 0;
+       return -1;
+}
+
+static gint cmp_chan_mdlid(gconstpointer a, gconstpointer b)
+{
+       const struct hdp_channel *chan = a;
+       const uint16_t *mdlid = b;
+
+       return chan->mdlid - *mdlid;
+}
+
+static gint cmp_chan_path(gconstpointer a, gconstpointer b)
+{
+       const struct hdp_channel *chan = a;
+       const char *path = b;
+
+       return g_ascii_strcasecmp(chan->path, path);
+}
+
+static gint cmp_chan_mdl(gconstpointer a, gconstpointer mdl)
+{
+       const struct hdp_channel *chan = a;
+
+       if (chan->mdl == mdl)
+               return 0;
+       return -1;
+}
+
+static uint8_t get_app_id(void)
+{
+       uint8_t id = next_app_id;
+
+       do {
+               GSList *l = g_slist_find_custom(applications, &id, cmp_app_id);
+
+               if (l == NULL) {
+                       next_app_id = (id % HDP_MDEP_FINAL) + 1;
+                       return id;
+               } else
+                       id = (id % HDP_MDEP_FINAL) + 1;
+       } while (id != next_app_id);
+
+       /* No more ids available */
+       return 0;
+}
+
+static int cmp_app(gconstpointer a, gconstpointer b)
+{
+       const struct hdp_application *app = a;
+
+       return g_strcmp0(app->path, b);
+}
+
+static gboolean set_app_path(struct hdp_application *app)
+{
+       app->id = get_app_id();
+       if (app->id == 0)
+               return FALSE;
+       app->path = g_strdup_printf(MANAGER_PATH "/health_app_%d", app->id);
+
+       return TRUE;
+};
+
+static void device_unref_mcl(struct hdp_device *hdp_device)
+{
+       if (hdp_device->mcl == NULL)
+               return;
+
+       mcap_close_mcl(hdp_device->mcl, FALSE);
+       mcap_mcl_unref(hdp_device->mcl);
+       hdp_device->mcl = NULL;
+       hdp_device->mcl_conn = FALSE;
+}
+
+static void free_health_device(struct hdp_device *device)
+{
+       if (device->conn != NULL) {
+               dbus_connection_unref(device->conn);
+               device->conn = NULL;
+       }
+
+       if (device->dev != NULL) {
+               btd_device_unref(device->dev);
+               device->dev = NULL;
+       }
+
+       device_unref_mcl(device);
+
+       g_free(device);
+}
+
+static void remove_application(struct hdp_application *app)
+{
+       DBG("Application %s deleted", app->path);
+       hdp_application_unref(app);
+
+       g_slist_foreach(adapters, (GFunc) update_adapter, NULL);
+}
+
+static void client_disconnected(DBusConnection *conn, void *user_data)
+{
+       struct hdp_application *app = user_data;
+
+       DBG("Client disconnected from the bus, deleting hdp application");
+       applications = g_slist_remove(applications, app);
+
+       app->dbus_watcher = 0; /* Watcher shouldn't be freed in this case */
+       remove_application(app);
+}
+
+static DBusMessage *manager_create_application(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct hdp_application *app;
+       const char *name;
+       DBusMessageIter iter;
+       GError *err = NULL;
+
+       dbus_message_iter_init(msg, &iter);
+       app = hdp_get_app_config(&iter, &err);
+       if (err != NULL) {
+               g_error_free(err);
+               return btd_error_invalid_args(msg);
+       }
+
+       name = dbus_message_get_sender(msg);
+       if (name == NULL) {
+               hdp_application_unref(app);
+               return g_dbus_create_error(msg,
+                                       ERROR_INTERFACE ".HealthError",
+                                       "Can't get sender name");
+       }
+
+       if (!set_app_path(app)) {
+               hdp_application_unref(app);
+               return g_dbus_create_error(msg,
+                               ERROR_INTERFACE ".HealthError",
+                               "Can't get a valid id for the application");
+       }
+
+       app->oname = g_strdup(name);
+       app->conn = dbus_connection_ref(conn);
+
+       applications = g_slist_prepend(applications, app);
+
+       app->dbus_watcher = g_dbus_add_disconnect_watch(conn, name,
+                                               client_disconnected, app, NULL);
+       g_slist_foreach(adapters, (GFunc) update_adapter, NULL);
+
+       DBG("Health application created with id %s", app->path);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_OBJECT_PATH, &app->path,
+                                                       DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *manager_destroy_application(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       const char *path;
+       struct hdp_application *app;
+       GSList *l;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       l = g_slist_find_custom(applications, path, cmp_app);
+
+       if (l == NULL)
+               return g_dbus_create_error(msg,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call, "
+                                       "no such application");
+
+       app = l->data;
+       applications = g_slist_remove(applications, app);
+
+       remove_application(app);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void manager_path_unregister(gpointer data)
+{
+       g_slist_foreach(applications, (GFunc) hdp_application_unref, NULL);
+
+       g_slist_free(applications);
+       applications = NULL;
+
+       g_slist_foreach(adapters, (GFunc) update_adapter, NULL);
+}
+
+static const GDBusMethodTable health_manager_methods[] = {
+       { GDBUS_METHOD("CreateApplication",
+                       GDBUS_ARGS({ "config", "a{sv}" }),
+                       GDBUS_ARGS({ "application", "o" }),
+                       manager_create_application) },
+       { GDBUS_METHOD("DestroyApplication",
+                       GDBUS_ARGS({ "application", "o" }), NULL,
+                       manager_destroy_application) },
+       { }
+};
+
+static DBusMessage *channel_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct hdp_channel *chan = user_data;
+       DBusMessageIter iter, dict;
+       DBusMessage *reply;
+       const char *path;
+       char *type;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       path = device_get_path(chan->dev->dev);
+       dict_append_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH, &path);
+
+       path = chan->app->path;
+       dict_append_entry(&dict, "Application", DBUS_TYPE_OBJECT_PATH, &path);
+
+       if (chan->config == HDP_RELIABLE_DC)
+               type = g_strdup("Reliable");
+       else
+               type = g_strdup("Streaming");
+
+       dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &type);
+
+       g_free(type);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void hdp_tmp_dc_data_destroy(gpointer data)
+{
+       struct hdp_tmp_dc_data *hdp_conn = data;
+
+       hdp_tmp_dc_data_unref(hdp_conn);
+}
+
+static void abort_mdl_cb(GError *err, gpointer data)
+{
+       if (err != NULL)
+               error("Aborting error: %s", err->message);
+}
+
+static void hdp_mdl_reconn_cb(struct mcap_mdl *mdl, GError *err, gpointer data)
+{
+       struct hdp_tmp_dc_data *dc_data = data;
+       DBusMessage *reply;
+       int fd;
+
+       if (err != NULL) {
+               struct hdp_channel *chan = dc_data->hdp_chann;
+               GError *gerr = NULL;
+
+               error("%s", err->message);
+               reply = g_dbus_create_error(dc_data->msg,
+                                       ERROR_INTERFACE ".HealthError",
+                                       "Cannot reconnect: %s", err->message);
+               g_dbus_send_message(dc_data->conn, reply);
+
+               /* Send abort request because remote side */
+               /* is now in PENDING state */
+               if (!mcap_mdl_abort(chan->mdl, abort_mdl_cb, NULL, NULL,
+                                                               &gerr)) {
+                       error("%s", gerr->message);
+                       g_error_free(gerr);
+               }
+               return;
+       }
+
+       fd = mcap_mdl_get_fd(dc_data->hdp_chann->mdl);
+       if (fd < 0) {
+               reply = g_dbus_create_error(dc_data->msg,
+                                               ERROR_INTERFACE ".HealthError",
+                                               "Cannot get file descriptor");
+               g_dbus_send_message(dc_data->conn, reply);
+               return;
+       }
+
+       reply = g_dbus_create_reply(dc_data->msg, DBUS_TYPE_UNIX_FD,
+                                                       &fd, DBUS_TYPE_INVALID);
+       g_dbus_send_message(dc_data->conn, reply);
+
+       g_dbus_emit_signal(dc_data->conn,
+                       device_get_path(dc_data->hdp_chann->dev->dev),
+                       HEALTH_DEVICE, "ChannelConnected",
+                       DBUS_TYPE_OBJECT_PATH, &dc_data->hdp_chann->path,
+                       DBUS_TYPE_INVALID);
+}
+
+static void hdp_get_dcpsm_cb(uint16_t dcpsm, gpointer user_data, GError *err)
+{
+       struct hdp_tmp_dc_data *hdp_conn = user_data;
+       struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
+       GError *gerr = NULL;
+       uint8_t mode;
+
+       if (err != NULL) {
+               hdp_conn->cb(hdp_chann->mdl, err, hdp_conn);
+               return;
+       }
+
+       if (hdp_chann->config == HDP_RELIABLE_DC)
+               mode = L2CAP_MODE_ERTM;
+       else
+               mode = L2CAP_MODE_STREAMING;
+
+       if (mcap_connect_mdl(hdp_chann->mdl, mode, dcpsm, hdp_conn->cb,
+                                       hdp_tmp_dc_data_ref(hdp_conn),
+                                       hdp_tmp_dc_data_destroy, &gerr))
+               return;
+
+       hdp_tmp_dc_data_unref(hdp_conn);
+       hdp_conn->cb(hdp_chann->mdl, err, hdp_conn);
+       g_error_free(gerr);
+}
+
+static void device_reconnect_mdl_cb(struct mcap_mdl *mdl, GError *err,
+                                                               gpointer data)
+{
+       struct hdp_tmp_dc_data *dc_data = data;
+       GError *gerr = NULL;
+       DBusMessage *reply;
+
+       if (err != NULL) {
+               reply = g_dbus_create_error(dc_data->msg,
+                                       ERROR_INTERFACE ".HealthError",
+                                       "Cannot reconnect: %s", err->message);
+               g_dbus_send_message(dc_data->conn, reply);
+               return;
+       }
+
+       dc_data->cb = hdp_mdl_reconn_cb;
+
+       if (hdp_get_dcpsm(dc_data->hdp_chann->dev, hdp_get_dcpsm_cb,
+                                       hdp_tmp_dc_data_ref(dc_data),
+                                       hdp_tmp_dc_data_destroy, &gerr))
+               return;
+
+       error("%s", gerr->message);
+
+       reply = g_dbus_create_error(dc_data->msg,
+                                       ERROR_INTERFACE ".HealthError",
+                                       "Cannot reconnect: %s", gerr->message);
+       g_dbus_send_message(dc_data->conn, reply);
+       hdp_tmp_dc_data_unref(dc_data);
+       g_error_free(gerr);
+
+       /* Send abort request because remote side is now in PENDING state */
+       if (!mcap_mdl_abort(mdl, abort_mdl_cb, NULL, NULL, &gerr)) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+       }
+}
+
+static DBusMessage *channel_acquire_continue(struct hdp_tmp_dc_data *data,
+                                                               GError *err)
+{
+       DBusMessage *reply;
+       GError *gerr = NULL;
+       int fd;
+
+       if (err != NULL) {
+               return g_dbus_create_error(data->msg,
+                                               ERROR_INTERFACE ".HealthError",
+                                               "%s", err->message);
+       }
+
+       fd = mcap_mdl_get_fd(data->hdp_chann->mdl);
+       if (fd >= 0)
+               return g_dbus_create_reply(data->msg, DBUS_TYPE_UNIX_FD, &fd,
+                                                       DBUS_TYPE_INVALID);
+
+       hdp_tmp_dc_data_ref(data);
+       if (mcap_reconnect_mdl(data->hdp_chann->mdl, device_reconnect_mdl_cb,
+                                       data, hdp_tmp_dc_data_destroy, &gerr))
+               return NULL;
+
+       hdp_tmp_dc_data_unref(data);
+       reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError",
+                                       "Cannot reconnect: %s", gerr->message);
+       g_error_free(gerr);
+
+       return reply;
+}
+
+static void channel_acquire_cb(gpointer data, GError *err)
+{
+       struct hdp_tmp_dc_data *dc_data = data;
+       DBusMessage *reply;
+
+       reply = channel_acquire_continue(data, err);
+
+       if (reply != NULL)
+               g_dbus_send_message(dc_data->conn, reply);
+}
+
+static DBusMessage *channel_acquire(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct hdp_channel *chan = user_data;
+       struct hdp_tmp_dc_data *dc_data;
+       GError *gerr = NULL;
+       DBusMessage *reply;
+
+       dc_data = g_new0(struct hdp_tmp_dc_data, 1);
+       dc_data->conn = dbus_connection_ref(conn);
+       dc_data->msg = dbus_message_ref(msg);
+       dc_data->hdp_chann = hdp_channel_ref(chan);
+
+       if (chan->dev->mcl_conn) {
+               reply = channel_acquire_continue(hdp_tmp_dc_data_ref(dc_data),
+                                                                       NULL);
+               hdp_tmp_dc_data_unref(dc_data);
+               return reply;
+       }
+
+       if (hdp_establish_mcl(chan->dev, channel_acquire_cb,
+                                               hdp_tmp_dc_data_ref(dc_data),
+                                               hdp_tmp_dc_data_destroy, &gerr))
+               return NULL;
+
+       reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+                                       "%s", gerr->message);
+       hdp_tmp_dc_data_unref(dc_data);
+       g_error_free(gerr);
+
+       return reply;
+}
+
+static void close_mdl(struct hdp_channel *hdp_chann)
+{
+       int fd;
+
+       fd = mcap_mdl_get_fd(hdp_chann->mdl);
+       if (fd < 0)
+               return;
+
+       close(fd);
+}
+
+static DBusMessage *channel_release(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct hdp_channel *hdp_chann = user_data;
+
+       close_mdl(hdp_chann);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void free_echo_data(struct hdp_echo_data *edata)
+{
+       if (edata == NULL)
+               return;
+
+       if (edata->tid > 0)
+               g_source_remove(edata->tid);
+
+       if (edata->buf != NULL)
+               g_free(edata->buf);
+
+
+       g_free(edata);
+}
+
+static void health_channel_destroy(void *data)
+{
+       struct hdp_channel *hdp_chan = data;
+       struct hdp_device *dev = hdp_chan->dev;
+
+       DBG("Destroy Health Channel %s", hdp_chan->path);
+       if (g_slist_find(dev->channels, hdp_chan) == NULL)
+               goto end;
+
+       dev->channels = g_slist_remove(dev->channels, hdp_chan);
+
+       if (hdp_chan->mdep != HDP_MDEP_ECHO)
+               g_dbus_emit_signal(dev->conn, device_get_path(dev->dev),
+                                       HEALTH_DEVICE, "ChannelDeleted",
+                                       DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
+                                       DBUS_TYPE_INVALID);
+
+       if (hdp_chan == dev->fr) {
+               hdp_channel_unref(dev->fr);
+               dev->fr = NULL;
+       }
+
+end:
+       hdp_channel_unref(hdp_chan);
+}
+
+static const GDBusMethodTable health_channels_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       channel_get_properties) },
+       { GDBUS_ASYNC_METHOD("Acquire",
+                       NULL, GDBUS_ARGS({ "fd", "h" }),
+                       channel_acquire) },
+       { GDBUS_METHOD("Release", NULL, NULL, channel_release) },
+       { }
+};
+
+static struct hdp_channel *create_channel(struct hdp_device *dev,
+                                               uint8_t config,
+                                               struct mcap_mdl *mdl,
+                                               uint16_t mdlid,
+                                               struct hdp_application *app,
+                                               GError **err)
+{
+       struct hdp_channel *hdp_chann;
+
+       if (dev == NULL)
+               return NULL;
+
+       hdp_chann = g_new0(struct hdp_channel, 1);
+       hdp_chann->config = config;
+       hdp_chann->dev = health_device_ref(dev);
+       hdp_chann->mdlid = mdlid;
+
+       if (mdl != NULL)
+               hdp_chann->mdl = mcap_mdl_ref(mdl);
+
+       if (app != NULL) {
+               hdp_chann->mdep = app->id;
+               hdp_chann->app = hdp_application_ref(app);
+       } else
+               hdp_chann->edata = g_new0(struct hdp_echo_data, 1);
+
+       hdp_chann->path = g_strdup_printf("%s/chan%d",
+                                       device_get_path(hdp_chann->dev->dev),
+                                       hdp_chann->mdlid);
+
+       dev->channels = g_slist_append(dev->channels,
+                                               hdp_channel_ref(hdp_chann));
+
+       if (hdp_chann->mdep == HDP_MDEP_ECHO)
+               return hdp_channel_ref(hdp_chann);
+
+       if (!g_dbus_register_interface(dev->conn, hdp_chann->path,
+                                       HEALTH_CHANNEL,
+                                       health_channels_methods, NULL, NULL,
+                                       hdp_chann, health_channel_destroy)) {
+               g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+                                       "Can't register the channel interface");
+               health_channel_destroy(hdp_chann);
+               return NULL;
+       }
+
+       return hdp_channel_ref(hdp_chann);
+}
+
+static void remove_channels(struct hdp_device *dev)
+{
+       struct hdp_channel *chan;
+       char *path;
+
+       while (dev->channels != NULL) {
+               chan = dev->channels->data;
+
+               path = g_strdup(chan->path);
+               if (!g_dbus_unregister_interface(dev->conn, path,
+                                                               HEALTH_CHANNEL))
+                       health_channel_destroy(chan);
+               g_free(path);
+       }
+}
+
+static void close_device_con(struct hdp_device *dev, gboolean cache)
+{
+       if (dev->mcl == NULL)
+               return;
+
+       mcap_close_mcl(dev->mcl, cache);
+       dev->mcl_conn = FALSE;
+
+       if (cache)
+               return;
+
+       device_unref_mcl(dev);
+       remove_channels(dev);
+
+       if (!dev->sdp_present) {
+               const char *path;
+
+               path = device_get_path(dev->dev);
+               g_dbus_unregister_interface(dev->conn, path, HEALTH_DEVICE);
+       }
+}
+
+static int send_echo_data(int sock, const void *buf, uint32_t size)
+{
+       const uint8_t *buf_b = buf;
+       uint32_t sent = 0;
+
+       while (sent < size) {
+               int n = write(sock, buf_b + sent, size - sent);
+               if (n < 0)
+                       return -1;
+               sent += n;
+       }
+
+       return 0;
+}
+
+static gboolean serve_echo(GIOChannel *io_chan, GIOCondition cond,
+                                                               gpointer data)
+{
+       struct hdp_channel *chan = data;
+       uint8_t buf[MCAP_DC_MTU];
+       int fd, len;
+
+       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+               hdp_channel_unref(chan);
+               return FALSE;
+       }
+
+       if (chan->edata->echo_done)
+               goto fail;
+
+       chan->edata->echo_done = TRUE;
+
+       fd = g_io_channel_unix_get_fd(io_chan);
+       len = read(fd, buf, sizeof(buf));
+
+       if (send_echo_data(fd, buf, len)  >= 0)
+               return TRUE;
+
+fail:
+       close_device_con(chan->dev, FALSE);
+       hdp_channel_unref(chan);
+       return FALSE;
+}
+
+static gboolean check_channel_conf(struct hdp_channel *chan)
+{
+       GError *err = NULL;
+       GIOChannel *io;
+       uint8_t mode;
+       uint16_t imtu, omtu;
+       int fd;
+
+       fd = mcap_mdl_get_fd(chan->mdl);
+       if (fd < 0)
+               return FALSE;
+       io = g_io_channel_unix_new(fd);
+
+       if (!bt_io_get(io, BT_IO_L2CAP, &err,
+                       BT_IO_OPT_MODE, &mode,
+                       BT_IO_OPT_IMTU, &imtu,
+                       BT_IO_OPT_OMTU, &omtu,
+                       BT_IO_OPT_INVALID)) {
+               error("Error: %s", err->message);
+               g_io_channel_unref(io);
+               g_error_free(err);
+               return FALSE;
+       }
+
+       g_io_channel_unref(io);
+
+       switch (chan->config) {
+       case HDP_RELIABLE_DC:
+               if (mode != L2CAP_MODE_ERTM)
+                       return FALSE;
+               break;
+       case HDP_STREAMING_DC:
+               if (mode != L2CAP_MODE_STREAMING)
+                       return FALSE;
+               break;
+       default:
+               error("Error: Connected with unknown configuration");
+               return FALSE;
+       }
+
+       DBG("MDL imtu %d omtu %d Channel imtu %d omtu %d", imtu, omtu,
+                                               chan->imtu, chan->omtu);
+
+       if (chan->imtu == 0)
+               chan->imtu = imtu;
+       if (chan->omtu == 0)
+               chan->omtu = omtu;
+
+       if (chan->imtu != imtu || chan->omtu != omtu)
+               return FALSE;
+
+       return TRUE;
+}
+
+static void hdp_mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+       struct hdp_device *dev = data;
+       struct hdp_channel *chan;
+
+       DBG("hdp_mcap_mdl_connected_cb");
+       if (dev->ndc == NULL)
+               return;
+
+       chan = dev->ndc;
+       if (chan->mdl == NULL)
+               chan->mdl = mcap_mdl_ref(mdl);
+
+       if (g_slist_find(dev->channels, chan) == NULL)
+               dev->channels = g_slist_prepend(dev->channels,
+                                                       hdp_channel_ref(chan));
+
+       if (!check_channel_conf(chan)) {
+               close_mdl(chan);
+               goto end;
+       }
+
+       if (chan->mdep == HDP_MDEP_ECHO) {
+               GIOChannel *io;
+               int fd;
+
+               fd = mcap_mdl_get_fd(chan->mdl);
+               if (fd < 0)
+                       goto end;
+
+               chan->edata->echo_done = FALSE;
+               io = g_io_channel_unix_new(fd);
+               g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
+                               serve_echo, hdp_channel_ref(chan));
+               g_io_channel_unref(io);
+               goto end;
+       }
+
+       g_dbus_emit_signal(dev->conn, device_get_path(dev->dev), HEALTH_DEVICE,
+                                       "ChannelConnected",
+                                       DBUS_TYPE_OBJECT_PATH, &chan->path,
+                                       DBUS_TYPE_INVALID);
+
+       if (dev->fr != NULL)
+               goto end;
+
+       dev->fr = hdp_channel_ref(chan);
+
+       emit_property_changed(dev->conn, device_get_path(dev->dev),
+                                       HEALTH_DEVICE, "MainChannel",
+                                       DBUS_TYPE_OBJECT_PATH, &dev->fr->path);
+
+end:
+       hdp_channel_unref(dev->ndc);
+       dev->ndc = NULL;
+}
+
+static void hdp_mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data)
+{
+       /* struct hdp_device *dev = data; */
+
+       DBG("hdp_mcap_mdl_closed_cb");
+
+       /* Nothing to do */
+}
+
+static void hdp_mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data)
+{
+       struct hdp_device *dev = data;
+       struct hdp_channel *chan;
+       char *path;
+       GSList *l;
+
+       DBG("hdp_mcap_mdl_deleted_cb");
+       l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl);
+       if (l == NULL)
+               return;
+
+       chan = l->data;
+
+       path = g_strdup(chan->path);
+       if (!g_dbus_unregister_interface(dev->conn, path, HEALTH_CHANNEL))
+               health_channel_destroy(chan);
+       g_free(path);
+}
+
+static void hdp_mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
+{
+       struct hdp_device *dev = data;
+
+       DBG("hdp_mcap_mdl_aborted_cb");
+       if (dev->ndc == NULL)
+               return;
+
+       dev->ndc->mdl = mcap_mdl_ref(mdl);
+
+       if (g_slist_find(dev->channels, dev->ndc) == NULL)
+               dev->channels = g_slist_prepend(dev->channels,
+                                               hdp_channel_ref(dev->ndc));
+
+       if (dev->ndc->mdep != HDP_MDEP_ECHO)
+               g_dbus_emit_signal(dev->conn, device_get_path(dev->dev),
+                                       HEALTH_DEVICE, "ChannelConnected",
+                                       DBUS_TYPE_OBJECT_PATH, &dev->ndc->path,
+                                       DBUS_TYPE_INVALID);
+
+       hdp_channel_unref(dev->ndc);
+       dev->ndc = NULL;
+}
+
+static uint8_t hdp2l2cap_mode(uint8_t hdp_mode)
+{
+       return hdp_mode == HDP_STREAMING_DC ? L2CAP_MODE_STREAMING :
+                                                               L2CAP_MODE_ERTM;
+}
+
+static uint8_t hdp_mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid,
+                               uint16_t mdlid, uint8_t *conf, void *data)
+{
+       struct hdp_device *dev = data;
+       struct hdp_application *app;
+       GError *err = NULL;
+       GSList *l;
+
+       DBG("Data channel request");
+
+       if (mdepid == HDP_MDEP_ECHO) {
+               switch (*conf) {
+               case HDP_NO_PREFERENCE_DC:
+                       *conf = HDP_RELIABLE_DC;
+               case HDP_RELIABLE_DC:
+                       break;
+               case HDP_STREAMING_DC:
+                       return MCAP_CONFIGURATION_REJECTED;
+               default:
+                       /* Special case defined in HDP spec 3.4. When an invalid
+                       * configuration is received we shall close the MCL when
+                       * we are still processing the callback. */
+                       close_device_con(dev, FALSE);
+                       return MCAP_CONFIGURATION_REJECTED; /* not processed */
+               }
+
+               if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
+                                               L2CAP_MODE_ERTM, &err)) {
+                       error("Error: %s", err->message);
+                       g_error_free(err);
+                       return MCAP_MDL_BUSY;
+               }
+
+               dev->ndc = create_channel(dev, *conf, NULL, mdlid, NULL, NULL);
+               if (dev->ndc == NULL)
+                       return MCAP_MDL_BUSY;
+
+               return MCAP_SUCCESS;
+       }
+
+       l = g_slist_find_custom(applications, &mdepid, cmp_app_id);
+       if (l == NULL)
+               return MCAP_INVALID_MDEP;
+
+       app = l->data;
+
+       /* Check if is the first dc if so,
+       * only reliable configuration is allowed */
+       switch (*conf) {
+       case HDP_NO_PREFERENCE_DC:
+               if (app->role == HDP_SINK)
+                       return MCAP_CONFIGURATION_REJECTED;
+               else if (dev->fr && app->chan_type_set)
+                       *conf = app->chan_type;
+               else
+                       *conf = HDP_RELIABLE_DC;
+               break;
+       case HDP_STREAMING_DC:
+               if (!dev->fr || app->role == HDP_SOURCE)
+                       return MCAP_CONFIGURATION_REJECTED;
+       case HDP_RELIABLE_DC:
+               if (app->role == HDP_SOURCE)
+                       return MCAP_CONFIGURATION_REJECTED;
+               break;
+       default:
+               /* Special case defined in HDP spec 3.4. When an invalid
+               * configuration is received we shall close the MCL when
+               * we are still processing the callback. */
+               close_device_con(dev, FALSE);
+               return MCAP_CONFIGURATION_REJECTED; /* not processed */
+       }
+
+       l = g_slist_find_custom(dev->channels, &mdlid, cmp_chan_mdlid);
+       if (l != NULL) {
+               struct hdp_channel *chan = l->data;
+               char *path;
+
+               path = g_strdup(chan->path);
+               g_dbus_unregister_interface(dev->conn, path, HEALTH_CHANNEL);
+               g_free(path);
+       }
+
+       if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
+                                               hdp2l2cap_mode(*conf), &err)) {
+               error("Error: %s", err->message);
+               g_error_free(err);
+               return MCAP_MDL_BUSY;
+       }
+
+       dev->ndc = create_channel(dev, *conf, NULL, mdlid, app, NULL);
+       if (dev->ndc == NULL)
+               return MCAP_MDL_BUSY;
+
+       return MCAP_SUCCESS;
+}
+
+static uint8_t hdp_mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data)
+{
+       struct hdp_device *dev = data;
+       struct hdp_channel *chan;
+       GError *err = NULL;
+       GSList *l;
+
+       l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl);
+       if (l == NULL)
+               return MCAP_INVALID_MDL;
+
+       chan = l->data;
+
+       if (dev->fr == NULL && chan->config != HDP_RELIABLE_DC &&
+                                               chan->mdep != HDP_MDEP_ECHO)
+               return MCAP_UNSPECIFIED_ERROR;
+
+       if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
+                                       hdp2l2cap_mode(chan->config), &err)) {
+               error("Error: %s", err->message);
+               g_error_free(err);
+               return MCAP_MDL_BUSY;
+       }
+
+       dev->ndc = hdp_channel_ref(chan);
+
+       return MCAP_SUCCESS;
+}
+
+gboolean hdp_set_mcl_cb(struct hdp_device *device, GError **err)
+{
+       gboolean ret;
+
+       if (device->mcl == NULL)
+               return FALSE;
+
+       ret = mcap_mcl_set_cb(device->mcl, device, err,
+               MCAP_MDL_CB_CONNECTED, hdp_mcap_mdl_connected_cb,
+               MCAP_MDL_CB_CLOSED, hdp_mcap_mdl_closed_cb,
+               MCAP_MDL_CB_DELETED, hdp_mcap_mdl_deleted_cb,
+               MCAP_MDL_CB_ABORTED, hdp_mcap_mdl_aborted_cb,
+               MCAP_MDL_CB_REMOTE_CONN_REQ, hdp_mcap_mdl_conn_req_cb,
+               MCAP_MDL_CB_REMOTE_RECONN_REQ, hdp_mcap_mdl_reconn_req_cb,
+               MCAP_MDL_CB_INVALID);
+
+       if (ret)
+               return TRUE;
+
+       error("Can't set mcl callbacks, closing mcl");
+       close_device_con(device, TRUE);
+
+       return FALSE;
+}
+
+static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
+{
+       struct hdp_device *hdp_device;
+       bdaddr_t addr;
+       GSList *l;
+
+       mcap_mcl_get_addr(mcl, &addr);
+       l = g_slist_find_custom(devices, &addr, cmp_dev_addr);
+       if (l == NULL) {
+               struct hdp_adapter *hdp_adapter = data;
+               struct btd_device *device;
+               char str[18];
+
+               ba2str(&addr, str);
+               device = adapter_get_device(connection,
+                                       hdp_adapter->btd_adapter, str);
+               if (!device)
+                       return;
+               hdp_device = create_health_device(connection, device);
+               if (!hdp_device)
+                       return;
+               devices = g_slist_append(devices, hdp_device);
+       } else
+               hdp_device = l->data;
+
+       hdp_device->mcl = mcap_mcl_ref(mcl);
+       hdp_device->mcl_conn = TRUE;
+
+       DBG("New mcl connected from  %s", device_get_path(hdp_device->dev));
+
+       hdp_set_mcl_cb(hdp_device, NULL);
+}
+
+static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data)
+{
+       struct hdp_device *hdp_device;
+       GSList *l;
+
+       l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
+       if (l == NULL)
+               return;
+
+       hdp_device = l->data;
+       hdp_device->mcl_conn = TRUE;
+
+       DBG("MCL reconnected %s", device_get_path(hdp_device->dev));
+
+       hdp_set_mcl_cb(hdp_device, NULL);
+}
+
+static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
+{
+       struct hdp_device *hdp_device;
+       GSList *l;
+
+       l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
+       if (l == NULL)
+               return;
+
+       hdp_device = l->data;
+       hdp_device->mcl_conn = FALSE;
+
+       DBG("Mcl disconnected %s", device_get_path(hdp_device->dev));
+}
+
+static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
+{
+       struct hdp_device *hdp_device;
+       const char *path;
+       GSList *l;
+
+       l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
+       if (l == NULL)
+               return;
+
+       hdp_device = l->data;
+       device_unref_mcl(hdp_device);
+
+       if (hdp_device->sdp_present)
+               return;
+
+       /* Because remote device hasn't announced an HDP record */
+       /* the Bluetooth daemon won't notify when the device shall */
+       /* be removed. Then we have to remove the HealthDevice */
+       /* interface manually */
+       path = device_get_path(hdp_device->dev);
+       g_dbus_unregister_interface(hdp_device->conn, path, HEALTH_DEVICE);
+       DBG("Mcl uncached %s", path);
+}
+
+static void check_devices_mcl(void)
+{
+       struct hdp_device *dev;
+       GSList *l, *to_delete = NULL;
+
+       for (l = devices; l; l = l->next) {
+               dev = l->data;
+               device_unref_mcl(dev);
+
+               if (!dev->sdp_present)
+                       to_delete = g_slist_append(to_delete, dev);
+               else
+                       remove_channels(dev);
+       }
+
+       for (l = to_delete; l; l = l->next) {
+               const char *path;
+
+               path = device_get_path(dev->dev);
+               g_dbus_unregister_interface(dev->conn, path, HEALTH_DEVICE);
+       }
+
+       g_slist_free(to_delete);
+}
+
+static void release_adapter_instance(struct hdp_adapter *hdp_adapter)
+{
+       if (hdp_adapter->mi == NULL)
+               return;
+
+       check_devices_mcl();
+       mcap_release_instance(hdp_adapter->mi);
+       mcap_instance_unref(hdp_adapter->mi);
+       hdp_adapter->mi = NULL;
+}
+
+static gboolean update_adapter(struct hdp_adapter *hdp_adapter)
+{
+       GError *err = NULL;
+       bdaddr_t addr;
+
+       if (applications == NULL) {
+               release_adapter_instance(hdp_adapter);
+               goto update;
+       }
+
+       if (hdp_adapter->mi != NULL)
+               goto update;
+
+       adapter_get_address(hdp_adapter->btd_adapter, &addr);
+       hdp_adapter->mi = mcap_create_instance(&addr, BT_IO_SEC_MEDIUM, 0, 0,
+                                       mcl_connected, mcl_reconnected,
+                                       mcl_disconnected, mcl_uncached,
+                                       NULL, /* CSP is not used by now */
+                                       hdp_adapter, &err);
+
+       if (hdp_adapter->mi == NULL) {
+               error("Error creating the MCAP instance: %s", err->message);
+               g_error_free(err);
+               return FALSE;
+       }
+
+       hdp_adapter->ccpsm = mcap_get_ctrl_psm(hdp_adapter->mi, &err);
+       if (err != NULL) {
+               error("Error getting MCAP control PSM: %s", err->message);
+               goto fail;
+       }
+
+       hdp_adapter->dcpsm = mcap_get_data_psm(hdp_adapter->mi, &err);
+       if (err != NULL) {
+               error("Error getting MCAP data PSM: %s", err->message);
+               goto fail;
+       }
+
+update:
+       if (hdp_update_sdp_record(hdp_adapter, applications))
+               return TRUE;
+       error("Error updating the SDP record");
+
+fail:
+       release_adapter_instance(hdp_adapter);
+       if (err != NULL)
+               g_error_free(err);
+
+       return FALSE;
+}
+
+int hdp_adapter_register(DBusConnection *conn, struct btd_adapter *adapter)
+{
+       struct hdp_adapter *hdp_adapter;
+
+       hdp_adapter = g_new0(struct hdp_adapter, 1);
+       hdp_adapter->btd_adapter = btd_adapter_ref(adapter);
+
+       if(!update_adapter(hdp_adapter))
+               goto fail;
+
+       adapters = g_slist_append(adapters, hdp_adapter);
+
+       return 0;
+
+fail:
+       btd_adapter_unref(hdp_adapter->btd_adapter);
+       g_free(hdp_adapter);
+       return -1;
+}
+
+void hdp_adapter_unregister(struct btd_adapter *adapter)
+{
+       struct hdp_adapter *hdp_adapter;
+       GSList *l;
+
+       l = g_slist_find_custom(adapters, adapter, cmp_adapter);
+
+       if (l == NULL)
+               return;
+
+       hdp_adapter = l->data;
+       adapters = g_slist_remove(adapters, hdp_adapter);
+       if (hdp_adapter->sdp_handler > 0)
+               remove_record_from_server(hdp_adapter->sdp_handler);
+       release_adapter_instance(hdp_adapter);
+       btd_adapter_unref(hdp_adapter->btd_adapter);
+       g_free(hdp_adapter);
+}
+
+static void delete_echo_channel_cb(GError *err, gpointer chan)
+{
+       if (err != NULL && err->code != MCAP_INVALID_MDL) {
+               /* TODO: Decide if more action is required here */
+               error("Error deleting echo channel: %s", err->message);
+               return;
+       }
+
+       health_channel_destroy(chan);
+}
+
+static void delete_echo_channel(struct hdp_channel *chan)
+{
+       GError *err = NULL;
+
+       if (!chan->dev->mcl_conn) {
+               error("Echo channel cannot be deleted: mcl closed");
+               return;
+       }
+
+       if (mcap_delete_mdl(chan->mdl, delete_echo_channel_cb,
+                               hdp_channel_ref(chan),
+                               (GDestroyNotify) hdp_channel_unref, &err))
+               return;
+
+       hdp_channel_unref(chan);
+       error("Error deleting the echo channel: %s", err->message);
+       g_error_free(err);
+
+       /* TODO: Decide if more action is required here */
+}
+
+static void abort_echo_channel_cb(GError *err, gpointer data)
+{
+       struct hdp_channel *chan = data;
+
+       if (err != NULL && err->code != MCAP_ERROR_INVALID_OPERATION) {
+               error("Aborting error: %s", err->message);
+               if (err->code == MCAP_INVALID_MDL) {
+                       /* MDL is removed from MCAP so we can */
+                       /* free the data channel without sending */
+                       /* a MD_DELETE_MDL_REQ */
+                       /* TODO review the above comment */
+                       /* hdp_channel_unref(chan); */
+               }
+               return;
+       }
+
+       delete_echo_channel(chan);
+}
+
+static void destroy_create_dc_data(gpointer data)
+{
+       struct hdp_create_dc *dc_data = data;
+
+       hdp_create_data_unref(dc_data);
+}
+
+static void *generate_echo_packet(void)
+{
+       uint8_t *buf;
+       int i;
+
+       buf = g_malloc(HDP_ECHO_LEN);
+       srand(time(NULL));
+
+       for(i = 0; i < HDP_ECHO_LEN; i++)
+               buf[i] = rand() % UINT8_MAX;
+
+       return buf;
+}
+
+static gboolean check_echo(GIOChannel *io_chan, GIOCondition cond,
+                                                               gpointer data)
+{
+       struct hdp_tmp_dc_data *hdp_conn =  data;
+       struct hdp_echo_data *edata = hdp_conn->hdp_chann->edata;
+       struct hdp_channel *chan = hdp_conn->hdp_chann;
+       uint8_t buf[MCAP_DC_MTU];
+       DBusMessage *reply;
+       gboolean value;
+       int fd, len;
+
+       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+               value = FALSE;
+               goto end;
+       }
+
+       fd = g_io_channel_unix_get_fd(io_chan);
+       len = read(fd, buf, sizeof(buf));
+
+       if (len != HDP_ECHO_LEN) {
+               value = FALSE;
+               goto end;
+       }
+
+       value = (memcmp(buf, edata->buf, len) == 0);
+
+end:
+       reply = g_dbus_create_reply(hdp_conn->msg, DBUS_TYPE_BOOLEAN, &value,
+                                                       DBUS_TYPE_INVALID);
+       g_dbus_send_message(hdp_conn->conn, reply);
+       g_source_remove(edata->tid);
+       edata->tid = 0;
+       g_free(edata->buf);
+       edata->buf = NULL;
+
+       if (!value)
+               close_device_con(chan->dev, FALSE);
+       else
+               delete_echo_channel(chan);
+       hdp_tmp_dc_data_unref(hdp_conn);
+
+       return FALSE;
+}
+
+static gboolean echo_timeout(gpointer data)
+{
+       struct hdp_channel *chan = data;
+       GIOChannel *io;
+       int fd;
+
+       error("Error: Echo request timeout");
+       chan->edata->tid = 0;
+
+       fd = mcap_mdl_get_fd(chan->mdl);
+       if (fd < 0)
+               return FALSE;
+
+       io = g_io_channel_unix_new(fd);
+       g_io_channel_shutdown(io, TRUE, NULL);
+
+       return FALSE;
+}
+
+static void hdp_echo_connect_cb(struct mcap_mdl *mdl, GError *err,
+                                                               gpointer data)
+{
+       struct hdp_tmp_dc_data *hdp_conn =  data;
+       struct hdp_echo_data *edata;
+       GError *gerr = NULL;
+       DBusMessage *reply;
+       GIOChannel *io;
+       int fd;
+
+       if (err != NULL) {
+               reply = g_dbus_create_error(hdp_conn->msg,
+                                               ERROR_INTERFACE ".HealthError",
+                                               "%s", err->message);
+               g_dbus_send_message(hdp_conn->conn, reply);
+
+               /* Send abort request because remote */
+               /* side is now in PENDING state. */
+               if (!mcap_mdl_abort(hdp_conn->hdp_chann->mdl,
+                                       abort_echo_channel_cb,
+                                       hdp_channel_ref(hdp_conn->hdp_chann),
+                                       (GDestroyNotify) hdp_channel_unref,
+                                       &gerr)) {
+                       error("%s", gerr->message);
+                       g_error_free(gerr);
+                       hdp_channel_unref(hdp_conn->hdp_chann);
+               }
+               return;
+       }
+
+       fd = mcap_mdl_get_fd(hdp_conn->hdp_chann->mdl);
+       if (fd < 0) {
+               reply = g_dbus_create_error(hdp_conn->msg,
+                                               ERROR_INTERFACE ".HealthError",
+                                               "Can't write in echo channel");
+               g_dbus_send_message(hdp_conn->conn, reply);
+               delete_echo_channel(hdp_conn->hdp_chann);
+               return;
+       }
+
+       edata = hdp_conn->hdp_chann->edata;
+       edata->buf = generate_echo_packet();
+       send_echo_data(fd, edata->buf, HDP_ECHO_LEN);
+
+       io = g_io_channel_unix_new(fd);
+       g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
+                       check_echo, hdp_tmp_dc_data_ref(hdp_conn));
+
+       edata->tid = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
+                                       ECHO_TIMEOUT, echo_timeout,
+                                       hdp_channel_ref(hdp_conn->hdp_chann),
+                                       (GDestroyNotify) hdp_channel_unref);
+
+       g_io_channel_unref(io);
+}
+
+static void delete_mdl_cb(GError *err, gpointer data)
+{
+       if (err != NULL)
+               error("Deleting error: %s", err->message);
+}
+
+static void abort_and_del_mdl_cb(GError *err, gpointer data)
+{
+       struct mcap_mdl *mdl = data;
+       GError *gerr = NULL;
+
+       if (err != NULL) {
+               error("%s", err->message);
+               if (err->code == MCAP_INVALID_MDL) {
+                       /* MDL is removed from MCAP so we don't */
+                       /* need to delete it. */
+                       return;
+               }
+       }
+
+       if (!mcap_delete_mdl(mdl, delete_mdl_cb, NULL, NULL, &gerr)) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+       }
+}
+
+static void abort_mdl_connection_cb(GError *err, gpointer data)
+{
+       struct hdp_tmp_dc_data *hdp_conn = data;
+       struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
+
+       if (err != NULL)
+               error("Aborting error: %s", err->message);
+
+       /* Connection operation has failed but we have to */
+       /* notify the channel created at MCAP level */
+       if (hdp_chann->mdep != HDP_MDEP_ECHO)
+               g_dbus_emit_signal(hdp_conn->conn,
+                                       device_get_path(hdp_chann->dev->dev),
+                                       HEALTH_DEVICE,
+                                       "ChannelConnected",
+                                       DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+                                       DBUS_TYPE_INVALID);
+}
+
+static void hdp_mdl_conn_cb(struct mcap_mdl *mdl, GError *err, gpointer data)
+{
+       struct hdp_tmp_dc_data *hdp_conn =  data;
+       struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
+       struct hdp_device *dev = hdp_chann->dev;
+       DBusMessage *reply;
+       GError *gerr = NULL;
+
+       if (err != NULL) {
+               error("%s", err->message);
+               reply = g_dbus_create_reply(hdp_conn->msg,
+                                       DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+                                       DBUS_TYPE_INVALID);
+               g_dbus_send_message(hdp_conn->conn, reply);
+
+               /* Send abort request because remote side */
+               /* is now in PENDING state */
+               if (!mcap_mdl_abort(hdp_chann->mdl, abort_mdl_connection_cb,
+                                       hdp_tmp_dc_data_ref(hdp_conn),
+                                       hdp_tmp_dc_data_destroy, &gerr)) {
+                       hdp_tmp_dc_data_unref(hdp_conn);
+                       error("%s", gerr->message);
+                       g_error_free(gerr);
+               }
+               return;
+       }
+
+       reply = g_dbus_create_reply(hdp_conn->msg,
+                                       DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+                                       DBUS_TYPE_INVALID);
+       g_dbus_send_message(hdp_conn->conn, reply);
+
+       g_dbus_emit_signal(hdp_conn->conn,
+                                       device_get_path(hdp_chann->dev->dev),
+                                       HEALTH_DEVICE,
+                                       "ChannelConnected",
+                                       DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+                                       DBUS_TYPE_INVALID);
+
+       if (!check_channel_conf(hdp_chann)) {
+               close_mdl(hdp_chann);
+               return;
+       }
+
+       if (dev->fr != NULL)
+               return;
+
+       dev->fr = hdp_channel_ref(hdp_chann);
+
+       emit_property_changed(dev->conn, device_get_path(dev->dev),
+                                       HEALTH_DEVICE, "MainChannel",
+                                       DBUS_TYPE_OBJECT_PATH, &dev->fr->path);
+}
+
+static void device_create_mdl_cb(struct mcap_mdl *mdl, uint8_t conf,
+                                               GError *err, gpointer data)
+{
+       struct hdp_create_dc *user_data = data;
+       struct hdp_tmp_dc_data *hdp_conn;
+       struct hdp_channel *hdp_chan;
+       GError *gerr = NULL;
+       DBusMessage *reply;
+
+       if (err != NULL) {
+               reply = g_dbus_create_error(user_data->msg,
+                                       ERROR_INTERFACE ".HealthError",
+                                       "%s", err->message);
+               g_dbus_send_message(user_data->conn, reply);
+               return;
+       }
+
+       if (user_data->mdep != HDP_MDEP_ECHO &&
+                               user_data->config == HDP_NO_PREFERENCE_DC) {
+               if (user_data->dev->fr == NULL && conf != HDP_RELIABLE_DC) {
+                       g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+                                       "Data channel aborted, first data "
+                                       "channel should be reliable");
+                       goto fail;
+               } else if (conf == HDP_NO_PREFERENCE_DC ||
+                                               conf > HDP_STREAMING_DC) {
+                       g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+                                                       "Data channel aborted, "
+                                                       "configuration error");
+                       goto fail;
+               }
+       }
+
+       hdp_chan = create_channel(user_data->dev, conf, mdl,
+                                                       mcap_mdl_get_mdlid(mdl),
+                                                       user_data->app, &gerr);
+       if (hdp_chan == NULL)
+               goto fail;
+
+       hdp_conn = g_new0(struct hdp_tmp_dc_data, 1);
+       hdp_conn->msg = dbus_message_ref(user_data->msg);
+       hdp_conn->conn = dbus_connection_ref(user_data->conn);
+       hdp_conn->hdp_chann = hdp_chan;
+       hdp_conn->cb = user_data->cb;
+       hdp_chan->mdep = user_data->mdep;
+
+       if (hdp_get_dcpsm(hdp_chan->dev, hdp_get_dcpsm_cb,
+                                               hdp_tmp_dc_data_ref(hdp_conn),
+                                               hdp_tmp_dc_data_destroy, &gerr))
+               return;
+
+       error("%s", gerr->message);
+       g_error_free(gerr);
+
+       reply = g_dbus_create_reply(hdp_conn->msg,
+                                       DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
+                                       DBUS_TYPE_INVALID);
+       g_dbus_send_message(hdp_conn->conn, reply);
+       hdp_tmp_dc_data_unref(hdp_conn);
+
+       /* Send abort request because remote side is now in PENDING state */
+       if (!mcap_mdl_abort(hdp_chan->mdl, abort_mdl_connection_cb,
+                                       hdp_tmp_dc_data_ref(hdp_conn),
+                                       hdp_tmp_dc_data_destroy, &gerr)) {
+               hdp_tmp_dc_data_unref(hdp_conn);
+               error("%s", gerr->message);
+               g_error_free(gerr);
+       }
+
+       return;
+
+fail:
+       reply = g_dbus_create_error(user_data->msg,
+                                               ERROR_INTERFACE ".HealthError",
+                                               "%s", gerr->message);
+       g_dbus_send_message(user_data->conn, reply);
+       g_error_free(gerr);
+
+       /* Send abort request because remote side is now in PENDING */
+       /* state. Then we have to delete it because we couldn't */
+       /* register the HealthChannel interface */
+       if (!mcap_mdl_abort(mdl, abort_and_del_mdl_cb, mcap_mdl_ref(mdl),
+                               (GDestroyNotify) mcap_mdl_unref, &gerr)) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               mcap_mdl_unref(mdl);
+       }
+}
+
+static void device_create_dc_cb(gpointer user_data, GError *err)
+{
+       struct hdp_create_dc *data = user_data;
+       DBusMessage *reply;
+       GError *gerr = NULL;
+
+       if (err != NULL) {
+               reply = g_dbus_create_error(data->msg,
+                                       ERROR_INTERFACE ".HealthError",
+                                       "%s", err->message);
+               g_dbus_send_message(data->conn, reply);
+               return;
+       }
+
+       if (data->dev->mcl == NULL) {
+               g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+                               "Mcl was closed");
+               goto fail;
+       }
+
+       hdp_create_data_ref(data);
+
+       if (mcap_create_mdl(data->dev->mcl, data->mdep, data->config,
+                                               device_create_mdl_cb, data,
+                                               destroy_create_dc_data, &gerr))
+               return;
+       hdp_create_data_unref(data);
+
+fail:
+       reply = g_dbus_create_error(data->msg, ERROR_INTERFACE ".HealthError",
+                                                       "%s", gerr->message);
+       g_error_free(gerr);
+       g_dbus_send_message(data->conn, reply);
+}
+
+static DBusMessage *device_echo(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct hdp_device *device = user_data;
+       struct hdp_create_dc *data;
+       DBusMessage *reply;
+       GError *err = NULL;
+
+       data = g_new0(struct hdp_create_dc, 1);
+       data->dev = health_device_ref(device);
+       data->mdep = HDP_MDEP_ECHO;
+       data->config = HDP_RELIABLE_DC;
+       data->msg = dbus_message_ref(msg);
+       data->conn = dbus_connection_ref(conn);
+       data->cb = hdp_echo_connect_cb;
+       hdp_create_data_ref(data);
+
+       if (device->mcl_conn && device->mcl != NULL) {
+               if (mcap_create_mdl(device->mcl, data->mdep, data->config,
+                                               device_create_mdl_cb, data,
+                                               destroy_create_dc_data, &err))
+                       return NULL;
+               goto fail;
+       }
+
+       if (hdp_establish_mcl(data->dev, device_create_dc_cb,
+                                       data, destroy_create_dc_data, &err))
+               return NULL;
+
+fail:
+       reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+                                                       "%s", err->message);
+       g_error_free(err);
+       hdp_create_data_unref(data);
+       return reply;
+}
+
+static void device_get_mdep_cb(uint8_t mdep, gpointer data, GError *err)
+{
+       struct hdp_create_dc *dc_data, *user_data = data;
+       DBusMessage *reply;
+       GError *gerr = NULL;
+
+       if (err != NULL) {
+               reply = g_dbus_create_error(user_data->msg,
+                                               ERROR_INTERFACE ".HealthError",
+                                               "%s", err->message);
+               g_dbus_send_message(user_data->conn, reply);
+               return;
+       }
+
+       dc_data = hdp_create_data_ref(user_data);
+       dc_data->mdep = mdep;
+
+       if (user_data->dev->mcl_conn) {
+               device_create_dc_cb(dc_data, NULL);
+               hdp_create_data_unref(dc_data);
+               return;
+       }
+
+       if (hdp_establish_mcl(dc_data->dev, device_create_dc_cb,
+                                       dc_data, destroy_create_dc_data, &gerr))
+               return;
+
+       reply = g_dbus_create_error(user_data->msg,
+                                               ERROR_INTERFACE ".HealthError",
+                                               "%s", gerr->message);
+       hdp_create_data_unref(dc_data);
+       g_error_free(gerr);
+       g_dbus_send_message(user_data->conn, reply);
+}
+
+static DBusMessage *device_create_channel(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct hdp_device *device = user_data;
+       struct hdp_application *app;
+       struct hdp_create_dc *data;
+       char *app_path, *conf;
+       DBusMessage *reply;
+       GError *err = NULL;
+       uint8_t config;
+       GSList *l;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &app_path,
+                                                       DBUS_TYPE_STRING, &conf,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       l = g_slist_find_custom(applications, app_path, cmp_app);
+       if (l == NULL)
+               return btd_error_invalid_args(msg);
+
+       app = l->data;
+
+       if (g_ascii_strcasecmp("Reliable", conf) == 0)
+               config = HDP_RELIABLE_DC;
+       else if (g_ascii_strcasecmp("Streaming", conf) == 0)
+               config = HDP_STREAMING_DC;
+       else if (g_ascii_strcasecmp("Any", conf) == 0)
+               config = HDP_NO_PREFERENCE_DC;
+       else
+               return btd_error_invalid_args(msg);
+
+       if (app->role == HDP_SINK && config != HDP_NO_PREFERENCE_DC)
+               return btd_error_invalid_args(msg);
+
+       if (app->role == HDP_SOURCE && config == HDP_NO_PREFERENCE_DC)
+               return btd_error_invalid_args(msg);
+
+       if (!device->fr && config == HDP_STREAMING_DC)
+               return btd_error_invalid_args(msg);
+
+       data = g_new0(struct hdp_create_dc, 1);
+       data->dev = health_device_ref(device);
+       data->config = config;
+       data->app = hdp_application_ref(app);
+       data->msg = dbus_message_ref(msg);
+       data->conn = dbus_connection_ref(conn);
+       data->cb = hdp_mdl_conn_cb;
+
+       if (hdp_get_mdep(device, l->data, device_get_mdep_cb,
+                                               hdp_create_data_ref(data),
+                                               destroy_create_dc_data, &err))
+               return NULL;
+
+       reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+                                                       "%s", err->message);
+       g_error_free(err);
+       hdp_create_data_unref(data);
+       return reply;
+}
+
+static void hdp_mdl_delete_cb(GError *err, gpointer data)
+{
+       struct hdp_tmp_dc_data *del_data = data;
+       DBusMessage *reply;
+       char *path;
+
+       if (err != NULL && err->code != MCAP_INVALID_MDL) {
+               reply = g_dbus_create_error(del_data->msg,
+                                               ERROR_INTERFACE ".HealthError",
+                                               "%s", err->message);
+               g_dbus_send_message(del_data->conn, reply);
+               return;
+       }
+
+       path = g_strdup(del_data->hdp_chann->path);
+       g_dbus_unregister_interface(del_data->conn, path, HEALTH_CHANNEL);
+       g_free(path);
+
+       reply = g_dbus_create_reply(del_data->msg, DBUS_TYPE_INVALID);
+       g_dbus_send_message(del_data->conn, reply);
+}
+
+static void hdp_continue_del_cb(gpointer user_data, GError *err)
+{
+       struct hdp_tmp_dc_data *del_data = user_data;
+       GError *gerr = NULL;
+       DBusMessage *reply;
+
+       if (err != NULL) {
+               reply = g_dbus_create_error(del_data->msg,
+                                       ERROR_INTERFACE ".HealthError",
+                                       "%s", err->message);
+               g_dbus_send_message(del_data->conn, reply);
+               return;
+       }
+
+       if (mcap_delete_mdl(del_data->hdp_chann->mdl, hdp_mdl_delete_cb,
+                                               hdp_tmp_dc_data_ref(del_data),
+                                               hdp_tmp_dc_data_destroy, &gerr))
+                       return;
+
+       reply = g_dbus_create_error(del_data->msg,
+                                               ERROR_INTERFACE ".HealthError",
+                                               "%s", gerr->message);
+       hdp_tmp_dc_data_unref(del_data);
+       g_error_free(gerr);
+       g_dbus_send_message(del_data->conn, reply);
+}
+
+static DBusMessage *device_destroy_channel(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct hdp_device *device = user_data;
+       struct hdp_tmp_dc_data *del_data;
+       struct hdp_channel *hdp_chan;
+       DBusMessage *reply;
+       GError *err = NULL;
+       char *path;
+       GSList *l;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID)){
+               return btd_error_invalid_args(msg);
+       }
+
+       l = g_slist_find_custom(device->channels, path, cmp_chan_path);
+       if (l == NULL)
+               return btd_error_invalid_args(msg);
+
+       hdp_chan = l->data;
+       del_data = g_new0(struct hdp_tmp_dc_data, 1);
+       del_data->msg = dbus_message_ref(msg);
+       del_data->conn = dbus_connection_ref(conn);
+       del_data->hdp_chann = hdp_channel_ref(hdp_chan);
+
+       if (device->mcl_conn) {
+               if (mcap_delete_mdl(hdp_chan->mdl, hdp_mdl_delete_cb,
+                                               hdp_tmp_dc_data_ref(del_data),
+                                               hdp_tmp_dc_data_destroy, &err))
+                       return NULL;
+               goto fail;
+       }
+
+       if (hdp_establish_mcl(device, hdp_continue_del_cb,
+                                               hdp_tmp_dc_data_ref(del_data),
+                                               hdp_tmp_dc_data_destroy, &err))
+               return NULL;
+
+fail:
+       reply = g_dbus_create_error(msg, ERROR_INTERFACE ".HealthError",
+                                                       "%s", err->message);
+       hdp_tmp_dc_data_unref(del_data);
+       g_error_free(err);
+       return reply;
+}
+
+static DBusMessage *device_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct hdp_device *device = user_data;
+       DBusMessageIter iter, dict;
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       if (device->fr != NULL)
+               dict_append_entry(&dict, "MainChannel", DBUS_TYPE_OBJECT_PATH,
+                                                       &device->fr->path);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void health_device_destroy(void *data)
+{
+       struct hdp_device *device = data;
+
+       DBG("Unregistered interface %s on path %s", HEALTH_DEVICE,
+                                               device_get_path(device->dev));
+
+       remove_channels(device);
+       if (device->ndc != NULL) {
+               hdp_channel_unref(device->ndc);
+               device->ndc = NULL;
+       }
+
+       devices = g_slist_remove(devices, device);
+       health_device_unref(device);
+}
+
+static const GDBusMethodTable health_device_methods[] = {
+       { GDBUS_ASYNC_METHOD("Echo",
+                       NULL, GDBUS_ARGS({ "value", "b" }), device_echo) },
+       { GDBUS_ASYNC_METHOD("CreateChannel",
+                       GDBUS_ARGS({ "application", "o" },
+                                       { "configuration", "s" }),
+                       GDBUS_ARGS({ "channel", "o" }),
+                       device_create_channel) },
+       { GDBUS_ASYNC_METHOD("DestroyChannel",
+                       GDBUS_ARGS({ "channel", "o" }), NULL,
+                       device_destroy_channel) },
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       device_get_properties) },
+       { }
+};
+
+static const GDBusSignalTable health_device_signals[] = {
+       { GDBUS_SIGNAL("ChannelConnected",
+                       GDBUS_ARGS({ "channel", "o" })) },
+       { GDBUS_SIGNAL("ChannelDeleted",
+                       GDBUS_ARGS({ "channel", "o" })) },
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static struct hdp_device *create_health_device(DBusConnection *conn,
+                                               struct btd_device *device)
+{
+       struct btd_adapter *adapter = device_get_adapter(device);
+       const gchar *path = device_get_path(device);
+       struct hdp_device *dev;
+       GSList *l;
+
+       if (device == NULL)
+               return NULL;
+
+       dev = g_new0(struct hdp_device, 1);
+       dev->conn = dbus_connection_ref(conn);
+       dev->dev = btd_device_ref(device);
+       health_device_ref(dev);
+
+       l = g_slist_find_custom(adapters, adapter, cmp_adapter);
+       if (l == NULL)
+               goto fail;
+
+       dev->hdp_adapter = l->data;
+
+       if (!g_dbus_register_interface(conn, path,
+                                       HEALTH_DEVICE,
+                                       health_device_methods,
+                                       health_device_signals, NULL,
+                                       dev, health_device_destroy)) {
+               error("D-Bus failed to register %s interface", HEALTH_DEVICE);
+               goto fail;
+       }
+
+       DBG("Registered interface %s on path %s", HEALTH_DEVICE, path);
+       return dev;
+
+fail:
+       health_device_unref(dev);
+       return NULL;
+}
+
+int hdp_device_register(DBusConnection *conn, struct btd_device *device)
+{
+       struct hdp_device *hdev;
+       GSList *l;
+
+       l = g_slist_find_custom(devices, device, cmp_device);
+       if (l != NULL) {
+               hdev = l->data;
+               hdev->sdp_present = TRUE;
+               return 0;
+       }
+
+       hdev = create_health_device(conn, device);
+       if (hdev == NULL)
+               return -1;
+
+       hdev->sdp_present = TRUE;
+
+       devices = g_slist_prepend(devices, hdev);
+       return 0;
+}
+
+void hdp_device_unregister(struct btd_device *device)
+{
+       struct hdp_device *hdp_dev;
+       const char *path;
+       GSList *l;
+
+       l = g_slist_find_custom(devices, device, cmp_device);
+       if (l == NULL)
+               return;
+
+       hdp_dev = l->data;
+       path = device_get_path(hdp_dev->dev);
+       g_dbus_unregister_interface(hdp_dev->conn, path, HEALTH_DEVICE);
+}
+
+int hdp_manager_start(DBusConnection *conn)
+{
+       DBG("Starting Health manager");
+
+       if (!g_dbus_register_interface(conn, MANAGER_PATH,
+                                       HEALTH_MANAGER,
+                                       health_manager_methods, NULL, NULL,
+                                       NULL, manager_path_unregister)) {
+               error("D-Bus failed to register %s interface", HEALTH_MANAGER);
+               return -1;
+       }
+
+       connection = dbus_connection_ref(conn);
+
+       return 0;
+}
+
+void hdp_manager_stop(void)
+{
+       g_dbus_unregister_interface(connection, MANAGER_PATH, HEALTH_MANAGER);
+
+       dbus_connection_unref(connection);
+       DBG("Stopped Health manager");
+}
+
+struct hdp_device *health_device_ref(struct hdp_device *hdp_dev)
+{
+       hdp_dev->ref++;
+
+       DBG("health_device_ref(%p): ref=%d", hdp_dev, hdp_dev->ref);
+
+       return hdp_dev;
+}
+
+void health_device_unref(struct hdp_device *hdp_dev)
+{
+       hdp_dev->ref--;
+
+       DBG("health_device_unref(%p): ref=%d", hdp_dev, hdp_dev->ref);
+
+       if (hdp_dev->ref > 0)
+               return;
+
+       free_health_device(hdp_dev);
+}
diff --git a/health/hdp.h b/health/hdp.h
new file mode 100644 (file)
index 0000000..39f0441
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int hdp_adapter_register(DBusConnection *conn, struct btd_adapter *btd_adapter);
+void hdp_adapter_unregister(struct btd_adapter *btd_adapter);
+
+int hdp_device_register(DBusConnection *conn, struct btd_device *device);
+void hdp_device_unregister(struct btd_device *device);
+
+int hdp_manager_start(DBusConnection *conn);
+void hdp_manager_stop(void);
+
+gboolean hdp_set_mcl_cb(struct hdp_device *device, GError **err);
diff --git a/health/hdp_main.c b/health/hdp_main.c
new file mode 100644 (file)
index 0000000..9367e73
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "hdp_manager.h"
+
+static DBusConnection *connection = NULL;
+
+static int hdp_init(void)
+{
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -EIO;
+
+       if (hdp_manager_init(connection) < 0) {
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void hdp_exit(void)
+{
+       hdp_manager_exit();
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
+
+BLUETOOTH_PLUGIN_DEFINE(health, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, hdp_init, hdp_exit)
diff --git a/health/hdp_manager.c b/health/hdp_manager.c
new file mode 100644 (file)
index 0000000..ffaed5d
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+
+#include <btio.h>
+#include <adapter.h>
+#include <device.h>
+#include <glib-helper.h>
+#include <log.h>
+
+#include "hdp_types.h"
+
+#include "hdp_manager.h"
+#include "hdp.h"
+
+static DBusConnection *connection = NULL;
+
+static int hdp_adapter_probe(struct btd_adapter *adapter)
+{
+       return hdp_adapter_register(connection, adapter);
+}
+
+static void hdp_adapter_remove(struct btd_adapter *adapter)
+{
+       hdp_adapter_unregister(adapter);
+}
+
+static struct btd_adapter_driver hdp_adapter_driver = {
+       .name   = "hdp-adapter-driver",
+       .probe  = hdp_adapter_probe,
+       .remove = hdp_adapter_remove,
+};
+
+static int hdp_driver_probe(struct btd_device *device, GSList *uuids)
+{
+       return hdp_device_register(connection, device);
+}
+
+static void hdp_driver_remove(struct btd_device *device)
+{
+       hdp_device_unregister(device);
+}
+
+static struct btd_device_driver hdp_device_driver = {
+       .name   = "hdp-device-driver",
+       .uuids  = BTD_UUIDS(HDP_UUID, HDP_SOURCE_UUID, HDP_SINK_UUID),
+       .probe  = hdp_driver_probe,
+       .remove = hdp_driver_remove
+};
+
+int hdp_manager_init(DBusConnection *conn)
+{
+       if (hdp_manager_start(conn) < 0)
+               return -1;
+
+       connection = dbus_connection_ref(conn);
+       btd_register_adapter_driver(&hdp_adapter_driver);
+       btd_register_device_driver(&hdp_device_driver);
+
+       return 0;
+}
+
+void hdp_manager_exit(void)
+{
+       btd_unregister_device_driver(&hdp_device_driver);
+       btd_unregister_adapter_driver(&hdp_adapter_driver);
+       hdp_manager_stop();
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
diff --git a/health/hdp_manager.h b/health/hdp_manager.h
new file mode 100644 (file)
index 0000000..d39f190
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int hdp_manager_init(DBusConnection *conn);
+void hdp_manager_exit(void);
diff --git a/health/hdp_types.h b/health/hdp_types.h
new file mode 100644 (file)
index 0000000..7f8b015
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __HDP_TYPES_H__
+#define __HDP_TYPES_H__
+
+#define MANAGER_PATH           "/org/bluez"
+
+#define HEALTH_MANAGER         "org.bluez.HealthManager"
+#define HEALTH_DEVICE          "org.bluez.HealthDevice"
+#define HEALTH_CHANNEL         "org.bluez.HealthChannel"
+
+#define HDP_VERSION            0x0100
+
+#define HDP_SERVICE_NAME       "Bluez HDP"
+#define HDP_SERVICE_DSC                "A Bluez health device profile implementation"
+#define HDP_SERVICE_PROVIDER   "Bluez"
+
+#define HDP_MDEP_ECHO          0x00
+#define HDP_MDEP_INITIAL       0x01
+#define HDP_MDEP_FINAL         0x7F
+
+#define HDP_ERROR              g_quark_from_static_string("hdp-error-quark")
+
+#define HDP_NO_PREFERENCE_DC   0x00
+#define HDP_RELIABLE_DC                0x01
+#define HDP_STREAMING_DC       0x02
+
+#define HDP_SINK_ROLE_AS_STRING                "sink"
+#define HDP_SOURCE_ROLE_AS_STRING      "source"
+
+typedef enum {
+       HDP_SOURCE = 0x00,
+       HDP_SINK = 0x01
+} HdpRole;
+
+typedef enum {
+       HDP_DIC_PARSE_ERROR,
+       HDP_DIC_ENTRY_PARSE_ERROR,
+       HDP_CONNECTION_ERROR,
+       HDP_UNSPECIFIED_ERROR,
+       HDP_UNKNOWN_ERROR
+} HdpError;
+
+enum data_specs {
+       DATA_EXCHANGE_SPEC_11073 = 0x01
+};
+
+struct hdp_application {
+       DBusConnection          *conn;          /* For dbus watcher */
+       char                    *path;          /* The path of the application */
+       uint16_t                data_type;      /* Data type handled for this application */
+       gboolean                data_type_set;  /* Flag for dictionary parsing */
+       uint8_t                 role;           /* Role of this application */
+       gboolean                role_set;       /* Flag for dictionary parsing */
+       uint8_t                 chan_type;      /* QoS preferred by source applications */
+       gboolean                chan_type_set;  /* Flag for dictionary parsing */
+       char                    *description;   /* Options description for SDP record */
+       uint8_t                 id;             /* The identification is also the mdepid */
+       char                    *oname;         /* Name of the owner application */
+       guint                   dbus_watcher;   /* Watch for clients disconnection */
+       gint                    ref;            /* Reference counter */
+};
+
+struct hdp_adapter {
+       struct btd_adapter      *btd_adapter;   /* Bluetooth adapter */
+       struct mcap_instance    *mi;            /* Mcap instance in */
+       uint16_t                ccpsm;          /* Control channel psm */
+       uint16_t                dcpsm;          /* Data channel psm */
+       uint32_t                sdp_handler;    /* SDP record handler */
+       uint32_t                record_state;   /* Service record state */
+};
+
+struct hdp_device {
+       DBusConnection          *conn;          /* For name listener handling */
+       struct btd_device       *dev;           /* Device reference */
+       struct hdp_adapter      *hdp_adapter;   /* hdp_adapater */
+       struct mcap_mcl         *mcl;           /* The mcap control channel */
+       gboolean                mcl_conn;       /* Mcl status */
+       gboolean                sdp_present;    /* Has an sdp record */
+       GSList                  *channels;      /* Data Channel list */
+       struct hdp_channel      *ndc;           /* Data channel being negotiated */
+       struct hdp_channel      *fr;            /* First reliable data channel */
+       gint                    ref;            /* Reference counting */
+};
+
+struct hdp_echo_data;
+
+struct hdp_channel {
+       struct hdp_device       *dev;           /* Device where this channel belongs */
+       struct hdp_application  *app;           /* Application */
+       struct mcap_mdl         *mdl;           /* The data channel reference */
+       char                    *path;          /* The path of the channel */
+       uint8_t                 config;         /* Channel configuration */
+       uint8_t                 mdep;           /* Remote MDEP */
+       uint16_t                mdlid;          /* Data channel Id */
+       uint16_t                imtu;           /* Channel incoming MTU */
+       uint16_t                omtu;           /* Channel outgoing MTU */
+       struct hdp_echo_data    *edata;         /* private data used by echo channels */
+       gint                    ref;            /* Reference counter */
+};
+
+#endif /* __HDP_TYPES_H__ */
diff --git a/health/hdp_util.c b/health/hdp_util.c
new file mode 100644 (file)
index 0000000..744e390
--- /dev/null
@@ -0,0 +1,1218 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include <adapter.h>
+#include <device.h>
+
+#include <sdpd.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+#include <sdp-client.h>
+#include <glib-helper.h>
+
+#include <btio.h>
+
+#include <log.h>
+
+#include "mcap.h"
+#include "mcap_lib.h"
+#include "hdp_types.h"
+#include "hdp.h"
+#include "hdp_util.h"
+
+typedef gboolean (*parse_item_f)(DBusMessageIter *iter, gpointer user_data,
+                                                               GError **err);
+
+struct dict_entry_func {
+       char            *key;
+       parse_item_f    func;
+};
+
+struct get_mdep_data {
+       struct hdp_application  *app;
+       gpointer                data;
+       hdp_continue_mdep_f     func;
+       GDestroyNotify          destroy;
+};
+
+struct conn_mcl_data {
+       int                     refs;
+       gpointer                data;
+       hdp_continue_proc_f     func;
+       GDestroyNotify          destroy;
+       struct hdp_device       *dev;
+};
+
+struct get_dcpsm_data {
+       gpointer                data;
+       hdp_continue_dcpsm_f    func;
+       GDestroyNotify          destroy;
+};
+
+static gboolean parse_dict_entry(struct dict_entry_func dict_context[],
+                                                       DBusMessageIter *iter,
+                                                       GError **err,
+                                                       gpointer user_data)
+{
+       DBusMessageIter entry;
+       char *key;
+       int ctype, i;
+       struct dict_entry_func df;
+
+       dbus_message_iter_recurse(iter, &entry);
+       ctype = dbus_message_iter_get_arg_type(&entry);
+       if (ctype != DBUS_TYPE_STRING) {
+               g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+                       "Dictionary entries should have a string as key");
+               return FALSE;
+       }
+
+       dbus_message_iter_get_basic(&entry, &key);
+       dbus_message_iter_next(&entry);
+       /* Find function and call it */
+       for (i = 0, df = dict_context[0]; df.key; i++, df = dict_context[i]) {
+               if (g_ascii_strcasecmp(df.key, key) == 0)
+                       return df.func(&entry, user_data, err);
+       }
+
+       g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+                       "No function found for parsing value for key %s", key);
+       return FALSE;
+}
+
+static gboolean parse_dict(struct dict_entry_func dict_context[],
+                                                       DBusMessageIter *iter,
+                                                       GError **err,
+                                                       gpointer user_data)
+{
+       int ctype;
+       DBusMessageIter dict;
+
+       ctype = dbus_message_iter_get_arg_type(iter);
+       if (ctype != DBUS_TYPE_ARRAY) {
+               g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+                                       "Dictionary should be an array");
+               return FALSE;
+       }
+
+       dbus_message_iter_recurse(iter, &dict);
+       while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+                                                       DBUS_TYPE_INVALID) {
+               if (ctype != DBUS_TYPE_DICT_ENTRY) {
+                       g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+                                               "Dictionary array should "
+                                               "contain dict entries");
+                       return FALSE;
+               }
+
+               /* Start parsing entry */
+               if (!parse_dict_entry(dict_context, &dict, err,
+                                                       user_data))
+                       return FALSE;
+               /* Finish entry parsing */
+
+               dbus_message_iter_next(&dict);
+       }
+
+       return TRUE;
+}
+
+static gboolean parse_data_type(DBusMessageIter *iter, gpointer data,
+                                                               GError **err)
+{
+       struct hdp_application *app = data;
+       DBusMessageIter *value;
+       int ctype;
+
+       ctype = dbus_message_iter_get_arg_type(iter);
+       value = iter;
+       if (ctype == DBUS_TYPE_VARIANT) {
+               DBusMessageIter variant;
+
+               /* Get value inside the variable */
+               dbus_message_iter_recurse(iter, &variant);
+               ctype = dbus_message_iter_get_arg_type(&variant);
+               value = &variant;
+       }
+
+       if (ctype != DBUS_TYPE_UINT16) {
+               g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+                       "Final value for data type should be uint16");
+               return FALSE;
+       }
+
+       dbus_message_iter_get_basic(value, &app->data_type);
+       app->data_type_set = TRUE;
+       return TRUE;
+}
+
+static gboolean parse_role(DBusMessageIter *iter, gpointer data, GError **err)
+{
+       struct hdp_application *app = data;
+       DBusMessageIter *string;
+       int ctype;
+       const char *role;
+
+       ctype = dbus_message_iter_get_arg_type(iter);
+       if (ctype == DBUS_TYPE_VARIANT) {
+               DBusMessageIter value;
+
+               /* Get value inside the variable */
+               dbus_message_iter_recurse(iter, &value);
+               ctype = dbus_message_iter_get_arg_type(&value);
+               string = &value;
+       } else {
+               string = iter;
+       }
+
+       if (ctype != DBUS_TYPE_STRING) {
+               g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+                               "Value data spec should be variable or string");
+               return FALSE;
+       }
+
+       dbus_message_iter_get_basic(string, &role);
+       if (g_ascii_strcasecmp(role, HDP_SINK_ROLE_AS_STRING) == 0) {
+               app->role = HDP_SINK;
+       } else if (g_ascii_strcasecmp(role, HDP_SOURCE_ROLE_AS_STRING) == 0) {
+               app->role = HDP_SOURCE;
+       } else {
+               g_set_error(err, HDP_ERROR, HDP_UNSPECIFIED_ERROR,
+                       "Role value should be \"source\" or \"sink\"");
+               return FALSE;
+       }
+
+       app->role_set = TRUE;
+
+       return TRUE;
+}
+
+static gboolean parse_desc(DBusMessageIter *iter, gpointer data, GError **err)
+{
+       struct hdp_application *app = data;
+       DBusMessageIter *string;
+       int ctype;
+       const char *desc;
+
+       ctype = dbus_message_iter_get_arg_type(iter);
+       if (ctype == DBUS_TYPE_VARIANT) {
+               DBusMessageIter variant;
+
+               /* Get value inside the variable */
+               dbus_message_iter_recurse(iter, &variant);
+               ctype = dbus_message_iter_get_arg_type(&variant);
+               string = &variant;
+       } else {
+               string = iter;
+       }
+
+       if (ctype != DBUS_TYPE_STRING) {
+               g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+                               "Value data spec should be variable or string");
+               return FALSE;
+       }
+
+       dbus_message_iter_get_basic(string, &desc);
+       app->description = g_strdup(desc);
+       return TRUE;
+}
+
+static gboolean parse_chan_type(DBusMessageIter *iter, gpointer data,
+                                                               GError **err)
+{
+       struct hdp_application *app = data;
+       DBusMessageIter *value;
+       char *chan_type;
+       int ctype;
+
+       ctype = dbus_message_iter_get_arg_type(iter);
+       value = iter;
+       if (ctype == DBUS_TYPE_VARIANT) {
+               DBusMessageIter variant;
+
+               /* Get value inside the variable */
+               dbus_message_iter_recurse(iter, &variant);
+               ctype = dbus_message_iter_get_arg_type(&variant);
+               value = &variant;
+       }
+
+       if (ctype != DBUS_TYPE_STRING) {
+               g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+                       "Final value for channel type should be an string");
+               return FALSE;
+       }
+
+       dbus_message_iter_get_basic(value, &chan_type);
+
+       if (g_ascii_strcasecmp("Reliable", chan_type) == 0)
+               app->chan_type = HDP_RELIABLE_DC;
+       else if (g_ascii_strcasecmp("Streaming", chan_type) == 0)
+               app->chan_type = HDP_STREAMING_DC;
+       else {
+               g_set_error(err, HDP_ERROR, HDP_DIC_ENTRY_PARSE_ERROR,
+                                               "Invalid value for data type");
+               return FALSE;
+       }
+
+       app->chan_type_set = TRUE;
+
+       return TRUE;
+}
+
+static struct dict_entry_func dict_parser[] = {
+       {"DataType",            parse_data_type},
+       {"Role",                parse_role},
+       {"Description",         parse_desc},
+       {"ChannelType",         parse_chan_type},
+       {NULL, NULL}
+};
+
+struct hdp_application *hdp_get_app_config(DBusMessageIter *iter, GError **err)
+{
+       struct hdp_application *app;
+
+       app = g_new0(struct hdp_application, 1);
+       app->ref = 1;
+       if (!parse_dict(dict_parser, iter, err, app))
+               goto fail;
+       if (!app->data_type_set || !app->role_set) {
+               g_set_error(err, HDP_ERROR, HDP_DIC_PARSE_ERROR,
+                                               "Mandatory fields aren't set");
+               goto fail;
+       }
+       return app;
+
+fail:
+       hdp_application_unref(app);
+       return NULL;
+}
+
+static gboolean is_app_role(GSList *app_list, HdpRole role)
+{
+       GSList *l;
+
+       for (l = app_list; l; l = l->next) {
+               struct hdp_application *app = l->data;
+
+               if (app->role == role)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean set_sdp_services_uuid(sdp_record_t *record, HdpRole role)
+{
+       uuid_t svc_uuid_source, svc_uuid_sink;
+       sdp_list_t *svc_list = NULL;
+
+       sdp_uuid16_create(&svc_uuid_sink, HDP_SINK_SVCLASS_ID);
+       sdp_uuid16_create(&svc_uuid_source, HDP_SOURCE_SVCLASS_ID);
+
+       sdp_get_service_classes(record, &svc_list);
+
+       if (role == HDP_SOURCE) {
+               if (!sdp_list_find(svc_list, &svc_uuid_source, sdp_uuid_cmp))
+                       svc_list = sdp_list_append(svc_list, &svc_uuid_source);
+       } else if (role == HDP_SINK) {
+               if (!sdp_list_find(svc_list, &svc_uuid_sink, sdp_uuid_cmp))
+                       svc_list = sdp_list_append(svc_list, &svc_uuid_sink);
+       }
+
+       if (sdp_set_service_classes(record, svc_list) < 0) {
+               sdp_list_free(svc_list, NULL);
+               return FALSE;
+       }
+
+       sdp_list_free(svc_list, NULL);
+
+       return TRUE;
+}
+
+static gboolean register_service_protocols(struct hdp_adapter *adapter,
+                                               sdp_record_t *sdp_record)
+{
+       gboolean ret;
+       uuid_t l2cap_uuid, mcap_c_uuid;
+       sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+       sdp_list_t *access_proto_list = NULL;
+       sdp_data_t *psm = NULL, *mcap_ver = NULL;
+       uint16_t version = MCAP_VERSION;
+
+       /* set l2cap information */
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+       if (l2cap_list == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       psm = sdp_data_alloc(SDP_UINT16, &adapter->ccpsm);
+       if (psm == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       if (sdp_list_append(l2cap_list, psm) == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       proto_list = sdp_list_append(NULL, l2cap_list);
+       if (proto_list == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       /* set mcap information */
+       sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
+       mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
+       if (mcap_list == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
+       if (mcap_ver == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       if (sdp_list_append(mcap_list, mcap_ver) == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       if (sdp_list_append(proto_list, mcap_list) == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       /* attach protocol information to service record */
+       access_proto_list = sdp_list_append(NULL, proto_list);
+       if (access_proto_list == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       if (sdp_set_access_protos(sdp_record, access_proto_list) < 0) {
+               ret = FALSE;
+               goto end;
+       }
+       ret = TRUE;
+
+end:
+       if (l2cap_list != NULL)
+               sdp_list_free(l2cap_list, NULL);
+       if (mcap_list != NULL)
+               sdp_list_free(mcap_list, NULL);
+       if (proto_list != NULL)
+               sdp_list_free(proto_list, NULL);
+       if (access_proto_list != NULL)
+               sdp_list_free(access_proto_list, NULL);
+       if (psm != NULL)
+               sdp_data_free(psm);
+       if (mcap_ver != NULL)
+               sdp_data_free(mcap_ver);
+
+       return ret;
+}
+
+static gboolean register_service_profiles(sdp_record_t *sdp_record)
+{
+       gboolean ret;
+       sdp_list_t *profile_list;
+       sdp_profile_desc_t hdp_profile;
+
+       /* set hdp information */
+       sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID);
+       hdp_profile.version = HDP_VERSION;
+       profile_list = sdp_list_append(NULL, &hdp_profile);
+       if (profile_list == NULL)
+               return FALSE;
+
+       /* set profile descriptor list */
+       if (sdp_set_profile_descs(sdp_record, profile_list) < 0)
+               ret = FALSE;
+       else
+               ret = TRUE;
+
+       sdp_list_free(profile_list, NULL);
+
+       return ret;
+}
+
+static gboolean register_service_additional_protocols(
+                                               struct hdp_adapter *adapter,
+                                               sdp_record_t *sdp_record)
+{
+       gboolean ret;
+       uuid_t l2cap_uuid, mcap_d_uuid;
+       sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
+       sdp_list_t *access_proto_list = NULL;
+       sdp_data_t *psm = NULL;
+
+       /* set l2cap information */
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
+       if (l2cap_list == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       psm = sdp_data_alloc(SDP_UINT16, &adapter->dcpsm);
+       if (psm == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       if (sdp_list_append(l2cap_list, psm) == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       proto_list = sdp_list_append(NULL, l2cap_list);
+       if (proto_list == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       /* set mcap information */
+       sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID);
+       mcap_list = sdp_list_append(NULL, &mcap_d_uuid);
+       if (mcap_list == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       if (sdp_list_append(proto_list, mcap_list) == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       /* attach protocol information to service record */
+       access_proto_list = sdp_list_append(NULL, proto_list);
+       if (access_proto_list == NULL) {
+               ret = FALSE;
+               goto end;
+       }
+
+       if (sdp_set_add_access_protos(sdp_record, access_proto_list) < 0)
+               ret = FALSE;
+       else
+               ret = TRUE;
+
+end:
+       if (l2cap_list != NULL)
+               sdp_list_free(l2cap_list, NULL);
+       if (mcap_list != NULL)
+               sdp_list_free(mcap_list, NULL);
+       if (proto_list  != NULL)
+               sdp_list_free(proto_list, NULL);
+       if (access_proto_list != NULL)
+               sdp_list_free(access_proto_list, NULL);
+       if (psm != NULL)
+               sdp_data_free(psm);
+
+       return ret;
+}
+
+static sdp_list_t *app_to_sdplist(struct hdp_application *app)
+{
+       sdp_data_t *mdepid,
+               *dtype = NULL,
+               *role = NULL,
+               *desc = NULL;
+       sdp_list_t *f_list = NULL;
+
+       mdepid = sdp_data_alloc(SDP_UINT8, &app->id);
+       if (mdepid == NULL)
+               return NULL;
+
+       dtype = sdp_data_alloc(SDP_UINT16, &app->data_type);
+       if (dtype == NULL)
+               goto fail;
+
+       role = sdp_data_alloc(SDP_UINT8, &app->role);
+       if (role == NULL)
+               goto fail;
+
+       if (app->description != NULL) {
+               desc = sdp_data_alloc(SDP_TEXT_STR8, app->description);
+               if (desc == NULL)
+                       goto fail;
+       }
+
+       f_list = sdp_list_append(NULL, mdepid);
+       if (f_list == NULL)
+               goto fail;
+
+       if (sdp_list_append(f_list, dtype) == NULL)
+               goto fail;
+
+       if (sdp_list_append(f_list, role) == NULL)
+               goto fail;
+
+       if (desc != NULL)
+               if (sdp_list_append(f_list, desc) == NULL)
+                       goto fail;
+
+       return f_list;
+
+fail:
+       if (f_list != NULL)
+               sdp_list_free(f_list, NULL);
+       if (mdepid != NULL)
+               sdp_data_free(mdepid);
+       if (dtype != NULL)
+               sdp_data_free(dtype);
+       if (role != NULL)
+               sdp_data_free(role);
+       if (desc != NULL)
+               sdp_data_free(desc);
+
+       return NULL;
+}
+
+static gboolean register_features(struct hdp_application *app,
+                                               sdp_list_t **sup_features)
+{
+       sdp_list_t *hdp_feature;
+
+       hdp_feature = app_to_sdplist(app);
+       if (hdp_feature == NULL)
+               goto fail;
+
+       if (*sup_features == NULL) {
+               *sup_features = sdp_list_append(NULL, hdp_feature);
+               if (*sup_features == NULL)
+                       goto fail;
+       } else if (sdp_list_append(*sup_features, hdp_feature) == NULL) {
+               goto fail;
+       }
+
+       return TRUE;
+
+fail:
+       if (hdp_feature != NULL)
+               sdp_list_free(hdp_feature, (sdp_free_func_t)sdp_data_free);
+       return FALSE;
+}
+
+static void free_hdp_list(void *list)
+{
+       sdp_list_t *hdp_list = list;
+
+       sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free);
+}
+
+static gboolean register_service_sup_features(GSList *app_list,
+                                               sdp_record_t *sdp_record)
+{
+       GSList *l;
+       sdp_list_t *sup_features = NULL;
+
+       for (l = app_list; l; l = l->next) {
+               if (!register_features(l->data, &sup_features))
+                       return FALSE;
+       }
+
+       if (sdp_set_supp_feat(sdp_record, sup_features) < 0) {
+               sdp_list_free(sup_features, free_hdp_list);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean register_data_exchange_spec(sdp_record_t *record)
+{
+       sdp_data_t *spec;
+       uint8_t data_spec = DATA_EXCHANGE_SPEC_11073;
+       /* As by now 11073 is the only supported we set it by default */
+
+       spec = sdp_data_alloc(SDP_UINT8, &data_spec);
+       if (spec == NULL)
+               return FALSE;
+
+       if (sdp_attr_add(record, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) {
+               sdp_data_free(spec);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean register_mcap_features(sdp_record_t *sdp_record)
+{
+       sdp_data_t *mcap_proc;
+       uint8_t mcap_sup_proc = MCAP_SUP_PROC;
+
+       mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc);
+       if (mcap_proc == NULL)
+               return FALSE;
+
+       if (sdp_attr_add(sdp_record, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES,
+                                                       mcap_proc) < 0) {
+               sdp_data_free(mcap_proc);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean hdp_update_sdp_record(struct hdp_adapter *adapter, GSList *app_list)
+{
+       sdp_record_t *sdp_record;
+       bdaddr_t addr;
+
+       if (adapter->sdp_handler > 0)
+               remove_record_from_server(adapter->sdp_handler);
+
+       if (app_list == NULL) {
+               adapter->sdp_handler = 0;
+               return TRUE;
+       }
+
+       sdp_record = sdp_record_alloc();
+       if (sdp_record == NULL)
+               return FALSE;
+
+       if (adapter->sdp_handler > 0)
+               sdp_record->handle = adapter->sdp_handler;
+       else
+               sdp_record->handle = 0xffffffff; /* Set automatically */
+
+       if (is_app_role(app_list, HDP_SINK))
+               set_sdp_services_uuid(sdp_record, HDP_SINK);
+       if (is_app_role(app_list, HDP_SOURCE))
+               set_sdp_services_uuid(sdp_record, HDP_SOURCE);
+
+       if (!register_service_protocols(adapter, sdp_record))
+               goto fail;
+       if (!register_service_profiles(sdp_record))
+               goto fail;
+       if (!register_service_additional_protocols(adapter, sdp_record))
+               goto fail;
+
+       sdp_set_info_attr(sdp_record, HDP_SERVICE_NAME, HDP_SERVICE_PROVIDER,
+                                                       HDP_SERVICE_DSC);
+       if (!register_service_sup_features(app_list, sdp_record))
+               goto fail;
+       if (!register_data_exchange_spec(sdp_record))
+               goto fail;
+
+       register_mcap_features(sdp_record);
+
+       if (sdp_set_record_state(sdp_record, adapter->record_state++) < 0)
+               goto fail;
+
+       adapter_get_address(adapter->btd_adapter, &addr);
+
+       if (add_record_to_server(&addr, sdp_record) < 0)
+               goto fail;
+       adapter->sdp_handler = sdp_record->handle;
+       return TRUE;
+
+fail:
+       if (sdp_record != NULL)
+               sdp_record_free(sdp_record);
+       return FALSE;
+}
+
+static gboolean check_role(uint8_t rec_role, uint8_t app_role)
+{
+       if ((rec_role == HDP_SINK && app_role == HDP_SOURCE) ||
+                       (rec_role == HDP_SOURCE && app_role == HDP_SINK))
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean get_mdep_from_rec(const sdp_record_t *rec, uint8_t role,
+                               uint16_t d_type, uint8_t *mdep, char **desc)
+{
+       sdp_data_t *list, *feat;
+
+       if (desc == NULL && mdep == NULL)
+               return TRUE;
+
+       list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
+       if (list == NULL || (list->dtd != SDP_SEQ8 && list->dtd != SDP_SEQ16 &&
+                                                       list->dtd != SDP_SEQ32))
+               return FALSE;
+
+       for (feat = list->val.dataseq; feat; feat = feat->next) {
+               sdp_data_t *data_type, *mdepid, *role_t, *desc_t;
+
+               if (feat->dtd != SDP_SEQ8 && feat->dtd != SDP_SEQ16 &&
+                                               feat->dtd != SDP_SEQ32)
+                       continue;
+
+               mdepid = feat->val.dataseq;
+               if (mdepid == NULL)
+                       continue;
+
+               data_type = mdepid->next;
+               if (data_type == NULL)
+                       continue;
+
+               role_t = data_type->next;
+               if (role_t == NULL)
+                       continue;
+
+               desc_t = role_t->next;
+
+               if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 ||
+                                               role_t->dtd != SDP_UINT8)
+                       continue;
+
+               if (data_type->val.uint16 != d_type ||
+                                       !check_role(role_t->val.uint8, role))
+                       continue;
+
+               if (mdep != NULL)
+                       *mdep = mdepid->val.uint8;
+
+               if (desc != NULL  && desc_t != NULL  &&
+                                       (desc_t->dtd == SDP_TEXT_STR8 ||
+                                       desc_t->dtd == SDP_TEXT_STR16  ||
+                                       desc_t->dtd == SDP_TEXT_STR32))
+                       *desc = g_strdup(desc_t->val.str);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void get_mdep_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+       struct get_mdep_data *mdep_data = user_data;
+       GError *gerr = NULL;
+       uint8_t mdep;
+
+       if (err < 0 || recs == NULL) {
+               g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+                                       "Error getting remote SDP records");
+               mdep_data->func(0, mdep_data->data, gerr);
+               g_error_free(gerr);
+               return;
+       }
+
+       if (!get_mdep_from_rec(recs->data, mdep_data->app->role,
+                               mdep_data->app->data_type, &mdep, NULL)) {
+               g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+                                       "No matching MDEP found");
+               mdep_data->func(0, mdep_data->data, gerr);
+               g_error_free(gerr);
+               return;
+       }
+
+       mdep_data->func(mdep, mdep_data->data, NULL);
+}
+
+static void free_mdep_data(gpointer data)
+{
+       struct get_mdep_data *mdep_data = data;
+
+       if (mdep_data->destroy)
+               mdep_data->destroy(mdep_data->data);
+       hdp_application_unref(mdep_data->app);
+
+       g_free(mdep_data);
+}
+
+gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app,
+                               hdp_continue_mdep_f func, gpointer data,
+                               GDestroyNotify destroy, GError **err)
+{
+       struct get_mdep_data *mdep_data;
+       bdaddr_t dst, src;
+       uuid_t uuid;
+
+       device_get_address(device->dev, &dst, NULL);
+       adapter_get_address(device_get_adapter(device->dev), &src);
+
+       mdep_data = g_new0(struct get_mdep_data, 1);
+       mdep_data->app = hdp_application_ref(app);
+       mdep_data->func = func;
+       mdep_data->data = data;
+       mdep_data->destroy = destroy;
+
+       bt_string2uuid(&uuid, HDP_UUID);
+       if (bt_search_service(&src, &dst, &uuid, get_mdep_cb, mdep_data,
+                                                       free_mdep_data) < 0) {
+               g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
+                                               "Can't get remote SDP record");
+               g_free(mdep_data);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val)
+{
+       sdp_data_t *iter;
+       int proto;
+
+       if (entry == NULL || (entry->dtd != SDP_SEQ8 &&
+                       entry->dtd != SDP_SEQ16 && entry->dtd != SDP_SEQ32))
+               return FALSE;
+
+       iter = entry->val.dataseq;
+       if (!(iter->dtd & SDP_UUID_UNSPEC))
+               return FALSE;
+
+       proto = sdp_uuid_to_proto(&iter->val.uuid);
+       if (proto != type)
+               return FALSE;
+
+       if (val == NULL)
+               return TRUE;
+
+       iter = iter->next;
+       if (iter->dtd != SDP_UINT16)
+               return FALSE;
+
+       *val = iter->val.uint16;
+
+       return TRUE;
+}
+
+static gboolean hdp_get_prot_desc_list(const sdp_record_t *rec, guint16 *psm,
+                                                       guint16 *version)
+{
+       sdp_data_t *pdl, *p0, *p1;
+
+       if (psm == NULL && version == NULL)
+               return TRUE;
+
+       pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+       if (pdl == NULL || (pdl->dtd != SDP_SEQ8 && pdl->dtd != SDP_SEQ16 &&
+                                                       pdl->dtd != SDP_SEQ32))
+               return FALSE;
+
+       p0 = pdl->val.dataseq;
+       if (!get_prot_desc_entry(p0, L2CAP_UUID, psm))
+               return FALSE;
+
+       p1 = p0->next;
+       if (!get_prot_desc_entry(p1, MCAP_CTRL_UUID, version))
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean hdp_get_add_prot_desc_list(const sdp_record_t *rec,
+                                                               guint16 *psm)
+{
+       sdp_data_t *pdl, *p0, *p1;
+
+       if (psm == NULL)
+               return TRUE;
+
+       pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
+       if (pdl == NULL || pdl->dtd != SDP_SEQ8)
+               return FALSE;
+       pdl = pdl->val.dataseq;
+       if (pdl->dtd != SDP_SEQ8)
+               return FALSE;
+
+       p0 = pdl->val.dataseq;
+
+       if (!get_prot_desc_entry(p0, L2CAP_UUID, psm))
+               return FALSE;
+       p1 = p0->next;
+       if (!get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL))
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm)
+{
+       sdp_list_t *l;
+
+       for (l = recs; l; l = l->next) {
+               sdp_record_t *rec = l->data;
+
+               if (hdp_get_prot_desc_list(rec, ccpsm, NULL))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm)
+{
+       sdp_list_t *l;
+
+       for (l = recs; l; l = l->next) {
+               sdp_record_t *rec = l->data;
+
+               if (hdp_get_add_prot_desc_list(rec, dcpsm))
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void con_mcl_data_unref(struct conn_mcl_data *conn_data)
+{
+       if (conn_data == NULL)
+               return;
+
+       if (--conn_data->refs > 0)
+               return;
+
+       if (conn_data->destroy)
+               conn_data->destroy(conn_data->data);
+
+       health_device_unref(conn_data->dev);
+       g_free(conn_data);
+}
+
+static void destroy_con_mcl_data(gpointer data)
+{
+       con_mcl_data_unref(data);
+}
+
+static struct conn_mcl_data *con_mcl_data_ref(struct conn_mcl_data *conn_data)
+{
+       if (conn_data == NULL)
+               return NULL;
+
+       conn_data->refs++;
+       return conn_data;
+}
+
+static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data)
+{
+       struct conn_mcl_data *conn_data = data;
+       struct hdp_device *device = conn_data->dev;
+       GError *gerr = NULL;
+
+       if (err != NULL) {
+               conn_data->func(conn_data->data, err);
+               return;
+       }
+
+       if (device->mcl == NULL)
+               device->mcl = mcap_mcl_ref(mcl);
+       device->mcl_conn = TRUE;
+
+       hdp_set_mcl_cb(device, &gerr);
+
+       conn_data->func(conn_data->data, gerr);
+       if (gerr != NULL)
+               g_error_free(gerr);
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+       struct conn_mcl_data *conn_data = user_data;
+       GError *gerr = NULL;
+       bdaddr_t dst;
+       uint16_t ccpsm;
+
+       if (conn_data->dev->hdp_adapter->mi == NULL) {
+               g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+                                               "Mcap instance released");
+               goto fail;
+       }
+
+       if (err < 0 || recs == NULL) {
+               g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+                                       "Error getting remote SDP records");
+               goto fail;
+       }
+
+       if (!get_ccpsm(recs, &ccpsm)) {
+               g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+                               "Can't get remote PSM for control channel");
+               goto fail;
+       }
+
+       conn_data = con_mcl_data_ref(conn_data);
+
+       device_get_address(conn_data->dev->dev, &dst, NULL);
+       if (!mcap_create_mcl(conn_data->dev->hdp_adapter->mi, &dst, ccpsm,
+                                               create_mcl_cb, conn_data,
+                                               destroy_con_mcl_data, &gerr)) {
+               con_mcl_data_unref(conn_data);
+               goto fail;
+       }
+       return;
+fail:
+       conn_data->func(conn_data->data, gerr);
+       g_error_free(gerr);
+}
+
+gboolean hdp_establish_mcl(struct hdp_device *device,
+                                               hdp_continue_proc_f func,
+                                               gpointer data,
+                                               GDestroyNotify destroy,
+                                               GError **err)
+{
+       struct conn_mcl_data *conn_data;
+       bdaddr_t dst, src;
+       uuid_t uuid;
+
+       device_get_address(device->dev, &dst, NULL);
+       adapter_get_address(device_get_adapter(device->dev), &src);
+
+       conn_data = g_new0(struct conn_mcl_data, 1);
+       conn_data->refs = 1;
+       conn_data->func = func;
+       conn_data->data = data;
+       conn_data->destroy = destroy;
+       conn_data->dev = health_device_ref(device);
+
+       bt_string2uuid(&uuid, HDP_UUID);
+       if (bt_search_service(&src, &dst, &uuid, search_cb, conn_data,
+                                               destroy_con_mcl_data) < 0) {
+               g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
+                                               "Can't get remote SDP record");
+               g_free(conn_data);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void get_dcpsm_cb(sdp_list_t *recs, int err, gpointer data)
+{
+       struct get_dcpsm_data *dcpsm_data = data;
+       GError *gerr = NULL;
+       uint16_t dcpsm;
+
+       if (err < 0 || recs == NULL) {
+               g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+                                       "Error getting remote SDP records");
+               goto fail;
+       }
+
+       if (!get_dcpsm(recs, &dcpsm)) {
+               g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
+                               "Can't get remote PSM for data channel");
+               goto fail;
+       }
+
+       dcpsm_data->func(dcpsm, dcpsm_data->data, NULL);
+       return;
+
+fail:
+       dcpsm_data->func(0, dcpsm_data->data, gerr);
+       g_error_free(gerr);
+}
+
+static void free_dcpsm_data(gpointer data)
+{
+       struct get_dcpsm_data *dcpsm_data = data;
+
+       if (dcpsm_data == NULL)
+               return;
+
+       if (dcpsm_data->destroy)
+               dcpsm_data->destroy(dcpsm_data->data);
+
+       g_free(dcpsm_data);
+}
+
+gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func,
+                                                       gpointer data,
+                                                       GDestroyNotify destroy,
+                                                       GError **err)
+{
+       struct get_dcpsm_data *dcpsm_data;
+       bdaddr_t dst, src;
+       uuid_t uuid;
+
+       device_get_address(device->dev, &dst, NULL);
+       adapter_get_address(device_get_adapter(device->dev), &src);
+
+       dcpsm_data = g_new0(struct get_dcpsm_data, 1);
+       dcpsm_data->func = func;
+       dcpsm_data->data = data;
+       dcpsm_data->destroy = destroy;
+
+       bt_string2uuid(&uuid, HDP_UUID);
+       if (bt_search_service(&src, &dst, &uuid, get_dcpsm_cb, dcpsm_data,
+                                                       free_dcpsm_data) < 0) {
+               g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
+                                               "Can't get remote SDP record");
+               g_free(dcpsm_data);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void hdp_free_application(struct hdp_application *app)
+{
+       if (app->dbus_watcher > 0)
+               g_dbus_remove_watch(app->conn, app->dbus_watcher);
+
+       if (app->conn != NULL)
+               dbus_connection_unref(app->conn);
+       g_free(app->oname);
+       g_free(app->description);
+       g_free(app->path);
+       g_free(app);
+}
+
+struct hdp_application *hdp_application_ref(struct hdp_application *app)
+{
+       if (app == NULL)
+               return NULL;
+
+       app->ref++;
+
+       DBG("health_application_ref(%p): ref=%d", app, app->ref);
+       return app;
+}
+
+void hdp_application_unref(struct hdp_application *app)
+{
+       if (app == NULL)
+               return;
+
+       app->ref--;
+
+       DBG("health_application_unref(%p): ref=%d", app, app->ref);
+       if (app->ref > 0)
+               return;
+
+       hdp_free_application(app);
+}
diff --git a/health/hdp_util.h b/health/hdp_util.h
new file mode 100644 (file)
index 0000000..35e1196
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __HDP_UTIL_H__
+#define __HDP_UTIL_H__
+
+typedef void (*hdp_continue_mdep_f)(uint8_t mdep, gpointer user_data,
+                                                               GError *err);
+typedef void (*hdp_continue_dcpsm_f)(uint16_t dcpsm, gpointer user_data,
+                                                               GError *err);
+typedef void (*hdp_continue_proc_f)(gpointer user_data, GError *err);
+
+struct hdp_application *hdp_get_app_config(DBusMessageIter *iter, GError **err);
+gboolean hdp_update_sdp_record(struct hdp_adapter *adapter, GSList *app_list);
+gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app,
+                               hdp_continue_mdep_f func,
+                               gpointer data, GDestroyNotify destroy,
+                               GError **err);
+
+gboolean hdp_establish_mcl(struct hdp_device *device,
+                                               hdp_continue_proc_f func,
+                                               gpointer data,
+                                               GDestroyNotify destroy,
+                                               GError **err);
+
+gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func,
+                                                       gpointer data,
+                                                       GDestroyNotify destroy,
+                                                       GError **err);
+
+
+struct hdp_application *hdp_application_ref(struct hdp_application *app);
+void hdp_application_unref(struct hdp_application *app);
+
+struct hdp_device *health_device_ref(struct hdp_device *hdp_dev);
+void health_device_unref(struct hdp_device *hdp_dev);
+
+
+#endif /* __HDP_UTIL_H__ */
diff --git a/health/mcap.c b/health/mcap.c
new file mode 100644 (file)
index 0000000..b76d88a
--- /dev/null
@@ -0,0 +1,2194 @@
+/*
+ *
+ *  MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+
+#include <btio.h>
+#include <log.h>
+#include <error.h>
+
+#include "mcap.h"
+#include "mcap_lib.h"
+#include "mcap_internal.h"
+
+#define RESPONSE_TIMER 6       /* seconds */
+#define MAX_CACHED     10      /* 10 devices */
+
+#define MCAP_ERROR g_quark_from_static_string("mcap-error-quark")
+
+#define RELEASE_TIMER(__mcl) do {              \
+       if (__mcl->tid) {                       \
+               g_source_remove(__mcl->tid);    \
+               __mcl->tid = 0;                 \
+       }                                       \
+} while(0)
+
+struct connect_mcl {
+       struct mcap_mcl         *mcl;           /* MCL for this operation */
+       mcap_mcl_connect_cb     connect_cb;     /* Connect callback */
+       GDestroyNotify          destroy;        /* Destroy callback */
+       gpointer                user_data;      /* Callback user data */
+};
+
+typedef union {
+       mcap_mdl_operation_cb           op;
+       mcap_mdl_operation_conf_cb      op_conf;
+       mcap_mdl_notify_cb              notify;
+} mcap_cb_type;
+
+struct mcap_mdl_op_cb {
+       struct mcap_mdl         *mdl;           /* MDL for this operation */
+       mcap_cb_type            cb;             /* Operation callback */
+       GDestroyNotify          destroy;        /* Destroy callback */
+       gpointer                user_data;      /* Callback user data */
+};
+
+/* MCAP finite state machine functions */
+static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l);
+static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l);
+static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l);
+
+static void (*proc_req[])(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) = {
+       proc_req_connected,
+       proc_req_pending,
+       proc_req_active
+};
+
+static void mcap_cache_mcl(struct mcap_mcl *mcl);
+
+static void default_mdl_connected_cb(struct mcap_mdl *mdl, gpointer data)
+{
+       DBG("MCAP Unmanaged mdl connection");
+}
+
+static void default_mdl_closed_cb(struct mcap_mdl *mdl, gpointer data)
+{
+       DBG("MCAP Unmanaged mdl closed");
+}
+
+static void default_mdl_deleted_cb(struct mcap_mdl *mdl, gpointer data)
+{
+       DBG("MCAP Unmanaged mdl deleted");
+}
+
+static void default_mdl_aborted_cb(struct mcap_mdl *mdl, gpointer data)
+{
+       DBG("MCAP Unmanaged mdl aborted");
+}
+
+static uint8_t default_mdl_conn_req_cb(struct mcap_mcl *mcl,
+                                               uint8_t mdepid, uint16_t mdlid,
+                                               uint8_t *conf, gpointer data)
+{
+       DBG("MCAP mdl remote connection aborted");
+       /* Due to this callback isn't managed this request won't be supported */
+       return MCAP_REQUEST_NOT_SUPPORTED;
+}
+
+static uint8_t default_mdl_reconn_req_cb(struct mcap_mdl *mdl,
+                                               gpointer data)
+{
+       DBG("MCAP mdl remote reconnection aborted");
+       /* Due to this callback isn't managed this request won't be supported */
+       return MCAP_REQUEST_NOT_SUPPORTED;
+}
+
+static void set_default_cb(struct mcap_mcl *mcl)
+{
+       if (!mcl->cb)
+               mcl->cb = g_new0(struct mcap_mdl_cb, 1);
+
+       mcl->cb->mdl_connected = default_mdl_connected_cb;
+       mcl->cb->mdl_closed = default_mdl_closed_cb;
+       mcl->cb->mdl_deleted = default_mdl_deleted_cb;
+       mcl->cb->mdl_aborted = default_mdl_aborted_cb;
+       mcl->cb->mdl_conn_req = default_mdl_conn_req_cb;
+       mcl->cb->mdl_reconn_req = default_mdl_reconn_req_cb;
+}
+
+static char *error2str(uint8_t rc)
+{
+       switch (rc) {
+       case MCAP_SUCCESS:
+               return "Success";
+       case MCAP_INVALID_OP_CODE:
+               return "Invalid Op Code";
+       case MCAP_INVALID_PARAM_VALUE:
+               return "Invalid Parameter Value";
+       case MCAP_INVALID_MDEP:
+               return "Invalid MDEP";
+       case MCAP_MDEP_BUSY:
+               return "MDEP Busy";
+       case MCAP_INVALID_MDL:
+               return "Invalid MDL";
+       case MCAP_MDL_BUSY:
+               return "MDL Busy";
+       case MCAP_INVALID_OPERATION:
+               return "Invalid Operation";
+       case MCAP_RESOURCE_UNAVAILABLE:
+               return "Resource Unavailable";
+       case MCAP_UNSPECIFIED_ERROR:
+               return "Unspecified Error";
+       case MCAP_REQUEST_NOT_SUPPORTED:
+               return "Request Not Supported";
+       case MCAP_CONFIGURATION_REJECTED:
+               return "Configuration Rejected";
+       default:
+               return "Unknown Response Code";
+       }
+}
+
+static gboolean mcap_send_std_opcode(struct mcap_mcl *mcl, void *cmd,
+                                               uint32_t size, GError **err)
+{
+       if (mcl->state == MCL_IDLE) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+                                                       "MCL is not connected");
+               return FALSE;
+       }
+
+       if (mcl->req != MCL_AVAILABLE) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE,
+                                                       "Pending request");
+               return FALSE;
+       }
+
+       if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_REQUEST_NOT_SUPPORTED,
+                               "Remote does not support standard opcodes");
+               return FALSE;
+       }
+
+       if (mcl->state == MCL_PENDING) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_OPERATION,
+                       "Not Std Op. Codes can be sent in PENDING State");
+               return FALSE;
+       }
+
+       if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), cmd, size) < 0) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+                                       "Command can't be sent, write error");
+               return FALSE;
+       }
+
+       mcl->lcmd = cmd;
+       mcl->req = MCL_WAITING_RSP;
+
+       return TRUE;
+}
+
+static void update_mcl_state(struct mcap_mcl *mcl)
+{
+       GSList *l;
+       struct mcap_mdl *mdl;
+
+       if (mcl->state == MCL_PENDING)
+               return;
+
+       for (l = mcl->mdls; l; l = l->next) {
+               mdl = l->data;
+
+               if (mdl->state == MDL_CONNECTED) {
+                       mcl->state = MCL_ACTIVE;
+                       return;
+               }
+       }
+
+       mcl->state = MCL_CONNECTED;
+}
+
+static void shutdown_mdl(struct mcap_mdl *mdl)
+{
+       mdl->state = MDL_CLOSED;
+
+       if (mdl->wid) {
+               g_source_remove(mdl->wid);
+               mdl->wid = 0;
+       }
+
+       if (mdl->dc) {
+               g_io_channel_shutdown(mdl->dc, TRUE, NULL);
+               g_io_channel_unref(mdl->dc);
+               mdl->dc = NULL;
+       }
+}
+
+static void free_mdl(struct mcap_mdl *mdl)
+{
+       if (!mdl)
+               return;
+
+       mcap_mcl_unref(mdl->mcl);
+       g_free(mdl);
+}
+
+static gint cmp_mdl_state(gconstpointer a, gconstpointer b)
+{
+       const struct mcap_mdl *mdl = a;
+       const MDLState *st = b;
+
+       if (mdl->state == *st)
+               return 0;
+       else if (mdl->state < *st)
+               return -1;
+       else
+               return 1;
+}
+
+static void free_mcap_mdl_op(struct mcap_mdl_op_cb *op)
+{
+       if (op->destroy)
+               op->destroy(op->user_data);
+
+       if (op->mdl)
+               mcap_mdl_unref(op->mdl);
+
+       g_free(op);
+}
+
+static void free_mcl_priv_data(struct mcap_mcl *mcl)
+{
+       free_mcap_mdl_op(mcl->priv_data);
+       mcl->priv_data = NULL;
+}
+
+static void mcap_notify_error(struct mcap_mcl *mcl, GError *err)
+{
+       struct mcap_mdl_op_cb *con = mcl->priv_data;
+       struct mcap_mdl *mdl;
+       MDLState st;
+       GSList *l;
+
+       if (!con || !mcl->lcmd)
+               return;
+
+       switch (mcl->lcmd[0]) {
+       case MCAP_MD_CREATE_MDL_REQ:
+               st = MDL_WAITING;
+               l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state);
+               mdl = l->data;
+               mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+               mcap_mdl_unref(mdl);
+               update_mcl_state(mcl);
+               con->cb.op_conf(NULL, 0, err, con->user_data);
+               break;
+       case MCAP_MD_ABORT_MDL_REQ:
+               st = MDL_WAITING;
+               l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state);
+               shutdown_mdl(l->data);
+               update_mcl_state(mcl);
+               con->cb.notify(err, con->user_data);
+               break;
+       case MCAP_MD_DELETE_MDL_REQ:
+               for (l = mcl->mdls; l; l = l->next) {
+                       mdl = l->data;
+                       if (mdl->state == MDL_DELETING)
+                               mdl->state = (mdl->dc) ? MDL_CONNECTED :
+                                                               MDL_CLOSED;
+               }
+               update_mcl_state(mcl);
+               con->cb.notify(err, con->user_data);
+               break;
+       case MCAP_MD_RECONNECT_MDL_REQ:
+               st = MDL_WAITING;
+               l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state);
+               shutdown_mdl(l->data);
+               update_mcl_state(mcl);
+               con->cb.op(NULL, err, con->user_data);
+               break;
+       }
+
+       free_mcl_priv_data(mcl);
+       g_free(mcl->lcmd);
+       mcl->lcmd = NULL;
+}
+
+int mcap_send_data(int sock, const void *buf, uint32_t size)
+{
+       const uint8_t *buf_b = buf;
+       uint32_t sent = 0;
+
+       while (sent < size) {
+               int n = write(sock, buf_b + sent, size - sent);
+               if (n < 0)
+                       return -1;
+               sent += n;
+       }
+
+       return 0;
+}
+
+static int mcap_send_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc,
+                                       uint16_t mdl, uint8_t *data, size_t len)
+{
+       mcap_rsp *cmd;
+       int sock, sent;
+
+       if (mcl->cc == NULL)
+               return -1;
+
+       sock = g_io_channel_unix_get_fd(mcl->cc);
+
+       cmd = g_malloc(sizeof(mcap_rsp) + len);
+       cmd->op = oc;
+       cmd->rc = rc;
+       cmd->mdl = htons(mdl);
+
+       if (data && len > 0)
+               memcpy(cmd->data, data, len);
+
+       sent = mcap_send_data(sock, cmd, sizeof(mcap_rsp) + len);
+       g_free(cmd);
+
+       return sent;
+}
+
+static struct mcap_mdl *get_mdl(struct mcap_mcl *mcl, uint16_t mdlid)
+{
+       GSList *l;
+       struct mcap_mdl *mdl;
+
+       for (l = mcl->mdls; l; l = l->next) {
+               mdl = l->data;
+               if (mdlid == mdl->mdlid)
+                       return mdl;
+       }
+
+       return NULL;
+}
+
+static uint16_t generate_mdlid(struct mcap_mcl *mcl)
+{
+       uint16_t mdlid = mcl->next_mdl;
+       struct mcap_mdl *mdl;
+
+       do {
+               mdl = get_mdl(mcl, mdlid);
+               if (!mdl) {
+                       mcl->next_mdl = (mdlid % MCAP_MDLID_FINAL) + 1;
+                       return mdlid;
+               } else
+                       mdlid = (mdlid % MCAP_MDLID_FINAL) + 1;
+       } while (mdlid != mcl->next_mdl);
+
+       /* No more mdlids availables */
+       return 0;
+}
+
+static mcap_md_req *create_req(uint8_t op, uint16_t mdl_id)
+{
+       mcap_md_req *req_cmd;
+
+       req_cmd = g_new0(mcap_md_req, 1);
+
+       req_cmd->op = op;
+       req_cmd->mdl = htons(mdl_id);
+
+       return req_cmd;
+}
+
+static mcap_md_create_mdl_req *create_mdl_req(uint16_t mdl_id, uint8_t mdep,
+                                                               uint8_t conf)
+{
+       mcap_md_create_mdl_req *req_mdl;
+
+       req_mdl = g_new0(mcap_md_create_mdl_req, 1);
+
+       req_mdl->op = MCAP_MD_CREATE_MDL_REQ;
+       req_mdl->mdl = htons(mdl_id);
+       req_mdl->mdep = mdep;
+       req_mdl->conf = conf;
+
+       return req_mdl;
+}
+
+static gint compare_mdl(gconstpointer a, gconstpointer b)
+{
+       const struct mcap_mdl *mdla = a;
+       const struct mcap_mdl *mdlb = b;
+
+       if (mdla->mdlid == mdlb->mdlid)
+               return 0;
+       else if (mdla->mdlid < mdlb->mdlid)
+               return -1;
+       else
+               return 1;
+}
+
+static gboolean wait_response_timer(gpointer data)
+{
+       struct mcap_mcl *mcl = data;
+
+       GError *gerr = NULL;
+
+       RELEASE_TIMER(mcl);
+
+       g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+                                       "Timeout waiting response");
+
+       mcap_notify_error(mcl, gerr);
+
+       g_error_free(gerr);
+       mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data);
+       mcap_cache_mcl(mcl);
+
+       return FALSE;
+}
+
+gboolean mcap_create_mdl(struct mcap_mcl *mcl,
+                               uint8_t mdepid,
+                               uint8_t conf,
+                               mcap_mdl_operation_conf_cb connect_cb,
+                               gpointer user_data,
+                               GDestroyNotify destroy,
+                               GError **err)
+{
+       struct mcap_mdl *mdl;
+       struct mcap_mdl_op_cb *con;
+       mcap_md_create_mdl_req *cmd;
+       uint16_t id;
+
+       id = generate_mdlid(mcl);
+       if (!id) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+                                       "Not more mdlids available");
+               return FALSE;
+       }
+
+       mdl = g_new0(struct mcap_mdl, 1);
+       mdl->mcl = mcap_mcl_ref(mcl);
+       mdl->mdlid = id;
+       mdl->mdep_id = mdepid;
+       mdl->state = MDL_WAITING;
+
+       con = g_new0(struct mcap_mdl_op_cb, 1);
+       con->mdl = mcap_mdl_ref(mdl);
+       con->cb.op_conf = connect_cb;
+       con->destroy = destroy;
+       con->user_data = user_data;
+
+       cmd = create_mdl_req(id, mdepid, conf);
+       if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_create_mdl_req),
+                                                                       err)) {
+               mcap_mdl_unref(con->mdl);
+               g_free(con);
+               g_free(cmd);
+               return FALSE;
+       }
+
+       mcl->state = MCL_ACTIVE;
+       mcl->priv_data = con;
+
+       mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl),
+                                                               compare_mdl);
+       mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+                                                                       mcl);
+       return TRUE;
+}
+
+gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl,
+                               mcap_mdl_operation_cb reconnect_cb,
+                               gpointer user_data,
+                               GDestroyNotify destroy,
+                               GError **err)
+{
+       struct mcap_mdl_op_cb *con;
+       struct mcap_mcl *mcl = mdl->mcl;
+       mcap_md_req *cmd;
+
+       if (mdl->state != MDL_CLOSED) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+                                       "MDL is not closed");
+               return FALSE;
+       }
+
+       cmd = create_req(MCAP_MD_RECONNECT_MDL_REQ, mdl->mdlid);
+       if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) {
+               g_free(cmd);
+               return FALSE;
+       }
+
+       mdl->state = MDL_WAITING;
+
+       con = g_new0(struct mcap_mdl_op_cb, 1);
+       con->mdl = mcap_mdl_ref(mdl);
+       con->cb.op = reconnect_cb;
+       con->destroy = destroy;
+       con->user_data = user_data;
+
+       mcl->state = MCL_ACTIVE;
+       mcl->priv_data = con;
+
+       mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+                                                                       mcl);
+       return TRUE;
+}
+
+static gboolean send_delete_req(struct mcap_mcl *mcl,
+                                               struct mcap_mdl_op_cb *con,
+                                               uint16_t mdlid,
+                                               GError **err)
+{
+       mcap_md_req *cmd;
+
+       cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdlid);
+       if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) {
+               g_free(cmd);
+               return FALSE;
+       }
+
+       mcl->priv_data = con;
+
+       mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+                                                                       mcl);
+       return TRUE;
+}
+
+gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl,
+                                       mcap_mdl_notify_cb delete_cb,
+                                       gpointer user_data,
+                                       GDestroyNotify destroy,
+                                       GError **err)
+{
+       GSList *l;
+       struct mcap_mdl *mdl;
+       struct mcap_mdl_op_cb *con;
+
+       DBG("MCL in state: %d", mcl->state);
+       if (!mcl->mdls) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+                               "There are not MDLs created");
+               return FALSE;
+       }
+
+       for (l = mcl->mdls; l; l = l->next) {
+               mdl = l->data;
+               if (mdl->state != MDL_WAITING)
+                       mdl->state = MDL_DELETING;
+       }
+
+       con = g_new0(struct mcap_mdl_op_cb, 1);
+       con->mdl = NULL;
+       con->cb.notify = delete_cb;
+       con->destroy = destroy;
+       con->user_data = user_data;
+
+
+       if (!send_delete_req(mcl, con, MCAP_ALL_MDLIDS, err)) {
+               g_free(con);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean mcap_delete_mdl(struct mcap_mdl *mdl, mcap_mdl_notify_cb delete_cb,
+                                                       gpointer user_data,
+                                                       GDestroyNotify destroy,
+                                                       GError **err)
+{
+       struct mcap_mcl *mcl= mdl->mcl;
+       struct mcap_mdl_op_cb *con;
+       GSList *l;
+
+       l = g_slist_find(mcl->mdls, mdl);
+
+       if (!l) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL,
+                                       "%s" , error2str(MCAP_INVALID_MDEP));
+               return FALSE;
+       }
+
+       if (mdl->state == MDL_WAITING) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+                                                       "Mdl is not created");
+               return FALSE;
+       }
+
+       mdl->state = MDL_DELETING;
+
+       con = g_new0(struct mcap_mdl_op_cb, 1);
+       con->mdl = mcap_mdl_ref(mdl);
+       con->cb.notify = delete_cb;
+       con->destroy = destroy;
+       con->user_data = user_data;
+
+       if (!send_delete_req(mcl, con, mdl->mdlid, err)) {
+               mcap_mdl_unref(con->mdl);
+               g_free(con);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+gboolean mcap_mdl_abort(struct mcap_mdl *mdl, mcap_mdl_notify_cb abort_cb,
+                                                       gpointer user_data,
+                                                       GDestroyNotify destroy,
+                                                       GError **err)
+{
+       struct mcap_mdl_op_cb *con;
+       struct mcap_mcl *mcl = mdl->mcl;
+       mcap_md_req *cmd;
+
+       if (mdl->state != MDL_WAITING) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED,
+                                                       "Mdl in invalid state");
+               return FALSE;
+       }
+
+       cmd = create_req(MCAP_MD_ABORT_MDL_REQ, mdl->mdlid);
+       if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) {
+               g_free(cmd);
+               return FALSE;
+       }
+
+       con = g_new0(struct mcap_mdl_op_cb, 1);
+       con->mdl = mcap_mdl_ref(mdl);
+       con->cb.notify = abort_cb;
+       con->destroy = destroy;
+       con->user_data = user_data;
+
+       mcl->priv_data = con;
+       mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer,
+                                                                       mcl);
+       return TRUE;
+}
+
+static struct mcap_mcl *find_mcl(GSList *list, const bdaddr_t *addr)
+{
+       struct mcap_mcl *mcl;
+
+       for (; list; list = list->next) {
+               mcl = list->data;
+
+               if (!bacmp(&mcl->addr, addr))
+                       return mcl;
+       }
+
+       return NULL;
+}
+
+int mcap_mdl_get_fd(struct mcap_mdl *mdl)
+{
+       if (!mdl || mdl->state != MDL_CONNECTED)
+               return -ENOTCONN;
+
+       return g_io_channel_unix_get_fd(mdl->dc);
+}
+
+uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl)
+{
+       if (!mdl)
+               return MCAP_MDLID_RESERVED;
+
+       return mdl->mdlid;
+}
+
+static void close_mcl(struct mcap_mcl *mcl, gboolean cache_requested)
+{
+       gboolean save = ((!(mcl->ctrl & MCAP_CTRL_FREE)) && cache_requested);
+
+       RELEASE_TIMER(mcl);
+
+       if (mcl->cc) {
+               g_io_channel_shutdown(mcl->cc, TRUE, NULL);
+               g_io_channel_unref(mcl->cc);
+               mcl->cc = NULL;
+       }
+
+       if (mcl->wid) {
+               g_source_remove(mcl->wid);
+               mcl->wid = 0;
+       }
+
+       if (mcl->lcmd) {
+               g_free(mcl->lcmd);
+               mcl->lcmd = NULL;
+       }
+
+       if (mcl->priv_data)
+               free_mcl_priv_data(mcl);
+
+       g_slist_foreach(mcl->mdls, (GFunc) shutdown_mdl, NULL);
+
+       mcap_sync_stop(mcl);
+
+       mcl->state = MCL_IDLE;
+
+       if (save)
+               return;
+
+       g_slist_foreach(mcl->mdls, (GFunc) mcap_mdl_unref, NULL);
+       g_slist_free(mcl->mdls);
+       mcl->mdls = NULL;
+}
+
+static void mcap_mcl_shutdown(struct mcap_mcl *mcl)
+{
+       close_mcl(mcl, TRUE);
+}
+
+static void mcap_mcl_release(struct mcap_mcl *mcl)
+{
+       close_mcl(mcl, FALSE);
+}
+
+static void mcap_cache_mcl(struct mcap_mcl *mcl)
+{
+       GSList *l;
+       struct mcap_mcl *last;
+       int len;
+
+       if (mcl->ctrl & MCAP_CTRL_CACHED)
+               return;
+
+       mcl->mi->mcls = g_slist_remove(mcl->mi->mcls, mcl);
+
+       if (mcl->ctrl & MCAP_CTRL_NOCACHE) {
+               mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl);
+               mcap_mcl_release(mcl);
+               mcap_mcl_unref(mcl);
+               return;
+       }
+
+       DBG("Caching MCL");
+
+       len = g_slist_length(mcl->mi->cached);
+       if (len == MAX_CACHED) {
+               /* Remove the latest cached mcl */
+               l = g_slist_last(mcl->mi->cached);
+               last = l->data;
+               mcl->mi->cached = g_slist_remove(mcl->mi->cached, last);
+               last->ctrl &= ~MCAP_CTRL_CACHED;
+               if (last->ctrl & MCAP_CTRL_CONN) {
+                       /* We have to release this MCL if */
+                       /* connection is not successful    */
+                       last->ctrl |= MCAP_CTRL_FREE;
+               } else {
+                       mcap_mcl_release(last);
+                       last->mi->mcl_uncached_cb(last, last->mi->user_data);
+               }
+               mcap_mcl_unref(last);
+       }
+
+       mcl->mi->cached = g_slist_prepend(mcl->mi->cached, mcl);
+       mcl->ctrl |= MCAP_CTRL_CACHED;
+       mcap_mcl_shutdown(mcl);
+}
+
+static void mcap_uncache_mcl(struct mcap_mcl *mcl)
+{
+       if (!(mcl->ctrl & MCAP_CTRL_CACHED))
+               return;
+
+       DBG("Got MCL from cache");
+
+       mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl);
+       mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, mcl);
+       mcl->ctrl &= ~MCAP_CTRL_CACHED;
+       mcl->ctrl &= ~MCAP_CTRL_FREE;
+}
+
+void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache)
+{
+       if (!mcl)
+               return;
+
+       if (mcl->ctrl & MCAP_CTRL_FREE) {
+               mcap_mcl_release(mcl);
+               return;
+       }
+
+       if (!cache)
+               mcl->ctrl |= MCAP_CTRL_NOCACHE;
+
+       if (mcl->cc) {
+               g_io_channel_shutdown(mcl->cc, TRUE, NULL);
+               g_io_channel_unref(mcl->cc);
+               mcl->cc = NULL;
+               mcl->state = MCL_IDLE;
+       } else if ((mcl->ctrl & MCAP_CTRL_CACHED) &&
+                                       (mcl->ctrl & MCAP_CTRL_NOCACHE)) {
+               mcl->ctrl &= ~MCAP_CTRL_CACHED;
+               mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl);
+               mcap_mcl_release(mcl);
+               mcap_mcl_unref(mcl);
+       }
+}
+
+struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl)
+{
+       mcl->ref++;
+
+       DBG("mcap_mcl_ref(%p): ref=%d", mcl, mcl->ref);
+
+       return mcl;
+}
+
+void mcap_mcl_unref(struct mcap_mcl *mcl)
+{
+       mcl->ref--;
+
+       DBG("mcap_mcl_unref(%p): ref=%d", mcl, mcl->ref);
+
+       if (mcl->ref > 0)
+               return;
+
+       mcap_mcl_release(mcl);
+       mcap_instance_unref(mcl->mi);
+       g_free(mcl->cb);
+       g_free(mcl);
+}
+
+static gboolean parse_set_opts(struct mcap_mdl_cb *mdl_cb, GError **err,
+                                               McapMclCb cb1, va_list args)
+{
+       McapMclCb cb = cb1;
+       struct mcap_mdl_cb *c;
+
+       c = g_new0(struct mcap_mdl_cb, 1);
+
+       while (cb != MCAP_MDL_CB_INVALID) {
+               switch (cb) {
+               case MCAP_MDL_CB_CONNECTED:
+                       c->mdl_connected = va_arg(args, mcap_mdl_event_cb);
+                       break;
+               case MCAP_MDL_CB_CLOSED:
+                       c->mdl_closed = va_arg(args, mcap_mdl_event_cb);
+                       break;
+               case MCAP_MDL_CB_DELETED:
+                       c->mdl_deleted = va_arg(args, mcap_mdl_event_cb);
+                       break;
+               case MCAP_MDL_CB_ABORTED:
+                       c->mdl_aborted = va_arg(args, mcap_mdl_event_cb);
+                       break;
+               case MCAP_MDL_CB_REMOTE_CONN_REQ:
+                       c->mdl_conn_req = va_arg(args,
+                                               mcap_remote_mdl_conn_req_cb);
+                       break;
+               case MCAP_MDL_CB_REMOTE_RECONN_REQ:
+                       c->mdl_reconn_req = va_arg(args,
+                                               mcap_remote_mdl_reconn_req_cb);
+                       break;
+               default:
+                       g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+                                               "Unknown option %d", cb);
+                       g_free(c);
+                       return FALSE;
+               }
+               cb = va_arg(args, int);
+       }
+
+       /* Set new callbacks */
+       if (c->mdl_connected)
+               mdl_cb->mdl_connected = c->mdl_connected;
+       if (c->mdl_closed)
+               mdl_cb->mdl_closed = c->mdl_closed;
+       if (c->mdl_deleted)
+               mdl_cb->mdl_deleted = c->mdl_deleted;
+       if (c->mdl_aborted)
+               mdl_cb->mdl_aborted = c->mdl_aborted;
+       if (c->mdl_conn_req)
+               mdl_cb->mdl_conn_req = c->mdl_conn_req;
+       if (c->mdl_reconn_req)
+               mdl_cb->mdl_reconn_req = c->mdl_reconn_req;
+
+       g_free(c);
+
+       return TRUE;
+}
+
+gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data,
+                                       GError **gerr, McapMclCb cb1, ...)
+{
+       va_list args;
+       gboolean ret;
+
+       va_start(args, cb1);
+       ret = parse_set_opts(mcl->cb, gerr, cb1, args);
+       va_end(args);
+
+       if (!ret)
+               return FALSE;
+
+       mcl->cb->user_data = user_data;
+       return TRUE;
+}
+
+void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr)
+{
+       bacpy(addr, &mcl->addr);
+}
+
+static void mcap_del_mdl(gpointer elem, gpointer user_data)
+{
+       struct mcap_mdl *mdl = elem;
+       gboolean notify = *(gboolean *) user_data;
+
+       shutdown_mdl(mdl);
+       if (notify)
+               mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data);
+
+       mcap_mdl_unref(mdl);
+}
+
+static gboolean check_cmd_req_length(struct mcap_mcl *mcl, void *cmd,
+                               uint32_t rlen, uint32_t explen, uint8_t rspcod)
+{
+       mcap_md_req *req;
+       uint16_t mdl_id;
+
+       if (rlen != explen) {
+               if (rlen >= sizeof(mcap_md_req)) {
+                       req = cmd;
+                       mdl_id = ntohs(req->mdl);
+               } else {
+                       /* We can't get mdlid */
+                       mdl_id = MCAP_MDLID_RESERVED;
+               }
+               mcap_send_cmd(mcl, rspcod, MCAP_INVALID_PARAM_VALUE, mdl_id,
+                                                               NULL, 0);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static void process_md_create_mdl_req(struct mcap_mcl *mcl, void *cmd,
+                                                               uint32_t len)
+{
+       mcap_md_create_mdl_req *req;
+       struct mcap_mdl *mdl;
+       uint16_t mdl_id;
+       uint8_t mdep_id;
+       uint8_t cfga, conf;
+       uint8_t rsp;
+
+       if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_create_mdl_req),
+                                                       MCAP_MD_CREATE_MDL_RSP))
+               return;
+
+       req = cmd;
+       mdl_id = ntohs(req->mdl);
+       if (mdl_id < MCAP_MDLID_INITIAL || mdl_id > MCAP_MDLID_FINAL) {
+               mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDL,
+                                                       mdl_id, NULL, 0);
+               return;
+       }
+
+       mdep_id = req->mdep;
+       if (mdep_id > MCAP_MDEPID_FINAL) {
+               mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDEP,
+                                                       mdl_id, NULL, 0);
+               return;
+       }
+
+       mdl = get_mdl(mcl, mdl_id);
+       if (mdl && (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING )) {
+               /* Creation request arrives for a MDL that is being managed
+               * at current moment */
+               mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_MDL_BUSY,
+                                                       mdl_id, NULL, 0);
+               return;
+       }
+
+       cfga = conf = req->conf;
+       /* Callback to upper layer */
+       rsp = mcl->cb->mdl_conn_req(mcl, mdep_id, mdl_id, &conf,
+                                                       mcl->cb->user_data);
+       if (mcl->state == MCL_IDLE) {
+               /* MCL has been closed int the callback */
+               return;
+       }
+
+       if (cfga != 0 && cfga != conf) {
+               /* Remote device set default configuration but upper profile */
+               /* has changed it. Protocol Error: force closing the MCL by */
+               /* remote device using UNSPECIFIED_ERROR response */
+               mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP,
+                               MCAP_UNSPECIFIED_ERROR, mdl_id, NULL, 0);
+               return;
+       }
+       if (rsp != MCAP_SUCCESS) {
+               mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, rsp, mdl_id,
+                                                               NULL, 0);
+               return;
+       }
+
+       if (!mdl) {
+               mdl = g_new0(struct mcap_mdl, 1);
+               mdl->mcl = mcap_mcl_ref(mcl);
+               mdl->mdlid = mdl_id;
+               mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl),
+                                                               compare_mdl);
+       } else if (mdl->state == MDL_CONNECTED) {
+               /* MCAP specification says that we should close the MCL if
+                * it is open when we receive a MD_CREATE_MDL_REQ */
+               shutdown_mdl(mdl);
+       }
+
+       mdl->mdep_id = mdep_id;
+       mdl->state = MDL_WAITING;
+
+       mcl->state = MCL_PENDING;
+       mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_SUCCESS, mdl_id,
+                                                               &conf, 1);
+}
+
+static void process_md_reconnect_mdl_req(struct mcap_mcl *mcl, void *cmd,
+                                                               uint32_t len)
+{
+       mcap_md_req *req;
+       struct mcap_mdl *mdl;
+       uint16_t mdl_id;
+       uint8_t rsp;
+
+       if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req),
+                                               MCAP_MD_RECONNECT_MDL_RSP))
+               return;
+
+       req = cmd;
+       mdl_id = ntohs(req->mdl);
+
+       mdl = get_mdl(mcl, mdl_id);
+       if (!mdl) {
+               mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_INVALID_MDL,
+                                                       mdl_id, NULL, 0);
+               return;
+       } else if (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING ) {
+               /* Creation request arrives for a MDL that is being managed
+               * at current moment */
+               mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_MDL_BUSY,
+                                                       mdl_id, NULL, 0);
+               return;
+       }
+
+       /* Callback to upper layer */
+       rsp = mcl->cb->mdl_reconn_req(mdl, mcl->cb->user_data);
+       if (mcl->state == MCL_IDLE)
+               return;
+
+       if (rsp != MCAP_SUCCESS) {
+               mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, rsp, mdl_id,
+                                                               NULL, 0);
+               return;
+       }
+
+       if (mdl->state == MDL_CONNECTED)
+               shutdown_mdl(mdl);
+
+       mdl->state = MDL_WAITING;
+       mcl->state = MCL_PENDING;
+       mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_SUCCESS, mdl_id,
+                                                               NULL, 0);
+}
+
+static void process_md_abort_mdl_req(struct mcap_mcl *mcl, void *cmd,
+                                                               uint32_t len)
+{
+       mcap_md_req *req;
+       GSList *l;
+       struct mcap_mdl *mdl, *abrt;
+       uint16_t mdl_id;
+
+       if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req),
+                                                       MCAP_MD_ABORT_MDL_RSP))
+               return;
+
+       req = cmd;
+       mdl_id = ntohs(req->mdl);
+       mcl->state = MCL_CONNECTED;
+       abrt = NULL;
+       for (l = mcl->mdls; l; l = l->next) {
+               mdl = l->data;
+               if (mdl_id == mdl->mdlid && mdl->state == MDL_WAITING) {
+                       abrt = mdl;
+                       if (mcl->state != MCL_CONNECTED)
+                               break;
+                       continue;
+               }
+               if (mdl->state == MDL_CONNECTED && mcl->state != MCL_ACTIVE)
+                       mcl->state = MCL_ACTIVE;
+
+               if (abrt && mcl->state == MCL_ACTIVE)
+                       break;
+       }
+
+       if (!abrt) {
+               mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_INVALID_MDL,
+                                                       mdl_id, NULL, 0);
+               return;
+       }
+
+       mcl->cb->mdl_aborted(abrt, mcl->cb->user_data);
+       abrt->state = MDL_CLOSED;
+       mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id,
+                                                               NULL, 0);
+}
+
+static void process_md_delete_mdl_req(struct mcap_mcl *mcl, void *cmd,
+                                                               uint32_t len)
+{
+       mcap_md_req *req;
+       struct mcap_mdl *mdl, *aux;
+       uint16_t mdlid;
+       gboolean notify;
+       GSList *l;
+
+       if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req),
+                                                       MCAP_MD_DELETE_MDL_RSP))
+               return;
+
+       req = cmd;
+       mdlid = ntohs(req->mdl);
+       if (mdlid == MCAP_ALL_MDLIDS) {
+               notify = FALSE;
+               g_slist_foreach(mcl->mdls, mcap_del_mdl, &notify);
+               g_slist_free(mcl->mdls);
+               mcl->mdls = NULL;
+               mcl->state = MCL_CONNECTED;
+               /* NULL mdl means ALL_MDLS */
+               mcl->cb->mdl_deleted(NULL, mcl->cb->user_data);
+               goto resp;
+       }
+
+       if (mdlid < MCAP_MDLID_INITIAL || mdlid > MCAP_MDLID_FINAL) {
+               mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL,
+                                                               mdlid, NULL, 0);
+               return;
+       }
+
+       for (l = mcl->mdls, mdl = NULL; l; l = l->next) {
+               aux = l->data;
+               if (aux->mdlid == mdlid) {
+                       mdl = aux;
+                       break;
+               }
+       }
+
+       if (!mdl || mdl->state == MDL_WAITING) {
+               mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL,
+                                                               mdlid, NULL, 0);
+               return;
+       }
+
+       mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+       update_mcl_state(mcl);
+       notify = TRUE;
+       mcap_del_mdl(mdl, &notify);
+
+resp:
+       mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_SUCCESS, mdlid,
+                                                               NULL, 0);
+}
+
+static void invalid_req_state(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       uint16_t mdlr;
+
+       error("Invalid cmd received (op code = %d) in state %d", cmd[0],
+                                                               mcl->state);
+       /* Get previously mdlid sent to generate an appropriate
+        * response if it is possible */
+       mdlr = len < sizeof(mcap_md_req) ? MCAP_MDLID_RESERVED :
+                                       ntohs(((mcap_md_req *) cmd)->mdl);
+       mcap_send_cmd(mcl, cmd[0]+1, MCAP_INVALID_OPERATION, mdlr, NULL, 0);
+}
+
+/* Function used to process commands depending of MCL state */
+static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       switch (cmd[0]) {
+       case MCAP_MD_CREATE_MDL_REQ:
+               process_md_create_mdl_req(mcl, cmd, len);
+               break;
+       case MCAP_MD_RECONNECT_MDL_REQ:
+               process_md_reconnect_mdl_req(mcl, cmd, len);
+               break;
+       case MCAP_MD_DELETE_MDL_REQ:
+               process_md_delete_mdl_req(mcl, cmd, len);
+               break;
+       default:
+               invalid_req_state(mcl, cmd, len);
+       }
+}
+
+static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       if (cmd[0] == MCAP_MD_ABORT_MDL_REQ)
+               process_md_abort_mdl_req(mcl, cmd, len);
+       else
+               invalid_req_state(mcl, cmd, len);
+}
+
+static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       switch (cmd[0]) {
+       case MCAP_MD_CREATE_MDL_REQ:
+               process_md_create_mdl_req(mcl, cmd, len);
+               break;
+       case MCAP_MD_RECONNECT_MDL_REQ:
+               process_md_reconnect_mdl_req(mcl, cmd, len);
+               break;
+       case MCAP_MD_DELETE_MDL_REQ:
+               process_md_delete_mdl_req(mcl, cmd, len);
+               break;
+       default:
+               invalid_req_state(mcl, cmd, len);
+       }
+}
+
+/* Function used to process replies */
+static gboolean check_err_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp,
+                               uint32_t rlen, uint32_t len, GError **gerr)
+{
+       mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+       gint err = MCAP_ERROR_FAILED;
+       gboolean close = FALSE;
+       char *msg;
+
+       if (rsp->op == MCAP_ERROR_RSP) {
+               msg = "MCAP_ERROR_RSP received";
+               close = FALSE;
+               goto fail;
+       }
+
+       /* Check if the response matches with the last request */
+       if (rlen < sizeof(mcap_rsp) || (mcl->lcmd[0] + 1) != rsp->op) {
+               msg = "Protocol error";
+               close = FALSE;
+               goto fail;
+       }
+
+       if (rlen < len) {
+               msg = "Protocol error";
+               close = FALSE;
+               goto fail;
+       }
+
+       if (rsp->mdl != cmdlast->mdl) {
+               msg = "MDLID received doesn't match with MDLID sent";
+               close = TRUE;
+               goto fail;
+       }
+
+       if (rsp->rc == MCAP_REQUEST_NOT_SUPPORTED) {
+               msg = "Remote does not support opcodes";
+               mcl->ctrl &= ~MCAP_CTRL_STD_OP;
+               goto fail;
+       }
+
+       if (rsp->rc == MCAP_UNSPECIFIED_ERROR) {
+               msg = "Unspecified error";
+               close = TRUE;
+               goto fail;
+       }
+
+       if (rsp->rc != MCAP_SUCCESS) {
+               msg = error2str(rsp->rc);
+               err = rsp->rc;
+               goto fail;
+       }
+
+       return FALSE;
+
+fail:
+       g_set_error(gerr, MCAP_ERROR, err, "%s", msg);
+       return close;
+}
+
+static gboolean process_md_create_mdl_rsp(struct mcap_mcl *mcl,
+                                               mcap_rsp *rsp, uint32_t len)
+{
+       mcap_md_create_mdl_req *cmdlast = (mcap_md_create_mdl_req *) mcl->lcmd;
+       struct mcap_mdl_op_cb *conn = mcl->priv_data;
+       mcap_mdl_operation_conf_cb connect_cb = conn->cb.op_conf;
+       gpointer user_data = conn->user_data;
+       struct mcap_mdl *mdl = conn->mdl;
+       uint8_t conf = cmdlast->conf;
+       gboolean close;
+       GError *gerr = NULL;
+
+       close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp) + 1, &gerr);
+       g_free(mcl->lcmd);
+       mcl->lcmd = NULL;
+       mcl->req = MCL_AVAILABLE;
+
+       if (gerr)
+               goto fail;
+
+       /* Check if preferences changed */
+       if (conf != 0x00 && rsp->data[0] != conf) {
+               g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED,
+                                               "Configuration changed");
+               close = TRUE;
+               goto fail;
+       }
+
+       connect_cb(mdl, rsp->data[0], gerr, user_data);
+       return close;
+
+fail:
+       connect_cb(NULL, 0, gerr, user_data);
+       mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+       mcap_mdl_unref(mdl);
+       g_error_free(gerr);
+       update_mcl_state(mcl);
+       return close;
+}
+
+static gboolean process_md_reconnect_mdl_rsp(struct mcap_mcl *mcl,
+                                               mcap_rsp *rsp, uint32_t len)
+{
+       struct mcap_mdl_op_cb *reconn = mcl->priv_data;
+       mcap_mdl_operation_cb reconn_cb = reconn->cb.op;
+       gpointer user_data = reconn->user_data;
+       struct mcap_mdl *mdl = reconn->mdl;
+       GError *gerr = NULL;
+       gboolean close;
+
+       close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr);
+
+       g_free(mcl->lcmd);
+       mcl->lcmd = NULL;
+       mcl->req = MCL_AVAILABLE;
+
+       reconn_cb(mdl, gerr, user_data);
+       if (!gerr)
+               return close;
+
+       g_error_free(gerr);
+       shutdown_mdl(mdl);
+       update_mcl_state(mcl);
+
+       if (rsp->rc != MCAP_INVALID_MDL)
+               return close;
+
+       /* Remove cached mdlid */
+       mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+       mcl->cb->mdl_deleted(mdl, mcl->cb->user_data);
+       mcap_mdl_unref(mdl);
+
+       return close;
+}
+
+static gboolean process_md_abort_mdl_rsp(struct mcap_mcl *mcl,
+                                               mcap_rsp *rsp, uint32_t len)
+{
+       struct mcap_mdl_op_cb *abrt = mcl->priv_data;
+       mcap_mdl_notify_cb abrt_cb = abrt->cb.notify;
+       gpointer user_data = abrt->user_data;
+       struct mcap_mdl *mdl = abrt->mdl;
+       GError *gerr = NULL;
+       gboolean close;
+
+       close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr);
+
+       g_free(mcl->lcmd);
+       mcl->lcmd = NULL;
+       mcl->req = MCL_AVAILABLE;
+
+       abrt_cb(gerr, user_data);
+       shutdown_mdl(mdl);
+
+       if (len >= sizeof(mcap_rsp) && rsp->rc == MCAP_INVALID_MDL) {
+               mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+               mcl->cb->mdl_deleted(mdl, mcl->cb->user_data);
+               mcap_mdl_unref(mdl);
+       }
+
+       if (gerr)
+               g_error_free(gerr);
+
+       update_mcl_state(mcl);
+
+       return close;
+}
+
+static void restore_mdl(gpointer elem, gpointer data)
+{
+       struct mcap_mdl *mdl = elem;
+
+       if (mdl->state == MDL_DELETING) {
+               if (mdl->dc)
+                       mdl->state = MDL_CONNECTED;
+               else
+                       mdl->state = MDL_CLOSED;
+       } else if (mdl->state == MDL_CLOSED)
+               mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
+}
+
+static void check_mdl_del_err(struct mcap_mdl *mdl, mcap_rsp *rsp)
+{
+       if (rsp->rc != MCAP_ERROR_INVALID_MDL) {
+               restore_mdl(mdl, NULL);
+               return;
+       }
+
+       /* MDL does not exist in remote side, we can delete it */
+       mdl->mcl->mdls = g_slist_remove(mdl->mcl->mdls, mdl);
+       mcap_mdl_unref(mdl);
+}
+
+static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp,
+                                                               uint32_t len)
+{
+       struct mcap_mdl_op_cb *del = mcl->priv_data;
+       struct mcap_mdl *mdl = del->mdl;
+       mcap_mdl_notify_cb deleted_cb = del->cb.notify;
+       gpointer user_data = del->user_data;
+       mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd;
+       uint16_t mdlid = ntohs(cmdlast->mdl);
+       GError *gerr = NULL;
+       gboolean close;
+       gboolean notify = FALSE;
+
+       close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr);
+
+       g_free(mcl->lcmd);
+       mcl->lcmd = NULL;
+       mcl->req = MCL_AVAILABLE;
+
+       if (gerr) {
+               if (mdl)
+                       check_mdl_del_err(mdl, rsp);
+               else
+                       g_slist_foreach(mcl->mdls, restore_mdl, NULL);
+               deleted_cb(gerr, user_data);
+               g_error_free(gerr);
+               return close;
+       }
+
+       if (mdlid == MCAP_ALL_MDLIDS) {
+               g_slist_foreach(mcl->mdls, mcap_del_mdl, &notify);
+               g_slist_free(mcl->mdls);
+               mcl->mdls = NULL;
+               mcl->state = MCL_CONNECTED;
+       } else {
+               mcl->mdls = g_slist_remove(mcl->mdls, mdl);
+               update_mcl_state(mcl);
+               mcap_del_mdl(mdl, &notify);
+       }
+
+       deleted_cb(gerr, user_data);
+
+       return close;
+}
+
+static void post_process_rsp(struct mcap_mcl *mcl, struct mcap_mdl_op_cb *op)
+{
+       if (mcl->priv_data != op) {
+               /* Queued MCAP request in some callback. */
+               /* We should not delete the mcl private data */
+               free_mcap_mdl_op(op);
+       } else {
+               /* This is not a queued request. It's safe */
+               /* delete the mcl private data here. */
+               free_mcl_priv_data(mcl);
+       }
+}
+
+static void proc_response(struct mcap_mcl *mcl, void *buf, uint32_t len)
+{
+       struct mcap_mdl_op_cb *op = mcl->priv_data;
+       mcap_rsp *rsp = buf;
+       gboolean close;
+
+       RELEASE_TIMER(mcl);
+
+       switch (mcl->lcmd[0] + 1) {
+       case MCAP_MD_CREATE_MDL_RSP:
+               close = process_md_create_mdl_rsp(mcl, rsp, len);
+               post_process_rsp(mcl, op);
+               break;
+       case MCAP_MD_RECONNECT_MDL_RSP:
+               close = process_md_reconnect_mdl_rsp(mcl, rsp, len);
+               post_process_rsp(mcl, op);
+               break;
+       case MCAP_MD_ABORT_MDL_RSP:
+               close = process_md_abort_mdl_rsp(mcl, rsp, len);
+               post_process_rsp(mcl, op);
+               break;
+       case MCAP_MD_DELETE_MDL_RSP:
+               close = process_md_delete_mdl_rsp(mcl, rsp, len);
+               post_process_rsp(mcl, op);
+               break;
+       default:
+               DBG("Unknown cmd response received (op code = %d)", rsp->op);
+               close = TRUE;
+               break;
+       }
+
+       if (close) {
+               mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data);
+               mcap_cache_mcl(mcl);
+       }
+}
+
+static void proc_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       GError *gerr = NULL;
+
+       if (cmd[0] > MCAP_MD_SYNC_INFO_IND ||
+                                       (cmd[0] > MCAP_MD_DELETE_MDL_RSP &&
+                                       cmd[0] < MCAP_MD_SYNC_CAP_REQ)) {
+               error("Unknown cmd received (op code = %d)", cmd[0]);
+               mcap_send_cmd(mcl, MCAP_ERROR_RSP, MCAP_INVALID_OP_CODE,
+                                               MCAP_MDLID_RESERVED, NULL, 0);
+               return;
+       }
+
+       if (cmd[0] >= MCAP_MD_SYNC_CAP_REQ &&
+                                       cmd[0] <= MCAP_MD_SYNC_INFO_IND) {
+               proc_sync_cmd(mcl, cmd, len);
+               return;
+       }
+
+       if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) {
+               /* In case the remote device doesn't work correctly */
+               error("Remote device does not support opcodes, cmd ignored");
+               return;
+       }
+
+       if (mcl->req == MCL_WAITING_RSP) {
+               if (cmd[0] & 0x01) {
+                       /* Request arrived when a response is expected */
+                       if (mcl->role == MCL_INITIATOR)
+                               /* ignore */
+                               return;
+                       /* Initiator will ignore our last request */
+                       RELEASE_TIMER(mcl);
+                       mcl->req = MCL_AVAILABLE;
+                       g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_REQ_IGNORED,
+                               "Initiator sent a request with more priority");
+                       mcap_notify_error(mcl, gerr);
+                       proc_req[mcl->state](mcl, cmd, len);
+                       return;
+               }
+               proc_response(mcl, cmd, len);
+       } else if (cmd[0] & 0x01)
+               proc_req[mcl->state](mcl, cmd, len);
+}
+
+static gboolean mdl_event_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+
+       struct mcap_mdl *mdl = data;
+       gboolean notify;
+
+       DBG("Close MDL %d", mdl->mdlid);
+
+       notify = (mdl->state == MDL_CONNECTED);
+       shutdown_mdl(mdl);
+
+       update_mcl_state(mdl->mcl);
+
+       if (notify) {
+               /*Callback to upper layer */
+               mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data);
+       }
+
+       return FALSE;
+}
+
+static void mcap_connect_mdl_cb(GIOChannel *chan, GError *conn_err,
+                                                               gpointer data)
+{
+       struct mcap_mdl_op_cb *con = data;
+       struct mcap_mdl *mdl = con->mdl;
+       mcap_mdl_operation_cb cb = con->cb.op;
+       gpointer user_data = con->user_data;
+
+       DBG("mdl connect callback");
+
+       if (conn_err) {
+               DBG("ERROR: mdl connect callback");
+               mdl->state = MDL_CLOSED;
+               g_io_channel_unref(mdl->dc);
+               mdl->dc = NULL;
+               cb(mdl, conn_err, user_data);
+               return;
+       }
+
+       mdl->state = MDL_CONNECTED;
+       mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT,
+                                       G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       (GIOFunc) mdl_event_cb,
+                                       mcap_mdl_ref(mdl),
+                                       (GDestroyNotify) mcap_mdl_unref);
+
+       cb(mdl, conn_err, user_data);
+}
+
+gboolean mcap_connect_mdl(struct mcap_mdl *mdl, uint8_t mode,
+                                       uint16_t dcpsm,
+                                       mcap_mdl_operation_cb connect_cb,
+                                       gpointer user_data,
+                                       GDestroyNotify destroy,
+                                       GError **err)
+{
+       struct mcap_mdl_op_cb *con;
+
+       if (mdl->state != MDL_WAITING) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL,
+                                       "%s", error2str(MCAP_INVALID_MDL));
+               return FALSE;
+       }
+
+       if ((mode != L2CAP_MODE_ERTM) && (mode != L2CAP_MODE_STREAMING)) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+                                               "Invalid MDL configuration");
+               return FALSE;
+       }
+
+       con = g_new0(struct mcap_mdl_op_cb, 1);
+       con->mdl = mcap_mdl_ref(mdl);
+       con->cb.op = connect_cb;
+       con->destroy = destroy;
+       con->user_data = user_data;
+
+       mdl->dc = bt_io_connect(BT_IO_L2CAP, mcap_connect_mdl_cb, con,
+                               (GDestroyNotify) free_mcap_mdl_op, err,
+                               BT_IO_OPT_SOURCE_BDADDR, &mdl->mcl->mi->src,
+                               BT_IO_OPT_DEST_BDADDR, &mdl->mcl->addr,
+                               BT_IO_OPT_PSM, dcpsm,
+                               BT_IO_OPT_MTU, MCAP_DC_MTU,
+                               BT_IO_OPT_SEC_LEVEL, mdl->mcl->mi->sec,
+                               BT_IO_OPT_MODE, mode,
+                               BT_IO_OPT_INVALID);
+       if (!mdl->dc) {
+               DBG("MDL Connection error");
+               mdl->state = MDL_CLOSED;
+               mcap_mdl_unref(con->mdl);
+               g_free(con);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean mcl_control_cb(GIOChannel *chan, GIOCondition cond,
+                                                               gpointer data)
+{
+       GError *gerr = NULL;
+       struct mcap_mcl *mcl = data;
+       int sk, len;
+       uint8_t buf[MCAP_CC_MTU];
+
+       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+               goto fail;
+
+       sk = g_io_channel_unix_get_fd(chan);
+       len = read(sk, buf, sizeof(buf));
+       if (len < 0)
+               goto fail;
+
+       proc_cmd(mcl, buf, (uint32_t) len);
+       return TRUE;
+
+fail:
+       if (mcl->state != MCL_IDLE) {
+               if (mcl->req == MCL_WAITING_RSP) {
+                       /* notify error in pending callback */
+                       g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_MCL_CLOSED,
+                                                               "MCL closed");
+                       mcap_notify_error(mcl, gerr);
+                       g_error_free(gerr);
+               }
+               mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data);
+       }
+       mcap_cache_mcl(mcl);
+       return FALSE;
+}
+
+static void mcap_connect_mcl_cb(GIOChannel *chan, GError *conn_err,
+                                                       gpointer user_data)
+{
+       char dstaddr[18];
+       struct connect_mcl *con = user_data;
+       struct mcap_mcl *aux, *mcl = con->mcl;
+       mcap_mcl_connect_cb connect_cb = con->connect_cb;
+       gpointer data = con->user_data;
+       GError *gerr = NULL;
+
+       mcl->ctrl &= ~MCAP_CTRL_CONN;
+
+       if (conn_err) {
+               if (mcl->ctrl & MCAP_CTRL_FREE) {
+                       mcap_mcl_release(mcl);
+                       mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data);
+               }
+               connect_cb(NULL, conn_err, data);
+               return;
+       }
+
+       ba2str(&mcl->addr, dstaddr);
+
+       aux = find_mcl(mcl->mi->mcls, &mcl->addr);
+       if (aux) {
+               /* Double MCL connection case */
+               error("MCL error: Device %s is already connected", dstaddr);
+               g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS,
+                                       "MCL %s is already connected", dstaddr);
+               connect_cb(NULL, gerr, data);
+               g_error_free(gerr);
+               return;
+       }
+
+       mcl->state = MCL_CONNECTED;
+       mcl->role = MCL_INITIATOR;
+       mcl->req = MCL_AVAILABLE;
+       mcl->ctrl |= MCAP_CTRL_STD_OP;
+
+       mcap_sync_init(mcl);
+
+       if (mcl->ctrl & MCAP_CTRL_CACHED)
+               mcap_uncache_mcl(mcl);
+       else {
+               mcl->ctrl &= ~MCAP_CTRL_FREE;
+               mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls,
+                                                       mcap_mcl_ref(mcl));
+       }
+
+       mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                               (GIOFunc) mcl_control_cb,
+                               mcap_mcl_ref(mcl),
+                               (GDestroyNotify) mcap_mcl_unref);
+       connect_cb(mcl, gerr, data);
+}
+
+static void set_mdl_properties(GIOChannel *chan, struct mcap_mdl *mdl)
+{
+       struct mcap_mcl *mcl = mdl->mcl;
+
+       mdl->state = MDL_CONNECTED;
+       mdl->dc = g_io_channel_ref(chan);
+       mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT,
+                                       G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       (GIOFunc) mdl_event_cb,
+                                       mcap_mdl_ref(mdl),
+                                       (GDestroyNotify) mcap_mdl_unref);
+
+       mcl->state = MCL_ACTIVE;
+       mcl->cb->mdl_connected(mdl, mcl->cb->user_data);
+}
+
+static void mcl_io_destroy(gpointer data)
+{
+       struct connect_mcl *con = data;
+
+       mcap_mcl_unref(con->mcl);
+       if (con->destroy)
+               con->destroy(con->user_data);
+       g_free(con);
+}
+
+gboolean mcap_create_mcl(struct mcap_instance *mi,
+                               const bdaddr_t *addr,
+                               uint16_t ccpsm,
+                               mcap_mcl_connect_cb connect_cb,
+                               gpointer user_data,
+                               GDestroyNotify destroy,
+                               GError **err)
+{
+       struct mcap_mcl *mcl;
+       struct connect_mcl *con;
+
+       mcl = find_mcl(mi->mcls, addr);
+       if (mcl) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS,
+                                       "MCL is already connected.");
+               return FALSE;
+       }
+
+       mcl = find_mcl(mi->cached, addr);
+       if (!mcl) {
+               mcl = g_new0(struct mcap_mcl, 1);
+               mcl->mi = mcap_instance_ref(mi);
+               mcl->state = MCL_IDLE;
+               bacpy(&mcl->addr, addr);
+               set_default_cb(mcl);
+               mcl->next_mdl = (rand() % MCAP_MDLID_FINAL) + 1;
+       }
+
+       mcl->ctrl |= MCAP_CTRL_CONN;
+
+       con = g_new0(struct connect_mcl, 1);
+       con->mcl = mcap_mcl_ref(mcl);
+       con->connect_cb = connect_cb;
+       con->destroy = destroy;
+       con->user_data = user_data;
+
+       mcl->cc = bt_io_connect(BT_IO_L2CAP, mcap_connect_mcl_cb, con,
+                               mcl_io_destroy, err,
+                               BT_IO_OPT_SOURCE_BDADDR, &mi->src,
+                               BT_IO_OPT_DEST_BDADDR, addr,
+                               BT_IO_OPT_PSM, ccpsm,
+                               BT_IO_OPT_MTU, MCAP_CC_MTU,
+                               BT_IO_OPT_SEC_LEVEL, mi->sec,
+                               BT_IO_OPT_MODE, L2CAP_MODE_ERTM,
+                               BT_IO_OPT_INVALID);
+       if (!mcl->cc) {
+               mcl->ctrl &= ~MCAP_CTRL_CONN;
+               if (mcl->ctrl & MCAP_CTRL_FREE) {
+                       mcap_mcl_release(mcl);
+                       mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data);
+               }
+               mcap_mcl_unref(con->mcl);
+               g_free(con);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void connect_dc_event_cb(GIOChannel *chan, GError *gerr,
+                                                       gpointer user_data)
+{
+       struct mcap_instance *mi = user_data;
+       struct mcap_mcl *mcl;
+       struct mcap_mdl *mdl;
+       GError *err = NULL;
+       bdaddr_t dst;
+       GSList *l;
+
+       if (gerr)
+               return;
+
+       bt_io_get(chan, BT_IO_L2CAP, &err,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               goto drop;
+       }
+
+       mcl = find_mcl(mi->mcls, &dst);
+       if (!mcl || mcl->state != MCL_PENDING)
+               goto drop;
+
+       for (l = mcl->mdls; l; l = l->next) {
+               mdl = l->data;
+               if (mdl->state == MDL_WAITING) {
+                       set_mdl_properties(chan, mdl);
+                       return;
+               }
+       }
+
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void set_mcl_conf(GIOChannel *chan, struct mcap_mcl *mcl)
+{
+       gboolean reconn;
+
+       mcl->state = MCL_CONNECTED;
+       mcl->role = MCL_ACCEPTOR;
+       mcl->req = MCL_AVAILABLE;
+       mcl->cc = g_io_channel_ref(chan);
+       mcl->ctrl |= MCAP_CTRL_STD_OP;
+
+       mcap_sync_init(mcl);
+
+       reconn = (mcl->ctrl & MCAP_CTRL_CACHED);
+       if (reconn)
+               mcap_uncache_mcl(mcl);
+       else
+               mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls,
+                                                       mcap_mcl_ref(mcl));
+
+       mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                               (GIOFunc) mcl_control_cb,
+                               mcap_mcl_ref(mcl),
+                               (GDestroyNotify) mcap_mcl_unref);
+
+       /* Callback to report new MCL */
+       if (reconn)
+               mcl->mi->mcl_reconnected_cb(mcl, mcl->mi->user_data);
+       else
+               mcl->mi->mcl_connected_cb(mcl, mcl->mi->user_data);
+}
+
+static void connect_mcl_event_cb(GIOChannel *chan, GError *gerr,
+                                                       gpointer user_data)
+{
+       struct mcap_instance *mi = user_data;
+       struct mcap_mcl *mcl;
+       bdaddr_t dst;
+       char address[18], srcstr[18];
+       GError *err = NULL;
+
+       if (gerr)
+               return;
+
+       bt_io_get(chan, BT_IO_L2CAP, &err,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_DEST, address,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               goto drop;
+       }
+
+       ba2str(&mi->src, srcstr);
+       mcl = find_mcl(mi->mcls, &dst);
+       if (mcl) {
+               error("Control channel already created with %s on adapter %s",
+                               address, srcstr);
+               goto drop;
+       }
+
+       mcl = find_mcl(mi->cached, &dst);
+       if (!mcl) {
+               mcl = g_new0(struct mcap_mcl, 1);
+               mcl->mi = mcap_instance_ref(mi);
+               bacpy(&mcl->addr, &dst);
+               set_default_cb(mcl);
+               mcl->next_mdl = (rand() % MCAP_MDLID_FINAL) + 1;
+       }
+
+       set_mcl_conf(chan, mcl);
+
+       return;
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+struct mcap_instance *mcap_create_instance(bdaddr_t *src,
+                                       BtIOSecLevel sec,
+                                       uint16_t ccpsm,
+                                       uint16_t dcpsm,
+                                       mcap_mcl_event_cb mcl_connected,
+                                       mcap_mcl_event_cb mcl_reconnected,
+                                       mcap_mcl_event_cb mcl_disconnected,
+                                       mcap_mcl_event_cb mcl_uncached,
+                                       mcap_info_ind_event_cb mcl_sync_info_ind,
+                                       gpointer user_data,
+                                       GError **gerr)
+{
+       struct mcap_instance *mi;
+
+       if (sec < BT_IO_SEC_MEDIUM) {
+               g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+                               "Security level can't be minor of %d",
+                               BT_IO_SEC_MEDIUM);
+               return NULL;
+       }
+
+       if (!(mcl_connected && mcl_reconnected &&
+                       mcl_disconnected && mcl_uncached)) {
+               g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+                               "The callbacks can't be null");
+               return NULL;
+       }
+
+       mi = g_new0(struct mcap_instance, 1);
+
+       bacpy(&mi->src, src);
+
+       mi->sec = sec;
+       mi->mcl_connected_cb = mcl_connected;
+       mi->mcl_reconnected_cb = mcl_reconnected;
+       mi->mcl_disconnected_cb = mcl_disconnected;
+       mi->mcl_uncached_cb = mcl_uncached;
+       mi->mcl_sync_infoind_cb = mcl_sync_info_ind;
+       mi->user_data = user_data;
+       mi->csp_enabled = FALSE;
+
+       /* Listen incoming connections in control channel */
+       mi->ccio = bt_io_listen(BT_IO_L2CAP, connect_mcl_event_cb, NULL, mi,
+                               NULL, gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &mi->src,
+                               BT_IO_OPT_PSM, ccpsm,
+                               BT_IO_OPT_MTU, MCAP_CC_MTU,
+                               BT_IO_OPT_SEC_LEVEL, sec,
+                               BT_IO_OPT_MODE, L2CAP_MODE_ERTM,
+                               BT_IO_OPT_INVALID);
+       if (!mi->ccio) {
+               error("%s", (*gerr)->message);
+               g_free(mi);
+               return NULL;
+       }
+
+       /* Listen incoming connections in data channels */
+       mi->dcio = bt_io_listen(BT_IO_L2CAP, connect_dc_event_cb, NULL, mi,
+                               NULL, gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &mi->src,
+                               BT_IO_OPT_PSM, dcpsm,
+                               BT_IO_OPT_MTU, MCAP_DC_MTU,
+                               BT_IO_OPT_SEC_LEVEL, sec,
+                               BT_IO_OPT_INVALID);
+       if (!mi->dcio) {
+               g_io_channel_shutdown(mi->ccio, TRUE, NULL);
+               g_io_channel_unref(mi->ccio);
+               mi->ccio = NULL;
+               error("%s", (*gerr)->message);
+               g_free(mi);
+               return NULL;
+       }
+
+       /* Initialize random seed to generate mdlids for this instance */
+       srand(time(NULL));
+
+       return mcap_instance_ref(mi);
+}
+
+void mcap_release_instance(struct mcap_instance *mi)
+{
+       GSList *l;
+
+       if (!mi)
+               return;
+
+       if (mi->ccio) {
+               g_io_channel_shutdown(mi->ccio, TRUE, NULL);
+               g_io_channel_unref(mi->ccio);
+               mi->ccio = NULL;
+       }
+
+       if (mi->dcio) {
+               g_io_channel_shutdown(mi->dcio, TRUE, NULL);
+               g_io_channel_unref(mi->dcio);
+               mi->dcio = NULL;
+       }
+
+       for (l = mi->mcls; l; l = l->next) {
+               mcap_mcl_release(l->data);
+               mcap_mcl_unref(l->data);
+       }
+
+       g_slist_free(mi->mcls);
+       mi->mcls = NULL;
+
+       for (l = mi->cached; l; l = l->next) {
+               mcap_mcl_release(l->data);
+               mcap_mcl_unref(l->data);
+       }
+
+       g_slist_free(mi->cached);
+       mi->cached = NULL;
+}
+
+struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi)
+{
+       mi->ref++;
+
+       DBG("mcap_instance_ref(%p): ref=%d", mi, mi->ref);
+
+       return mi;
+}
+
+void mcap_instance_unref(struct mcap_instance *mi)
+{
+       mi->ref--;
+
+       DBG("mcap_instance_unref(%p): ref=%d", mi, mi->ref);
+
+       if (mi->ref > 0)
+               return;
+
+       mcap_release_instance(mi);
+       g_free(mi);
+}
+
+uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err)
+{
+       uint16_t lpsm;
+
+       if (!(mi && mi->ccio)) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+                       "Invalid MCAP instance");
+               return 0;
+       }
+
+       if (!bt_io_get(mi->ccio, BT_IO_L2CAP, err,
+                       BT_IO_OPT_PSM, &lpsm,
+                       BT_IO_OPT_INVALID))
+               return 0;
+
+       return lpsm;
+}
+
+uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err)
+{
+       uint16_t lpsm;
+
+       if (!(mi && mi->dcio)) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+                       "Invalid MCAP instance");
+               return 0;
+       }
+
+       if (!bt_io_get(mi->dcio, BT_IO_L2CAP, err,
+                       BT_IO_OPT_PSM, &lpsm,
+                       BT_IO_OPT_INVALID))
+               return 0;
+
+       return lpsm;
+}
+
+gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode,
+                                                               GError **err)
+{
+       if (!(mi && mi->dcio)) {
+               g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
+                                               "Invalid MCAP instance");
+               return FALSE;
+       }
+
+       return bt_io_set(mi->dcio, BT_IO_L2CAP, err, BT_IO_OPT_MODE, mode,
+                                                       BT_IO_OPT_INVALID);
+}
+
+struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl)
+{
+       mdl->ref++;
+
+       DBG("mcap_mdl_ref(%p): ref=%d", mdl, mdl->ref);
+
+       return mdl;
+}
+
+void mcap_mdl_unref(struct mcap_mdl *mdl)
+{
+       mdl->ref--;
+
+       DBG("mcap_mdl_unref(%p): ref=%d", mdl, mdl->ref);
+
+       if (mdl->ref > 0)
+               return;
+
+       free_mdl(mdl);
+}
diff --git a/health/mcap.h b/health/mcap.h
new file mode 100644 (file)
index 0000000..34a8382
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ *
+ *  MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *  Copyright (C) 2010 Signove
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __MCAP_H
+#define __MCAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MCAP_VERSION   0x0100  /* current version 01.00 */
+
+/* bytes to get MCAP Supported Procedures */
+#define MCAP_SUP_PROC  0x06
+
+/* maximum transmission unit for channels */
+#define MCAP_CC_MTU    48
+#define MCAP_DC_MTU    L2CAP_DEFAULT_MTU
+
+/* MCAP Standard Op Codes */
+#define MCAP_ERROR_RSP                 0x00
+#define MCAP_MD_CREATE_MDL_REQ         0x01
+#define MCAP_MD_CREATE_MDL_RSP         0x02
+#define MCAP_MD_RECONNECT_MDL_REQ      0x03
+#define MCAP_MD_RECONNECT_MDL_RSP      0x04
+#define MCAP_MD_ABORT_MDL_REQ          0x05
+#define MCAP_MD_ABORT_MDL_RSP          0x06
+#define MCAP_MD_DELETE_MDL_REQ         0x07
+#define MCAP_MD_DELETE_MDL_RSP         0x08
+
+/* MCAP Clock Sync Op Codes */
+#define MCAP_MD_SYNC_CAP_REQ           0x11
+#define MCAP_MD_SYNC_CAP_RSP           0x12
+#define MCAP_MD_SYNC_SET_REQ           0x13
+#define MCAP_MD_SYNC_SET_RSP           0x14
+#define MCAP_MD_SYNC_INFO_IND          0x15
+
+/* MCAP Response codes */
+#define MCAP_SUCCESS                   0x00
+#define MCAP_INVALID_OP_CODE           0x01
+#define MCAP_INVALID_PARAM_VALUE       0x02
+#define MCAP_INVALID_MDEP              0x03
+#define MCAP_MDEP_BUSY                 0x04
+#define MCAP_INVALID_MDL               0x05
+#define MCAP_MDL_BUSY                  0x06
+#define MCAP_INVALID_OPERATION         0x07
+#define MCAP_RESOURCE_UNAVAILABLE      0x08
+#define MCAP_UNSPECIFIED_ERROR         0x09
+#define MCAP_REQUEST_NOT_SUPPORTED     0x0A
+#define MCAP_CONFIGURATION_REJECTED    0x0B
+
+/* MDL IDs */
+#define MCAP_MDLID_RESERVED            0x0000
+#define MCAP_MDLID_INITIAL             0x0001
+#define MCAP_MDLID_FINAL               0xFEFF
+#define MCAP_ALL_MDLIDS                        0xFFFF
+
+/* MDEP IDs */
+#define MCAP_MDEPID_INITIAL            0x00
+#define MCAP_MDEPID_FINAL              0x7F
+
+/* CSP special values */
+#define MCAP_BTCLOCK_IMMEDIATE         0xffffffffUL
+#define MCAP_TMSTAMP_DONTSET           0xffffffffffffffffULL
+#define MCAP_BTCLOCK_MAX               0x0fffffff
+#define MCAP_BTCLOCK_FIELD             (MCAP_BTCLOCK_MAX + 1)
+
+/*
+ * MCAP Request Packet Format
+ */
+
+typedef struct {
+       uint8_t         op;
+       uint16_t        mdl;
+       uint8_t         mdep;
+       uint8_t         conf;
+} __attribute__ ((packed)) mcap_md_create_mdl_req;
+
+typedef struct {
+       uint8_t         op;
+       uint16_t        mdl;
+} __attribute__ ((packed)) mcap_md_req;
+
+/*
+ * MCAP Response Packet Format
+ */
+
+typedef struct {
+       uint8_t         op;
+       uint8_t         rc;
+       uint16_t        mdl;
+       uint8_t         data[0];
+} __attribute__ ((packed)) mcap_rsp;
+
+/*
+ * MCAP Clock Synchronization Protocol
+ */
+
+typedef struct {
+       uint8_t         op;
+       uint16_t        timest;
+} __attribute__ ((packed)) mcap_md_sync_cap_req;
+
+typedef struct {
+       uint8_t         op;
+       uint8_t         rc;
+} __attribute__ ((packed)) mcap_md_sync_rsp;
+
+typedef struct {
+        uint8_t         op;
+       uint8_t         rc;
+       uint8_t         btclock;
+        uint16_t        sltime;
+       uint16_t        timestnr;
+       uint16_t        timestna;
+} __attribute__ ((packed)) mcap_md_sync_cap_rsp;
+
+typedef struct {
+       uint8_t         op;
+       uint8_t         timestui;
+       uint32_t        btclock;
+       uint64_t        timestst;
+} __attribute__ ((packed)) mcap_md_sync_set_req;
+
+typedef struct {
+       int8_t          op;
+       uint8_t         rc;
+       uint32_t        btclock;
+       uint64_t        timestst;
+       uint16_t        timestsa;
+} __attribute__ ((packed)) mcap_md_sync_set_rsp;
+
+typedef struct {
+       uint8_t         op;
+       uint32_t        btclock;
+       uint64_t        timestst;
+       uint16_t        timestsa;
+} __attribute__ ((packed)) mcap_md_sync_info_ind;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_H */
diff --git a/health/mcap_internal.h b/health/mcap_internal.h
new file mode 100644 (file)
index 0000000..7b044ef
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ *
+ *  MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __MCAP_INTERNAL_H
+#define __MCAP_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+       MCL_CONNECTED,
+       MCL_PENDING,
+       MCL_ACTIVE,
+       MCL_IDLE
+} MCLState;
+
+typedef enum {
+       MCL_ACCEPTOR,
+       MCL_INITIATOR
+} MCLRole;
+
+typedef enum {
+       MCL_AVAILABLE,
+       MCL_WAITING_RSP
+} MCAPCtrl;
+
+typedef enum {
+       MDL_WAITING,
+       MDL_CONNECTED,
+       MDL_DELETING,
+       MDL_CLOSED
+} MDLState;
+
+struct mcap_mdl_cb {
+       mcap_mdl_event_cb               mdl_connected;  /* Remote device has created a MDL */
+       mcap_mdl_event_cb               mdl_closed;     /* Remote device has closed a MDL */
+       mcap_mdl_event_cb               mdl_deleted;    /* Remote device requested deleting a MDL */
+       mcap_mdl_event_cb               mdl_aborted;    /* Remote device aborted the mdl creation */
+       mcap_remote_mdl_conn_req_cb     mdl_conn_req;   /* Remote device requested creating a MDL */
+       mcap_remote_mdl_reconn_req_cb   mdl_reconn_req; /* Remote device requested reconnecting a MDL */
+       gpointer                        user_data;      /* User data */
+};
+
+struct mcap_instance {
+       bdaddr_t                src;                    /* Source address */
+       GIOChannel              *ccio;                  /* Control Channel IO */
+       GIOChannel              *dcio;                  /* Data Channel IO */
+       GSList                  *mcls;                  /* MCAP instance list */
+       GSList                  *cached;                /* List with all cached MCLs (MAX_CACHED macro) */
+       BtIOSecLevel            sec;                    /* Security level */
+       mcap_mcl_event_cb       mcl_connected_cb;       /* New MCL connected */
+       mcap_mcl_event_cb       mcl_reconnected_cb;     /* Old MCL has been reconnected */
+       mcap_mcl_event_cb       mcl_disconnected_cb;    /* MCL disconnected */
+       mcap_mcl_event_cb       mcl_uncached_cb;        /* MCL has been removed from MCAP cache */
+       mcap_info_ind_event_cb  mcl_sync_infoind_cb;    /* (CSP Master) Received info indication */
+       gpointer                user_data;              /* Data to be provided in callbacks */
+       gint                    ref;                    /* Reference counter */
+
+       gboolean                csp_enabled;            /* CSP: functionality enabled */
+};
+
+struct mcap_csp;
+struct mcap_mdl_op_cb;
+
+struct mcap_mcl {
+       struct mcap_instance    *mi;            /* MCAP instance where this MCL belongs */
+       bdaddr_t                addr;           /* Device address */
+       GIOChannel              *cc;            /* MCAP Control Channel IO */
+       guint                   wid;            /* MCL Watcher id */
+       GSList                  *mdls;          /* List of Data Channels shorted by mdlid */
+       MCLState                state;          /* Current MCL State */
+       MCLRole                 role;           /* Initiator or acceptor of this MCL */
+       MCAPCtrl                req;            /* Request control flag */
+       struct mcap_mdl_op_cb   *priv_data;     /* Temporal data to manage responses */
+       struct mcap_mdl_cb      *cb;            /* MDL callbacks */
+       guint                   tid;            /* Timer id for waiting for a response */
+       uint8_t                 *lcmd;          /* Last command sent */
+       gint                    ref;            /* References counter */
+       uint8_t                 ctrl;           /* MCL control flag */
+       uint16_t                next_mdl;       /* id used to create next MDL */
+       struct mcap_csp         *csp;           /* CSP control structure */
+};
+
+#define        MCAP_CTRL_CACHED        0x01    /* MCL is cached */
+#define        MCAP_CTRL_STD_OP        0x02    /* Support for standard op codes */
+#define        MCAP_CTRL_SYNC_OP       0x04    /* Support for synchronization commands */
+#define        MCAP_CTRL_CONN          0x08    /* MCL is in connecting process */
+#define        MCAP_CTRL_FREE          0x10    /* MCL is marked as releasable */
+#define        MCAP_CTRL_NOCACHE       0x20    /* MCL is marked as not cacheable */
+
+struct mcap_mdl {
+       struct mcap_mcl         *mcl;           /* MCL where this MDL belongs */
+       GIOChannel              *dc;            /* MCAP Data Channel IO */
+       guint                   wid;            /* MDL Watcher id */
+       uint16_t                mdlid;          /* MDL id */
+       uint8_t                 mdep_id;        /* MCAP Data End Point */
+       MDLState                state;          /* MDL state */
+       gint                    ref;            /* References counter */
+};
+
+struct sync_info_ind_data {
+       uint32_t        btclock;
+       uint64_t        timestamp;
+       uint16_t        accuracy;
+};
+
+int mcap_send_data(int sock, const void *buf, uint32_t size);
+
+void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len);
+void mcap_sync_init(struct mcap_mcl *mcl);
+void mcap_sync_stop(struct mcap_mcl *mcl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_INTERNAL_H */
diff --git a/health/mcap_lib.h b/health/mcap_lib.h
new file mode 100644 (file)
index 0000000..8fcc141
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ *
+ *  MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __MCAP_LIB_H
+#define __MCAP_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+/* MCAP Error Response Codes */
+       MCAP_ERROR_INVALID_OP_CODE = 1,
+       MCAP_ERROR_INVALID_PARAM_VALUE,
+       MCAP_ERROR_INVALID_MDEP,
+       MCAP_ERROR_MDEP_BUSY,
+       MCAP_ERROR_INVALID_MDL,
+       MCAP_ERROR_MDL_BUSY,
+       MCAP_ERROR_INVALID_OPERATION,
+       MCAP_ERROR_RESOURCE_UNAVAILABLE,
+       MCAP_ERROR_UNSPECIFIED_ERROR,
+       MCAP_ERROR_REQUEST_NOT_SUPPORTED,
+       MCAP_ERROR_CONFIGURATION_REJECTED,
+/* MCAP Internal Errors */
+       MCAP_ERROR_INVALID_ARGS,
+       MCAP_ERROR_ALREADY_EXISTS,
+       MCAP_ERROR_REQ_IGNORED,
+       MCAP_ERROR_MCL_CLOSED,
+       MCAP_ERROR_FAILED
+} McapError;
+
+typedef enum {
+       MCAP_MDL_CB_INVALID,
+       MCAP_MDL_CB_CONNECTED,          /* mcap_mdl_event_cb */
+       MCAP_MDL_CB_CLOSED,             /* mcap_mdl_event_cb */
+       MCAP_MDL_CB_DELETED,            /* mcap_mdl_event_cb */
+       MCAP_MDL_CB_ABORTED,            /* mcap_mdl_event_cb */
+       MCAP_MDL_CB_REMOTE_CONN_REQ,    /* mcap_remote_mdl_conn_req_cb */
+       MCAP_MDL_CB_REMOTE_RECONN_REQ   /* mcap_remote_mdl_reconn_req_cb */
+} McapMclCb;
+
+struct mcap_instance;
+struct mcap_mcl;
+struct mcap_mdl;
+struct sync_info_ind_data;
+
+/************ Callbacks ************/
+
+/* MDL callbacks */
+
+typedef void (* mcap_mdl_event_cb) (struct mcap_mdl *mdl, gpointer data);
+typedef void (* mcap_mdl_operation_conf_cb) (struct mcap_mdl *mdl, uint8_t conf,
+                                               GError *err, gpointer data);
+typedef void (* mcap_mdl_operation_cb) (struct mcap_mdl *mdl, GError *err,
+                                               gpointer data);
+typedef void (* mcap_mdl_notify_cb) (GError *err, gpointer data);
+
+/* Next function should return an MCAP appropriate response code */
+typedef uint8_t (* mcap_remote_mdl_conn_req_cb) (struct mcap_mcl *mcl,
+                                               uint8_t mdepid, uint16_t mdlid,
+                                               uint8_t *conf, gpointer data);
+typedef uint8_t (* mcap_remote_mdl_reconn_req_cb) (struct mcap_mdl *mdl,
+                                               gpointer data);
+
+/* MCL callbacks */
+
+typedef void (* mcap_mcl_event_cb) (struct mcap_mcl *mcl, gpointer data);
+typedef void (* mcap_mcl_connect_cb) (struct mcap_mcl *mcl, GError *err,
+                                                               gpointer data);
+
+/* CSP callbacks */
+
+typedef void (* mcap_info_ind_event_cb) (struct mcap_mcl *mcl,
+                                       struct sync_info_ind_data *data);
+
+typedef void (* mcap_sync_cap_cb) (struct mcap_mcl *mcl,
+                                       uint8_t mcap_err,
+                                       uint8_t btclockres,
+                                       uint16_t synclead,
+                                       uint16_t tmstampres,
+                                       uint16_t tmstampacc,
+                                       GError *err,
+                                       gpointer data);
+
+typedef void (* mcap_sync_set_cb) (struct mcap_mcl *mcl,
+                                       uint8_t mcap_err,
+                                       uint32_t btclock,
+                                       uint64_t timestamp,
+                                       uint16_t accuracy,
+                                       GError *err,
+                                       gpointer data);
+
+/************ Operations ************/
+
+/* MDL operations */
+
+gboolean mcap_create_mdl(struct mcap_mcl *mcl,
+                               uint8_t mdepid,
+                               uint8_t conf,
+                               mcap_mdl_operation_conf_cb connect_cb,
+                               gpointer user_data,
+                               GDestroyNotify destroy,
+                               GError **err);
+gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl,
+                               mcap_mdl_operation_cb reconnect_cb,
+                               gpointer user_data,
+                               GDestroyNotify destroy,
+                               GError **err);
+gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl,
+                               mcap_mdl_notify_cb delete_cb,
+                               gpointer user_data,
+                               GDestroyNotify destroy,
+                               GError **err);
+gboolean mcap_delete_mdl(struct mcap_mdl *mdl,
+                               mcap_mdl_notify_cb delete_cb,
+                               gpointer user_data,
+                               GDestroyNotify destroy,
+                               GError **err);
+gboolean mcap_connect_mdl(struct mcap_mdl *mdl,
+                               uint8_t mode,
+                               uint16_t dcpsm,
+                               mcap_mdl_operation_cb connect_cb,
+                               gpointer user_data,
+                               GDestroyNotify destroy,
+                               GError **err);
+gboolean mcap_mdl_abort(struct mcap_mdl *mdl,
+                               mcap_mdl_notify_cb abort_cb,
+                               gpointer user_data,
+                               GDestroyNotify destroy,
+                               GError **err);
+
+int mcap_mdl_get_fd(struct mcap_mdl *mdl);
+uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl);
+
+struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl);
+void mcap_mdl_unref(struct mcap_mdl *mdl);
+
+/* MCL operations */
+
+gboolean mcap_create_mcl(struct mcap_instance *mi,
+                               const bdaddr_t *addr,
+                               uint16_t ccpsm,
+                               mcap_mcl_connect_cb connect_cb,
+                               gpointer user_data,
+                               GDestroyNotify destroy,
+                               GError **err);
+void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache);
+gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data,
+                                       GError **gerr, McapMclCb cb1, ...);
+void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr);
+
+struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl);
+void mcap_mcl_unref(struct mcap_mcl *mcl);
+
+/* CSP operations */
+
+void mcap_enable_csp(struct mcap_instance *mi);
+void mcap_disable_csp(struct mcap_instance *mi);
+
+uint64_t mcap_get_timestamp(struct mcap_mcl *mcl,
+                               struct timespec *given_time);
+uint32_t mcap_get_btclock(struct mcap_mcl *mcl);
+
+void mcap_sync_cap_req(struct mcap_mcl *mcl,
+                       uint16_t reqacc,
+                       mcap_sync_cap_cb cb,
+                       gpointer user_data,
+                       GError **err);
+
+void mcap_sync_set_req(struct mcap_mcl *mcl,
+                       uint8_t update,
+                       uint32_t btclock,
+                       uint64_t timestamp,
+                       mcap_sync_set_cb cb,
+                       gpointer user_data,
+                       GError **err);
+
+/* MCAP main operations */
+
+struct mcap_instance *mcap_create_instance(bdaddr_t *src,
+                                       BtIOSecLevel sec, uint16_t ccpsm,
+                                       uint16_t dcpsm,
+                                       mcap_mcl_event_cb mcl_connected,
+                                       mcap_mcl_event_cb mcl_reconnected,
+                                       mcap_mcl_event_cb mcl_disconnected,
+                                       mcap_mcl_event_cb mcl_uncached,
+                                       mcap_info_ind_event_cb mcl_sync_info_ind,
+                                       gpointer user_data,
+                                       GError **gerr);
+void mcap_release_instance(struct mcap_instance *mi);
+
+struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi);
+void mcap_instance_unref(struct mcap_instance *mi);
+
+uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err);
+uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err);
+
+gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode,
+                                                               GError **err);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MCAP_LIB_H */
diff --git a/health/mcap_sync.c b/health/mcap_sync.c
new file mode 100644 (file)
index 0000000..6d8d66b
--- /dev/null
@@ -0,0 +1,1012 @@
+/*
+ *
+ *  MCAP for BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *  Copyright (C) 2010 Signove
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <netinet/in.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <adapter.h>
+#include <manager.h>
+#include <btio.h>
+#include <log.h>
+
+#include "mcap.h"
+#include "mcap_lib.h"
+#include "mcap_internal.h"
+
+#define MCAP_BTCLOCK_HALF (MCAP_BTCLOCK_FIELD / 2)
+#define CLK CLOCK_MONOTONIC
+
+#define MCAP_CSP_ERROR g_quark_from_static_string("mcap-csp-error-quark")
+#define MAX_RETRIES    10
+#define SAMPLE_COUNT   20
+
+struct mcap_csp {
+       uint64_t        base_tmstamp;   /* CSP base timestamp */
+       struct timespec base_time;      /* CSP base time when timestamp set */
+       guint           local_caps;     /* CSP-Master: have got remote caps */
+       guint           remote_caps;    /* CSP-Slave: remote master got caps */
+       guint           rem_req_acc;    /* CSP-Slave: accuracy required by master */
+       guint           ind_expected;   /* CSP-Master: indication expected */
+       MCAPCtrl        csp_req;        /* CSP-Master: Request control flag */
+       guint           ind_timer;      /* CSP-Slave: indication timer */
+       guint           set_timer;      /* CSP-Slave: delayed set timer */
+       void            *set_data;      /* CSP-Slave: delayed set data */
+       void            *csp_priv_data; /* CSP-Master: In-flight request data */
+};
+
+struct mcap_sync_cap_cbdata {
+       mcap_sync_cap_cb        cb;
+       gpointer                user_data;
+};
+
+struct mcap_sync_set_cbdata {
+       mcap_sync_set_cb        cb;
+       gpointer                user_data;
+};
+
+struct csp_caps {
+       int ts_acc;             /* timestamp accuracy */
+       int ts_res;             /* timestamp resolution */
+       int latency;            /* Read BT clock latency */
+       int preempt_thresh;     /* Preemption threshold for latency */
+       int syncleadtime_ms;    /* SyncLeadTime in ms */
+};
+
+struct sync_set_data {
+       uint8_t update;
+       uint32_t sched_btclock;
+       uint64_t timestamp;
+       int ind_freq;
+       gboolean role;
+};
+
+#define hton64(x)     ntoh64(x)
+
+static gboolean csp_caps_initialized = FALSE;
+struct csp_caps _caps;
+
+static int send_sync_cmd(struct mcap_mcl *mcl, const void *buf, uint32_t size)
+{
+       int sock;
+
+       if (mcl->cc == NULL)
+               return -1;
+
+       sock = g_io_channel_unix_get_fd(mcl->cc);
+       return mcap_send_data(sock, buf, size);
+}
+
+static int send_unsupported_cap_req(struct mcap_mcl *mcl)
+{
+       mcap_md_sync_cap_rsp *cmd;
+       int sent;
+
+       cmd = g_new0(mcap_md_sync_cap_rsp, 1);
+       cmd->op = MCAP_MD_SYNC_CAP_RSP;
+       cmd->rc = MCAP_REQUEST_NOT_SUPPORTED;
+
+       sent = send_sync_cmd(mcl, cmd, sizeof(*cmd));
+       g_free(cmd);
+
+       return sent;
+}
+
+static int send_unsupported_set_req(struct mcap_mcl *mcl)
+{
+       mcap_md_sync_set_rsp *cmd;
+       int sent;
+
+       cmd = g_new0(mcap_md_sync_set_rsp, 1);
+       cmd->op = MCAP_MD_SYNC_SET_RSP;
+       cmd->rc = MCAP_REQUEST_NOT_SUPPORTED;
+
+       sent = send_sync_cmd(mcl, cmd, sizeof(*cmd));
+       g_free(cmd);
+
+       return sent;
+}
+
+static void reset_tmstamp(struct mcap_csp *csp, struct timespec *base_time,
+                               uint64_t new_tmstamp)
+{
+       csp->base_tmstamp = new_tmstamp;
+       if (base_time)
+               csp->base_time = *base_time;
+       else
+               clock_gettime(CLK, &csp->base_time);
+}
+
+void mcap_sync_init(struct mcap_mcl *mcl)
+{
+       if (!mcl->mi->csp_enabled) {
+               mcl->csp = NULL;
+               return;
+       }
+
+       mcl->csp = g_new0(struct mcap_csp, 1);
+
+       mcl->csp->rem_req_acc = 10000; /* safe divisor */
+       mcl->csp->set_data = NULL;
+       mcl->csp->csp_priv_data = NULL;
+
+       reset_tmstamp(mcl->csp, NULL, 0);
+}
+
+void mcap_sync_stop(struct mcap_mcl *mcl)
+{
+       if (!mcl->csp)
+               return;
+
+       if (mcl->csp->ind_timer)
+               g_source_remove(mcl->csp->ind_timer);
+
+       if (mcl->csp->set_timer)
+               g_source_remove(mcl->csp->set_timer);
+
+       if (mcl->csp->set_data)
+               g_free(mcl->csp->set_data);
+
+       if (mcl->csp->csp_priv_data)
+               g_free(mcl->csp->csp_priv_data);
+
+       mcl->csp->ind_timer = 0;
+       mcl->csp->set_timer = 0;
+       mcl->csp->set_data = NULL;
+       mcl->csp->csp_priv_data = NULL;
+
+       g_free(mcl->csp);
+       mcl->csp = NULL;
+}
+
+static uint64_t time_us(struct timespec *tv)
+{
+       return tv->tv_sec * 1000000 + tv->tv_nsec / 1000;
+}
+
+static int64_t bt2us(int bt)
+{
+       return bt * 312.5;
+}
+
+static int bt2ms(int bt)
+{
+       return bt * 312.5 / 1000;
+}
+
+static int btoffset(uint32_t btclk1, uint32_t btclk2)
+{
+       int offset = btclk2 - btclk1;
+
+       if (offset <= -MCAP_BTCLOCK_HALF)
+               offset += MCAP_BTCLOCK_FIELD;
+       else if (offset > MCAP_BTCLOCK_HALF)
+               offset -= MCAP_BTCLOCK_FIELD;
+
+       return offset;
+}
+
+static int btdiff(uint32_t btclk1, uint32_t btclk2)
+{
+       return btoffset(btclk1, btclk2);
+}
+
+static gboolean valid_btclock(uint32_t btclk)
+{
+       return btclk <= MCAP_BTCLOCK_MAX;
+}
+
+/* This call may fail; either deal with retry or use read_btclock_retry */
+static gboolean read_btclock(struct mcap_mcl *mcl, uint32_t *btclock,
+                                                       uint16_t *btaccuracy)
+{
+       int which = 1;
+       struct btd_adapter *adapter;
+
+       adapter = manager_find_adapter(&mcl->mi->src);
+
+       if (!adapter)
+               return FALSE;
+
+       if (btd_adapter_read_clock(adapter, &mcl->addr, which, 1000,
+                                               btclock, btaccuracy) < 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static gboolean read_btclock_retry(struct mcap_mcl *mcl, uint32_t *btclock,
+                                                       uint16_t *btaccuracy)
+{
+       int retries = 5;
+
+       while (--retries >= 0) {
+               if (read_btclock(mcl, btclock, btaccuracy))
+                       return TRUE;
+               DBG("CSP: retrying to read bt clock...");
+       }
+
+       return FALSE;
+}
+
+static gboolean get_btrole(struct mcap_mcl *mcl)
+{
+       int sock, flags;
+       socklen_t len;
+
+       if (mcl->cc == NULL)
+               return -1;
+
+       sock = g_io_channel_unix_get_fd(mcl->cc);
+       len = sizeof(flags);
+
+       if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len))
+               DBG("CSP: could not read role");
+
+       return flags & L2CAP_LM_MASTER;
+}
+
+uint64_t mcap_get_timestamp(struct mcap_mcl *mcl,
+                               struct timespec *given_time)
+{
+       struct timespec now;
+       uint64_t tmstamp;
+
+       if (!mcl->csp)
+               return MCAP_TMSTAMP_DONTSET;
+
+       if (given_time)
+               now = *given_time;
+       else
+               clock_gettime(CLK, &now);
+
+       tmstamp = time_us(&now) - time_us(&mcl->csp->base_time)
+               + mcl->csp->base_tmstamp;
+
+       return tmstamp;
+}
+
+uint32_t mcap_get_btclock(struct mcap_mcl *mcl)
+{
+       uint32_t btclock;
+       uint16_t accuracy;
+
+       if (!mcl->csp)
+               return MCAP_BTCLOCK_IMMEDIATE;
+
+       if (!read_btclock_retry(mcl, &btclock, &accuracy))
+               btclock = 0xffffffff;
+
+       return btclock;
+}
+
+static gboolean initialize_caps(struct mcap_mcl *mcl)
+{
+       struct timespec t1, t2;
+       int latencies[SAMPLE_COUNT];
+       int latency, avg, dev;
+       uint32_t btclock;
+       uint16_t btaccuracy;
+       int i;
+       int retries;
+
+       clock_getres(CLK, &t1);
+
+       _caps.ts_res = time_us(&t1);
+       if (_caps.ts_res < 1)
+               _caps.ts_res = 1;
+
+       _caps.ts_acc = 20; /* ppm, estimated */
+
+       /* A little exercise before measuing latency */
+       clock_gettime(CLK, &t1);
+       read_btclock_retry(mcl, &btclock, &btaccuracy);
+
+       /* Read clock a number of times and measure latency */
+       avg = 0;
+       i = 0;
+       retries = MAX_RETRIES;
+       while (i < SAMPLE_COUNT && retries > 0) {
+               clock_gettime(CLK, &t1);
+               if (!read_btclock(mcl, &btclock, &btaccuracy)) {
+                       retries--;
+                       continue;
+               }
+               clock_gettime(CLK, &t2);
+
+               latency = time_us(&t2) - time_us(&t1);
+               latencies[i] = latency;
+               avg += latency;
+               i++;
+       }
+
+       if (retries <= 0)
+               return FALSE;
+
+       /* Calculate average and deviation */
+       avg /= SAMPLE_COUNT;
+       dev = 0;
+       for (i = 0; i < SAMPLE_COUNT; ++i)
+               dev += abs(latencies[i] - avg);
+       dev /= SAMPLE_COUNT;
+
+       /* Calculate corrected average, without 'freak' latencies */
+       latency = 0;
+       for (i = 0; i < SAMPLE_COUNT; ++i) {
+               if (latencies[i] > (avg + dev * 6))
+                       latency += avg;
+               else
+                       latency += latencies[i];
+       }
+       latency /= SAMPLE_COUNT;
+
+       _caps.latency = latency;
+       _caps.preempt_thresh = latency * 4;
+       _caps.syncleadtime_ms = latency * 50 / 1000;
+
+       csp_caps_initialized = TRUE;
+       return TRUE;
+}
+
+static struct csp_caps *caps(struct mcap_mcl *mcl)
+{
+       if (!csp_caps_initialized)
+               if (!initialize_caps(mcl)) {
+                       /* Temporary failure in reading BT clock */
+                       return NULL;
+               }
+
+       return &_caps;
+}
+
+static int send_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t rspcode,
+                       uint8_t btclockres, uint16_t synclead,
+                       uint16_t tmstampres, uint16_t tmstampacc)
+{
+       mcap_md_sync_cap_rsp *rsp;
+       int sent;
+
+       rsp = g_new0(mcap_md_sync_cap_rsp, 1);
+
+       rsp->op = MCAP_MD_SYNC_CAP_RSP;
+       rsp->rc = rspcode;
+
+       rsp->btclock = btclockres;
+       rsp->sltime = htons(synclead);
+       rsp->timestnr = htons(tmstampres);
+       rsp->timestna = htons(tmstampacc);
+
+       sent = send_sync_cmd(mcl, rsp, sizeof(*rsp));
+       g_free(rsp);
+
+       return sent;
+}
+
+static void proc_sync_cap_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       mcap_md_sync_cap_req *req;
+       uint16_t required_accuracy;
+       uint16_t our_accuracy;
+       uint32_t btclock;
+       uint16_t btres;
+
+       if (len != sizeof(mcap_md_sync_cap_req)) {
+               send_sync_cap_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+                                       0, 0, 0, 0);
+               return;
+       }
+
+       if (!caps(mcl)) {
+               send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE,
+                                       0, 0, 0, 0);
+               return;
+       }
+
+       req = (mcap_md_sync_cap_req *) cmd;
+       required_accuracy = ntohs(req->timest);
+       our_accuracy = caps(mcl)->ts_acc;
+
+       if (required_accuracy < our_accuracy || required_accuracy < 1) {
+               send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE,
+                                       0, 0, 0, 0);
+               return;
+       }
+
+       if (!read_btclock_retry(mcl, &btclock, &btres)) {
+               send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE,
+                                       0, 0, 0, 0);
+               return;
+       }
+
+       mcl->csp->remote_caps = 1;
+       mcl->csp->rem_req_acc = required_accuracy;
+
+       send_sync_cap_rsp(mcl, MCAP_SUCCESS, btres,
+                               caps(mcl)->syncleadtime_ms,
+                               caps(mcl)->ts_res, our_accuracy);
+}
+
+static int send_sync_set_rsp(struct mcap_mcl *mcl, uint8_t rspcode,
+                       uint32_t btclock, uint64_t timestamp,
+                       uint16_t tmstampres)
+{
+       mcap_md_sync_set_rsp *rsp;
+       int sent;
+
+       rsp = g_new0(mcap_md_sync_set_rsp, 1);
+
+       rsp->op = MCAP_MD_SYNC_SET_RSP;
+       rsp->rc = rspcode;
+       rsp->btclock = htonl(btclock);
+       rsp->timestst = hton64(timestamp);
+       rsp->timestsa = htons(tmstampres);
+
+       sent = send_sync_cmd(mcl, rsp, sizeof(*rsp));
+       g_free(rsp);
+
+       return sent;
+}
+
+static gboolean get_all_clocks(struct mcap_mcl *mcl, uint32_t *btclock,
+                               struct timespec *base_time,
+                               uint64_t *timestamp)
+{
+       int latency;
+       int retry = 5;
+       uint16_t btres;
+       struct timespec t0;
+
+       if (!caps(mcl))
+               return FALSE;
+
+       latency = caps(mcl)->preempt_thresh + 1;
+
+       while (latency > caps(mcl)->preempt_thresh && --retry >= 0) {
+
+               clock_gettime(CLK, &t0);
+
+               if (!read_btclock(mcl, btclock, &btres))
+                       continue;
+
+               clock_gettime(CLK, base_time);
+
+               /* Tries to detect preemption between clock_gettime
+                * and read_btclock by measuring transaction time
+                */
+               latency = time_us(base_time) - time_us(&t0);
+       }
+
+       *timestamp = mcap_get_timestamp(mcl, base_time);
+
+       return TRUE;
+}
+
+static gboolean sync_send_indication(gpointer user_data)
+{
+       struct mcap_mcl *mcl;
+       mcap_md_sync_info_ind *cmd;
+       uint32_t btclock;
+       uint64_t tmstamp;
+       struct timespec base_time;
+       int sent;
+
+       if (!user_data)
+               return FALSE;
+
+       mcl = user_data;
+
+       if (!caps(mcl))
+               return FALSE;
+
+       if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp))
+               return FALSE;
+
+       cmd = g_new0(mcap_md_sync_info_ind, 1);
+
+       cmd->op = MCAP_MD_SYNC_INFO_IND;
+       cmd->btclock = htonl(btclock);
+       cmd->timestst = hton64(tmstamp);
+       cmd->timestsa = htons(caps(mcl)->latency);
+
+       sent = send_sync_cmd(mcl, cmd, sizeof(*cmd));
+       g_free(cmd);
+
+       return !sent;
+}
+
+static gboolean proc_sync_set_req_phase2(gpointer user_data)
+{
+       struct mcap_mcl *mcl;
+       struct sync_set_data *data;
+       uint8_t update;
+       uint32_t sched_btclock;
+       uint64_t new_tmstamp;
+       int ind_freq;
+       int role;
+       uint32_t btclock;
+       uint64_t tmstamp;
+       struct timespec base_time;
+       uint16_t tmstampacc;
+       gboolean reset;
+       int delay;
+
+       if (!user_data)
+               return FALSE;
+
+       mcl = user_data;
+
+       if (!mcl->csp->set_data)
+               return FALSE;
+
+       data = mcl->csp->set_data;
+       update = data->update;
+       sched_btclock = data->sched_btclock;
+       new_tmstamp = data->timestamp;
+       ind_freq = data->ind_freq;
+       role = data->role;
+
+       if (!caps(mcl)) {
+               send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+               return FALSE;
+       }
+
+       if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp)) {
+               send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+               return FALSE;
+       }
+
+       if (get_btrole(mcl) != role) {
+               send_sync_set_rsp(mcl, MCAP_INVALID_OPERATION, 0, 0, 0);
+               return FALSE;
+       }
+
+       reset = (new_tmstamp != MCAP_TMSTAMP_DONTSET);
+
+       if (reset) {
+               if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE) {
+                       delay = bt2us(btdiff(sched_btclock, btclock));
+                       if (delay >= 0 || ((new_tmstamp - delay) > 0)) {
+                               new_tmstamp += delay;
+                               DBG("CSP: reset w/ delay %dus, compensated",
+                                                                       delay);
+                       } else
+                               DBG("CSP: reset w/ delay %dus, uncompensated",
+                                                                       delay);
+               }
+
+               reset_tmstamp(mcl->csp, &base_time, new_tmstamp);
+               tmstamp = new_tmstamp;
+       }
+
+       tmstampacc = caps(mcl)->latency + caps(mcl)->ts_acc;
+
+       if (mcl->csp->ind_timer) {
+               g_source_remove(mcl->csp->ind_timer);
+               mcl->csp->ind_timer = 0;
+       }
+
+       if (update) {
+               int when = ind_freq + caps(mcl)->syncleadtime_ms;
+               mcl->csp->ind_timer = g_timeout_add(when,
+                                               sync_send_indication,
+                                               mcl);
+       }
+
+       send_sync_set_rsp(mcl, MCAP_SUCCESS, btclock, tmstamp, tmstampacc);
+
+       /* First indication after set is immediate */
+       if (update)
+               sync_send_indication(mcl);
+
+       return FALSE;
+}
+
+static void proc_sync_set_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       mcap_md_sync_set_req *req;
+       uint32_t sched_btclock, cur_btclock;
+       uint16_t btres;
+       uint8_t update;
+       uint64_t timestamp;
+       struct sync_set_data *set_data;
+       int phase2_delay, ind_freq, when;
+
+       if (len != sizeof(mcap_md_sync_set_req)) {
+               send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+               return;
+       }
+
+       req = (mcap_md_sync_set_req *) cmd;
+       sched_btclock = ntohl(req->btclock);
+       update = req->timestui;
+       timestamp = ntoh64(req->timestst);
+
+       if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE &&
+                       !valid_btclock(sched_btclock)) {
+               send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+               return;
+       }
+
+       if (update > 1) {
+               send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+               return;
+       }
+
+       if (!mcl->csp->remote_caps) {
+               /* Remote side did not ask our capabilities yet */
+               send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0);
+               return;
+       }
+
+       if (!caps(mcl)) {
+               send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+               return;
+       }
+
+       if (!read_btclock_retry(mcl, &cur_btclock, &btres)) {
+               send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0);
+               return;
+       }
+
+       if (sched_btclock == MCAP_BTCLOCK_IMMEDIATE)
+               phase2_delay = 0;
+       else {
+               phase2_delay = btdiff(cur_btclock, sched_btclock);
+
+               if (phase2_delay < 0) {
+                       /* can not reset in the past tense */
+                       send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+                                               0, 0, 0);
+                       return;
+               }
+
+               /* Convert to miliseconds */
+               phase2_delay = bt2ms(phase2_delay);
+
+               if (phase2_delay > 61*1000) {
+                       /* More than 60 seconds in the future */
+                       send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+                                               0, 0, 0);
+                       return;
+               } else if (phase2_delay < caps(mcl)->latency / 1000) {
+                       /* Too fast for us to do in time */
+                       send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+                                               0, 0, 0);
+                       return;
+               }
+       }
+
+       if (update) {
+               /* Indication frequency: required accuracy divided by ours */
+               /* Converted to milisseconds */
+               ind_freq = (1000 * mcl->csp->rem_req_acc) / caps(mcl)->ts_acc;
+
+               if (ind_freq < MAX(caps(mcl)->latency * 2 / 1000, 100)) {
+                       /* Too frequent, we can't handle */
+                       send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE,
+                                               0, 0, 0);
+                       return;
+               }
+
+               DBG("CSP: indication every %dms", ind_freq);
+       } else
+               ind_freq = 0;
+
+       if (mcl->csp->ind_timer) {
+               /* Old indications are no longer sent */
+               g_source_remove(mcl->csp->ind_timer);
+               mcl->csp->ind_timer = 0;
+       }
+
+       if (!mcl->csp->set_data)
+               mcl->csp->set_data = g_new0(struct sync_set_data, 1);
+
+       set_data = (struct sync_set_data *) mcl->csp->set_data;
+
+       set_data->update = update;
+       set_data->sched_btclock = sched_btclock;
+       set_data->timestamp = timestamp;
+       set_data->ind_freq = ind_freq;
+       set_data->role = get_btrole(mcl);
+
+       /* TODO is there some way to schedule a call based directly on
+        * a BT clock value, instead of this estimation that uses
+        * the SO clock? */
+
+       if (phase2_delay > 0) {
+               when = phase2_delay + caps(mcl)->syncleadtime_ms;
+               mcl->csp->set_timer = g_timeout_add(when,
+                                               proc_sync_set_req_phase2,
+                                               mcl);
+       } else
+               proc_sync_set_req_phase2(mcl);
+
+       /* First indication is immediate */
+       if (update)
+               sync_send_indication(mcl);
+}
+
+static void proc_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       mcap_md_sync_cap_rsp *rsp;
+       uint8_t mcap_err;
+       uint8_t btclockres;
+       uint16_t synclead;
+       uint16_t tmstampres;
+       uint16_t tmstampacc;
+       struct mcap_sync_cap_cbdata *cbdata;
+       mcap_sync_cap_cb cb;
+       gpointer user_data;
+
+       if (mcl->csp->csp_req != MCAP_MD_SYNC_CAP_REQ) {
+               DBG("CSP: got unexpected cap respose");
+               return;
+       }
+
+       if (!mcl->csp->csp_priv_data) {
+               DBG("CSP: no priv data for cap respose");
+               return;
+       }
+
+       cbdata = mcl->csp->csp_priv_data;
+       cb = cbdata->cb;
+       user_data = cbdata->user_data;
+       g_free(cbdata);
+
+       mcl->csp->csp_priv_data = NULL;
+       mcl->csp->csp_req = 0;
+
+       if (len != sizeof(mcap_md_sync_cap_rsp)) {
+               DBG("CSP: got corrupted cap respose");
+               return;
+       }
+
+       rsp = (mcap_md_sync_cap_rsp *) cmd;
+       mcap_err = rsp->rc;
+       btclockres = rsp->btclock;
+       synclead = ntohs(rsp->sltime);
+       tmstampres = ntohs(rsp->timestnr);
+       tmstampacc = ntohs(rsp->timestna);
+
+       if (!mcap_err)
+               mcl->csp->local_caps = TRUE;
+
+       cb(mcl, mcap_err, btclockres, synclead, tmstampres, tmstampacc, NULL,
+                                                               user_data);
+}
+
+static void proc_sync_set_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       mcap_md_sync_set_rsp *rsp;
+       uint8_t mcap_err;
+       uint32_t btclock;
+       uint64_t timestamp;
+       uint16_t accuracy;
+       struct mcap_sync_set_cbdata *cbdata;
+       mcap_sync_set_cb cb;
+       gpointer user_data;
+
+       if (mcl->csp->csp_req != MCAP_MD_SYNC_SET_REQ) {
+               DBG("CSP: got unexpected set respose");
+               return;
+       }
+
+       if (!mcl->csp->csp_priv_data) {
+               DBG("CSP: no priv data for set respose");
+               return;
+       }
+
+       cbdata = mcl->csp->csp_priv_data;
+       cb = cbdata->cb;
+       user_data = cbdata->user_data;
+       g_free(cbdata);
+
+       mcl->csp->csp_priv_data = NULL;
+       mcl->csp->csp_req = 0;
+
+       if (len != sizeof(mcap_md_sync_set_rsp)) {
+               DBG("CSP: got corrupted set respose");
+               return;
+       }
+
+       rsp = (mcap_md_sync_set_rsp *) cmd;
+       mcap_err = rsp->rc;
+       btclock = ntohl(rsp->btclock);
+       timestamp = ntoh64(rsp->timestst);
+       accuracy = ntohs(rsp->timestsa);
+
+       if (!mcap_err && !valid_btclock(btclock))
+               mcap_err = MCAP_ERROR_INVALID_ARGS;
+
+       cb(mcl, mcap_err, btclock, timestamp, accuracy, NULL, user_data);
+}
+
+static void proc_sync_info_ind(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       mcap_md_sync_info_ind *req;
+       struct sync_info_ind_data data;
+       uint32_t btclock;
+
+       if (!mcl->csp->ind_expected) {
+               DBG("CSP: received unexpected info indication");
+               return;
+       }
+
+       if (len != sizeof(mcap_md_sync_info_ind))
+               return;
+
+       req = (mcap_md_sync_info_ind *) cmd;
+
+       btclock = ntohl(req->btclock);
+
+       if (!valid_btclock(btclock))
+               return;
+
+       data.btclock = btclock;
+       data.timestamp = ntoh64(req->timestst);
+       data.accuracy = ntohs(req->timestsa);
+
+       if (mcl->mi->mcl_sync_infoind_cb)
+               mcl->mi->mcl_sync_infoind_cb(mcl, &data);
+}
+
+void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len)
+{
+       if (!mcl->mi->csp_enabled || !mcl->csp) {
+               switch (cmd[0]) {
+               case MCAP_MD_SYNC_CAP_REQ:
+                       send_unsupported_cap_req(mcl);
+                       break;
+               case MCAP_MD_SYNC_SET_REQ:
+                       send_unsupported_set_req(mcl);
+                       break;
+               }
+               return;
+       }
+
+       switch (cmd[0]) {
+       case MCAP_MD_SYNC_CAP_REQ:
+               proc_sync_cap_req(mcl, cmd, len);
+               break;
+       case MCAP_MD_SYNC_CAP_RSP:
+               proc_sync_cap_rsp(mcl, cmd, len);
+               break;
+       case MCAP_MD_SYNC_SET_REQ:
+               proc_sync_set_req(mcl, cmd, len);
+               break;
+       case MCAP_MD_SYNC_SET_RSP:
+               proc_sync_set_rsp(mcl, cmd, len);
+               break;
+       case MCAP_MD_SYNC_INFO_IND:
+               proc_sync_info_ind(mcl, cmd, len);
+               break;
+       }
+}
+
+void mcap_sync_cap_req(struct mcap_mcl *mcl, uint16_t reqacc,
+                       mcap_sync_cap_cb cb, gpointer user_data,
+                       GError **err)
+{
+       struct mcap_sync_cap_cbdata *cbdata;
+       mcap_md_sync_cap_req *cmd;
+
+       if (!mcl->mi->csp_enabled || !mcl->csp) {
+               g_set_error(err,
+                       MCAP_CSP_ERROR,
+                       MCAP_ERROR_RESOURCE_UNAVAILABLE,
+                       "CSP not enabled for the instance");
+               return;
+       }
+
+       if (mcl->csp->csp_req) {
+               g_set_error(err,
+                       MCAP_CSP_ERROR,
+                       MCAP_ERROR_RESOURCE_UNAVAILABLE,
+                       "Pending CSP request");
+               return;
+       }
+
+       mcl->csp->csp_req = MCAP_MD_SYNC_CAP_REQ;
+       cmd = g_new0(mcap_md_sync_cap_req, 1);
+
+       cmd->op = MCAP_MD_SYNC_CAP_REQ;
+       cmd->timest = htons(reqacc);
+
+       cbdata = g_new0(struct mcap_sync_cap_cbdata, 1);
+       cbdata->cb = cb;
+       cbdata->user_data = user_data;
+       mcl->csp->csp_priv_data = cbdata;
+
+       send_sync_cmd(mcl, cmd, sizeof(*cmd));
+
+       g_free(cmd);
+}
+
+void mcap_sync_set_req(struct mcap_mcl *mcl, uint8_t update, uint32_t btclock,
+                       uint64_t timestamp, mcap_sync_set_cb cb,
+                       gpointer user_data, GError **err)
+{
+       mcap_md_sync_set_req *cmd;
+       struct mcap_sync_set_cbdata *cbdata;
+
+       if (!mcl->mi->csp_enabled || !mcl->csp) {
+               g_set_error(err,
+                       MCAP_CSP_ERROR,
+                       MCAP_ERROR_RESOURCE_UNAVAILABLE,
+                       "CSP not enabled for the instance");
+               return;
+       }
+
+       if (!mcl->csp->local_caps) {
+               g_set_error(err,
+                       MCAP_CSP_ERROR,
+                       MCAP_ERROR_RESOURCE_UNAVAILABLE,
+                       "Did not get CSP caps from slave yet");
+               return;
+       }
+
+       if (mcl->csp->csp_req) {
+               g_set_error(err,
+                       MCAP_CSP_ERROR,
+                       MCAP_ERROR_RESOURCE_UNAVAILABLE,
+                       "Pending CSP request");
+               return;
+       }
+
+       mcl->csp->csp_req = MCAP_MD_SYNC_SET_REQ;
+       cmd = g_new0(mcap_md_sync_set_req, 1);
+
+       cmd->op = MCAP_MD_SYNC_SET_REQ;
+       cmd->timestui = update;
+       cmd->btclock = htonl(btclock);
+       cmd->timestst = hton64(timestamp);
+
+       mcl->csp->ind_expected = update;
+
+       cbdata = g_new0(struct mcap_sync_set_cbdata, 1);
+       cbdata->cb = cb;
+       cbdata->user_data = user_data;
+       mcl->csp->csp_priv_data = cbdata;
+
+       send_sync_cmd(mcl, cmd, sizeof(*cmd));
+
+       g_free(cmd);
+}
+
+void mcap_enable_csp(struct mcap_instance *mi)
+{
+       mi->csp_enabled = TRUE;
+}
+
+void mcap_disable_csp(struct mcap_instance *mi)
+{
+       mi->csp_enabled = FALSE;
+}
diff --git a/input/device.c b/input/device.c
new file mode 100644 (file)
index 0000000..0e3f4a9
--- /dev/null
@@ -0,0 +1,1308 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hidp.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "uinput.h"
+
+#include "../src/adapter.h"
+#include "../src/device.h"
+#include "../src/storage.h"
+#include "../src/manager.h"
+#include "../src/dbus-common.h"
+
+#include "device.h"
+#include "error.h"
+#include "fakehid.h"
+#include "btio.h"
+
+#include "sdp-client.h"
+
+#define INPUT_DEVICE_INTERFACE "org.bluez.Input"
+
+#define BUF_SIZE               16
+
+#define UPDOWN_ENABLED         1
+
+#define FI_FLAG_CONNECTED      1
+
+struct input_conn {
+       struct fake_input       *fake;
+       DBusMessage             *pending_connect;
+       char                    *uuid;
+       GIOChannel              *ctrl_io;
+       GIOChannel              *intr_io;
+       guint                   ctrl_watch;
+       guint                   intr_watch;
+       guint                   sec_watch;
+       int                     timeout;
+       struct hidp_connadd_req *req;
+       struct input_device     *idev;
+};
+
+struct input_device {
+       DBusConnection          *conn;
+       char                    *path;
+       bdaddr_t                src;
+       bdaddr_t                dst;
+       uint32_t                handle;
+       guint                   dc_id;
+       gboolean                disable_sdp;
+       char                    *name;
+       struct btd_device       *device;
+       GSList                  *connections;
+};
+
+static GSList *devices = NULL;
+
+static struct input_device *find_device_by_path(GSList *list, const char *path)
+{
+       for (; list; list = list->next) {
+               struct input_device *idev = list->data;
+
+               if (!strcmp(idev->path, path))
+                       return idev;
+       }
+
+       return NULL;
+}
+
+static struct input_conn *find_connection(GSList *list, const char *pattern)
+{
+       for (; list; list = list->next) {
+               struct input_conn *iconn = list->data;
+
+               if (!strcasecmp(iconn->uuid, pattern))
+                       return iconn;
+       }
+
+       return NULL;
+}
+
+static void input_conn_free(struct input_conn *iconn)
+{
+       if (iconn->pending_connect)
+               dbus_message_unref(iconn->pending_connect);
+
+       if (iconn->ctrl_watch)
+               g_source_remove(iconn->ctrl_watch);
+
+       if (iconn->intr_watch)
+               g_source_remove(iconn->intr_watch);
+
+       if (iconn->sec_watch)
+               g_source_remove(iconn->sec_watch);
+
+       if (iconn->intr_io)
+               g_io_channel_unref(iconn->intr_io);
+
+       if (iconn->ctrl_io)
+               g_io_channel_unref(iconn->ctrl_io);
+
+       g_free(iconn->uuid);
+       g_free(iconn->fake);
+       g_free(iconn);
+}
+
+static void input_device_free(struct input_device *idev)
+{
+       if (idev->dc_id)
+               device_remove_disconnect_watch(idev->device, idev->dc_id);
+
+       dbus_connection_unref(idev->conn);
+       btd_device_unref(idev->device);
+       g_free(idev->name);
+       g_free(idev->path);
+       g_free(idev);
+}
+
+static int uinput_create(char *name)
+{
+       struct uinput_dev dev;
+       int fd, err;
+
+       fd = open("/dev/uinput", O_RDWR);
+       if (fd < 0) {
+               fd = open("/dev/input/uinput", O_RDWR);
+               if (fd < 0) {
+                       fd = open("/dev/misc/uinput", O_RDWR);
+                       if (fd < 0) {
+                               err = -errno;
+                               error("Can't open input device: %s (%d)",
+                                                       strerror(-err), -err);
+                               return err;
+                       }
+               }
+       }
+
+       memset(&dev, 0, sizeof(dev));
+       if (name)
+               strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+       dev.id.bustype = BUS_BLUETOOTH;
+       dev.id.vendor  = 0x0000;
+       dev.id.product = 0x0000;
+       dev.id.version = 0x0000;
+
+       if (write(fd, &dev, sizeof(dev)) < 0) {
+               err = -errno;
+               error("Can't write device information: %s (%d)",
+                                               strerror(-err), -err);
+               close(fd);
+               return err;
+       }
+
+       ioctl(fd, UI_SET_EVBIT, EV_KEY);
+       ioctl(fd, UI_SET_EVBIT, EV_REL);
+       ioctl(fd, UI_SET_EVBIT, EV_REP);
+
+       ioctl(fd, UI_SET_KEYBIT, KEY_UP);
+       ioctl(fd, UI_SET_KEYBIT, KEY_PAGEUP);
+       ioctl(fd, UI_SET_KEYBIT, KEY_DOWN);
+       ioctl(fd, UI_SET_KEYBIT, KEY_PAGEDOWN);
+
+       if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+               err = -errno;
+               error("Can't create uinput device: %s (%d)",
+                                               strerror(-err), -err);
+               close(fd);
+               return err;
+       }
+
+       return fd;
+}
+
+static int decode_key(const char *str)
+{
+       static int mode = UPDOWN_ENABLED, gain = 0;
+
+       uint16_t key;
+       int new_gain;
+
+       /* Switch from key up/down to page up/down */
+       if (strncmp("AT+CKPD=200", str, 11) == 0) {
+               mode = ~mode;
+               return KEY_RESERVED;
+       }
+
+       if (strncmp("AT+VG", str, 5))
+               return KEY_RESERVED;
+
+       /* Gain key pressed */
+       if (strlen(str) != 10)
+               return KEY_RESERVED;
+
+       new_gain = strtol(&str[7], NULL, 10);
+       if (new_gain <= gain)
+               key = (mode == UPDOWN_ENABLED ? KEY_UP : KEY_PAGEUP);
+       else
+               key = (mode == UPDOWN_ENABLED ? KEY_DOWN : KEY_PAGEDOWN);
+
+       gain = new_gain;
+
+       return key;
+}
+
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+       struct uinput_event event;
+
+       memset(&event, 0, sizeof(event));
+       event.type      = type;
+       event.code      = code;
+       event.value     = value;
+
+       return write(fd, &event, sizeof(event));
+}
+
+static void send_key(int fd, uint16_t key)
+{
+       /* Key press */
+       send_event(fd, EV_KEY, key, 1);
+       send_event(fd, EV_SYN, SYN_REPORT, 0);
+       /* Key release */
+       send_event(fd, EV_KEY, key, 0);
+       send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct fake_input *fake = data;
+       const char *ok = "\r\nOK\r\n";
+       char buf[BUF_SIZE];
+       ssize_t bread = 0, bwritten;
+       uint16_t key;
+       int fd;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       if (cond & (G_IO_HUP | G_IO_ERR)) {
+               error("Hangup or error on rfcomm server socket");
+               goto failed;
+       }
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       memset(buf, 0, BUF_SIZE);
+       bread = read(fd, buf, sizeof(buf) - 1);
+       if (bread < 0) {
+               error("IO Channel read error");
+               goto failed;
+       }
+
+       DBG("Received: %s", buf);
+
+       bwritten = write(fd, ok, 6);
+       if (bwritten < 0) {
+               error("IO Channel write error");
+               goto failed;
+       }
+
+       key = decode_key(buf);
+       if (key != KEY_RESERVED)
+               send_key(fake->uinput, key);
+
+       return TRUE;
+
+failed:
+       ioctl(fake->uinput, UI_DEV_DESTROY);
+       close(fake->uinput);
+       fake->uinput = -1;
+       g_io_channel_unref(fake->io);
+
+       return FALSE;
+}
+
+static void rfcomm_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+       struct input_conn *iconn = user_data;
+       struct input_device *idev = iconn->idev;
+       struct fake_input *fake = iconn->fake;
+       DBusMessage *reply;
+
+       if (err) {
+               reply = btd_error_failed(iconn->pending_connect, err->message);
+               goto failed;
+       }
+
+       fake->rfcomm = g_io_channel_unix_get_fd(chan);
+
+       /*
+        * FIXME: Some headsets required a sco connection
+        * first to report volume gain key events
+        */
+       fake->uinput = uinput_create(idev->name);
+       if (fake->uinput < 0) {
+               int err = fake->uinput;
+
+               g_io_channel_shutdown(chan, TRUE, NULL);
+               reply = btd_error_failed(iconn->pending_connect,
+                                                       strerror(-err));
+               goto failed;
+       }
+
+       fake->io = g_io_channel_unix_new(fake->rfcomm);
+       g_io_channel_set_close_on_unref(fake->io, TRUE);
+       g_io_add_watch(fake->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                               (GIOFunc) rfcomm_io_cb, fake);
+
+       /* Replying to the requestor */
+       reply = dbus_message_new_method_return(iconn->pending_connect);
+       g_dbus_send_message(idev->conn, reply);
+
+       dbus_message_unref(iconn->pending_connect);
+       iconn->pending_connect = NULL;
+
+       return;
+
+failed:
+       g_dbus_send_message(idev->conn, reply);
+       dbus_message_unref(iconn->pending_connect);
+       iconn->pending_connect = NULL;
+}
+
+static gboolean rfcomm_connect(struct input_conn *iconn, GError **err)
+{
+       struct input_device *idev = iconn->idev;
+       GIOChannel *io;
+
+       io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, iconn,
+                               NULL, err,
+                               BT_IO_OPT_SOURCE_BDADDR, &idev->src,
+                               BT_IO_OPT_DEST_BDADDR, &idev->dst,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_INVALID);
+       if (!io)
+               return FALSE;
+
+       g_io_channel_unref(io);
+
+       return TRUE;
+}
+
+static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct input_conn *iconn = data;
+       struct input_device *idev = iconn->idev;
+       gboolean connected = FALSE;
+
+       /* Checking for ctrl_watch avoids a double g_io_channel_shutdown since
+        * it's likely that ctrl_watch_cb has been queued for dispatching in
+        * this mainloop iteration */
+       if ((cond & (G_IO_HUP | G_IO_ERR)) && iconn->ctrl_watch)
+               g_io_channel_shutdown(chan, TRUE, NULL);
+
+       emit_property_changed(idev->conn, idev->path, INPUT_DEVICE_INTERFACE,
+                               "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+       device_remove_disconnect_watch(idev->device, idev->dc_id);
+       idev->dc_id = 0;
+
+       iconn->intr_watch = 0;
+
+       g_io_channel_unref(iconn->intr_io);
+       iconn->intr_io = NULL;
+
+       /* Close control channel */
+       if (iconn->ctrl_io && !(cond & G_IO_NVAL))
+               g_io_channel_shutdown(iconn->ctrl_io, TRUE, NULL);
+
+       return FALSE;
+}
+
+static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct input_conn *iconn = data;
+
+       /* Checking for intr_watch avoids a double g_io_channel_shutdown since
+        * it's likely that intr_watch_cb has been queued for dispatching in
+        * this mainloop iteration */
+       if ((cond & (G_IO_HUP | G_IO_ERR)) && iconn->intr_watch)
+               g_io_channel_shutdown(chan, TRUE, NULL);
+
+       iconn->ctrl_watch = 0;
+
+       g_io_channel_unref(iconn->ctrl_io);
+       iconn->ctrl_io = NULL;
+
+       /* Close interrupt channel */
+       if (iconn->intr_io && !(cond & G_IO_NVAL))
+               g_io_channel_shutdown(iconn->intr_io, TRUE, NULL);
+
+       return FALSE;
+}
+
+static gboolean fake_hid_connect(struct input_conn *iconn, GError **err)
+{
+       struct fake_hid *fhid = iconn->fake->priv;
+
+       return fhid->connect(iconn->fake, err);
+}
+
+static int fake_hid_disconnect(struct input_conn *iconn)
+{
+       struct fake_hid *fhid = iconn->fake->priv;
+
+       return fhid->disconnect(iconn->fake);
+}
+
+static void epox_endian_quirk(unsigned char *data, int size)
+{
+       /* USAGE_PAGE (Keyboard)        05 07
+        * USAGE_MINIMUM (0)            19 00
+        * USAGE_MAXIMUM (65280)        2A 00 FF   <= must be FF 00
+        * LOGICAL_MINIMUM (0)          15 00
+        * LOGICAL_MAXIMUM (65280)      26 00 FF   <= must be FF 00
+        */
+       unsigned char pattern[] = { 0x05, 0x07, 0x19, 0x00, 0x2a, 0x00, 0xff,
+                                               0x15, 0x00, 0x26, 0x00, 0xff };
+       unsigned int i;
+
+       if (!data)
+               return;
+
+       for (i = 0; i < size - sizeof(pattern); i++) {
+               if (!memcmp(data + i, pattern, sizeof(pattern))) {
+                       data[i + 5] = 0xff;
+                       data[i + 6] = 0x00;
+                       data[i + 10] = 0xff;
+                       data[i + 11] = 0x00;
+               }
+       }
+}
+
+static void extract_hid_record(sdp_record_t *rec, struct hidp_connadd_req *req)
+{
+       sdp_data_t *pdlist, *pdlist2;
+       uint8_t attr_val;
+
+       pdlist = sdp_data_get(rec, 0x0101);
+       pdlist2 = sdp_data_get(rec, 0x0102);
+       if (pdlist) {
+               if (pdlist2) {
+                       if (strncmp(pdlist->val.str, pdlist2->val.str, 5)) {
+                               strncpy(req->name, pdlist2->val.str, 127);
+                               strcat(req->name, " ");
+                       }
+                       strncat(req->name, pdlist->val.str, 127 - strlen(req->name));
+               } else
+                       strncpy(req->name, pdlist->val.str, 127);
+       } else {
+               pdlist2 = sdp_data_get(rec, 0x0100);
+               if (pdlist2)
+                       strncpy(req->name, pdlist2->val.str, 127);
+       }
+
+       pdlist = sdp_data_get(rec, SDP_ATTR_HID_PARSER_VERSION);
+       req->parser = pdlist ? pdlist->val.uint16 : 0x0100;
+
+       pdlist = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS);
+       req->subclass = pdlist ? pdlist->val.uint8 : 0;
+
+       pdlist = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE);
+       req->country = pdlist ? pdlist->val.uint8 : 0;
+
+       pdlist = sdp_data_get(rec, SDP_ATTR_HID_VIRTUAL_CABLE);
+       attr_val = pdlist ? pdlist->val.uint8 : 0;
+       if (attr_val)
+               req->flags |= (1 << HIDP_VIRTUAL_CABLE_UNPLUG);
+
+       pdlist = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE);
+       attr_val = pdlist ? pdlist->val.uint8 : 0;
+       if (attr_val)
+               req->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
+
+       pdlist = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST);
+       if (pdlist) {
+               pdlist = pdlist->val.dataseq;
+               pdlist = pdlist->val.dataseq;
+               pdlist = pdlist->next;
+
+               req->rd_data = g_try_malloc0(pdlist->unitSize);
+               if (req->rd_data) {
+                       memcpy(req->rd_data, (unsigned char *) pdlist->val.str,
+                                                               pdlist->unitSize);
+                       req->rd_size = pdlist->unitSize;
+                       epox_endian_quirk(req->rd_data, req->rd_size);
+               }
+       }
+}
+
+static int ioctl_connadd(struct hidp_connadd_req *req)
+{
+       int ctl, err = 0;
+
+       ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+       if (ctl < 0)
+               return -errno;
+
+       if (ioctl(ctl, HIDPCONNADD, req) < 0)
+               err = -errno;
+
+       close(ctl);
+
+       return err;
+}
+
+static void encrypt_completed(uint8_t status, gpointer user_data)
+{
+       struct hidp_connadd_req *req = user_data;
+       int err;
+
+       if (status) {
+               error("Encryption failed: %s(0x%x)",
+                               strerror(bt_error(status)), status);
+               goto failed;
+       }
+
+       err = ioctl_connadd(req);
+       if (err == 0)
+               goto cleanup;
+
+       error("ioctl_connadd(): %s(%d)", strerror(-err), -err);
+failed:
+       close(req->intr_sock);
+       close(req->ctrl_sock);
+
+cleanup:
+       free(req->rd_data);
+
+       g_free(req);
+}
+
+static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition,
+                                                               gpointer data)
+{
+       struct input_conn *iconn = data;
+       struct hidp_connadd_req *req = iconn->req;
+
+       DBG(" ");
+
+       encrypt_completed(0, req);
+
+       iconn->sec_watch = 0;
+       iconn->req = NULL;
+
+       return FALSE;
+}
+
+static int hidp_add_connection(const struct input_device *idev,
+                                       struct input_conn *iconn)
+{
+       struct hidp_connadd_req *req;
+       struct fake_hid *fake_hid;
+       struct fake_input *fake;
+       sdp_record_t *rec;
+       char src_addr[18], dst_addr[18];
+       GError *gerr = NULL;
+       int err;
+
+       req = g_new0(struct hidp_connadd_req, 1);
+       req->ctrl_sock = g_io_channel_unix_get_fd(iconn->ctrl_io);
+       req->intr_sock = g_io_channel_unix_get_fd(iconn->intr_io);
+       req->flags     = 0;
+       req->idle_to   = iconn->timeout;
+
+       ba2str(&idev->src, src_addr);
+       ba2str(&idev->dst, dst_addr);
+
+       rec = fetch_record(src_addr, dst_addr, idev->handle);
+       if (!rec) {
+               error("Rejected connection from unknown device %s", dst_addr);
+               err = -EPERM;
+               goto cleanup;
+       }
+
+       extract_hid_record(rec, req);
+       sdp_record_free(rec);
+
+       req->vendor = btd_device_get_vendor(idev->device);
+       req->product = btd_device_get_product(idev->device);
+       req->version = btd_device_get_version(idev->device);
+
+       fake_hid = get_fake_hid(req->vendor, req->product);
+       if (fake_hid) {
+               err = 0;
+               fake = g_new0(struct fake_input, 1);
+               fake->connect = fake_hid_connect;
+               fake->disconnect = fake_hid_disconnect;
+               fake->priv = fake_hid;
+               fake->idev = idev;
+               fake = fake_hid_connadd(fake, iconn->intr_io, fake_hid);
+               if (fake == NULL)
+                       err = -ENOMEM;
+               else
+                       fake->flags |= FI_FLAG_CONNECTED;
+               goto cleanup;
+       }
+
+       if (idev->name)
+               strncpy(req->name, idev->name, sizeof(req->name) - 1);
+
+       /* Encryption is mandatory for keyboards */
+       if (req->subclass & 0x40) {
+               struct btd_adapter *adapter = device_get_adapter(idev->device);
+
+               err = btd_adapter_encrypt_link(adapter, (bdaddr_t *) &idev->dst,
+                                               encrypt_completed, req);
+               if (err == 0) {
+                       /* Waiting async encryption */
+                       return 0;
+               }
+
+               if (err == -ENOSYS)
+                       goto nosys;
+
+               if (err != -EALREADY) {
+                       error("encrypt_link: %s (%d)", strerror(-err), -err);
+                       goto cleanup;
+               }
+       }
+
+       err = ioctl_connadd(req);
+
+cleanup:
+       free(req->rd_data);
+       g_free(req);
+
+       return err;
+
+nosys:
+       if (!bt_io_set(iconn->intr_io, BT_IO_L2CAP, &gerr,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_INVALID)) {
+               error("btio: %s", gerr->message);
+               g_error_free(gerr);
+               goto cleanup;
+       }
+
+       iconn->req = req;
+       iconn->sec_watch = g_io_add_watch(iconn->intr_io, G_IO_OUT,
+                                                       encrypt_notify, iconn);
+       return 0;
+}
+
+static int is_connected(struct input_conn *iconn)
+{
+       struct input_device *idev = iconn->idev;
+       struct fake_input *fake = iconn->fake;
+       struct hidp_conninfo ci;
+       int ctl;
+
+       /* Fake input */
+       if (fake)
+               return fake->flags & FI_FLAG_CONNECTED;
+
+       /* Standard HID */
+       ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+       if (ctl < 0)
+               return 0;
+
+       memset(&ci, 0, sizeof(ci));
+       bacpy(&ci.bdaddr, &idev->dst);
+       if (ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) {
+               close(ctl);
+               return 0;
+       }
+
+       close(ctl);
+
+       if (ci.state != BT_CONNECTED)
+               return 0;
+       else
+               return 1;
+}
+
+static int connection_disconnect(struct input_conn *iconn, uint32_t flags)
+{
+       struct input_device *idev = iconn->idev;
+       struct fake_input *fake = iconn->fake;
+       struct hidp_conndel_req req;
+       struct hidp_conninfo ci;
+       int ctl, err = 0;
+
+       /* Fake input disconnect */
+       if (fake) {
+               err = fake->disconnect(iconn);
+               if (err == 0)
+                       fake->flags &= ~FI_FLAG_CONNECTED;
+               return err;
+       }
+
+       /* Standard HID disconnect */
+       if (iconn->intr_io)
+               g_io_channel_shutdown(iconn->intr_io, TRUE, NULL);
+       if (iconn->ctrl_io)
+               g_io_channel_shutdown(iconn->ctrl_io, TRUE, NULL);
+
+       ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+       if (ctl < 0) {
+               error("Can't open HIDP control socket");
+               return -errno;
+       }
+
+       memset(&ci, 0, sizeof(ci));
+       bacpy(&ci.bdaddr, &idev->dst);
+       if ((ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) ||
+                               (ci.state != BT_CONNECTED)) {
+               err = -ENOTCONN;
+               goto fail;
+       }
+
+       memset(&req, 0, sizeof(req));
+       bacpy(&req.bdaddr, &idev->dst);
+       req.flags = flags;
+       if (ioctl(ctl, HIDPCONNDEL, &req) < 0) {
+               err = -errno;
+               error("Can't delete the HID device: %s(%d)",
+                               strerror(-err), -err);
+               goto fail;
+       }
+
+fail:
+       close(ctl);
+
+       return err;
+}
+
+static int disconnect(struct input_device *idev, uint32_t flags)
+{
+       struct input_conn *iconn = NULL;
+       GSList *l;
+
+       for (l = idev->connections; l; l = l->next) {
+               iconn = l->data;
+
+               if (is_connected(iconn))
+                       break;
+       }
+
+       if (!iconn)
+               return -ENOTCONN;
+
+       return connection_disconnect(iconn, flags);
+}
+
+static void disconnect_cb(struct btd_device *device, gboolean removal,
+                               void *user_data)
+{
+       struct input_device *idev = user_data;
+       int flags;
+
+       info("Input: disconnect %s", idev->path);
+
+       flags = removal ? (1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0;
+
+       disconnect(idev, flags);
+}
+
+static int input_device_connected(struct input_device *idev,
+                                               struct input_conn *iconn)
+{
+       dbus_bool_t connected;
+       int err;
+
+       if (iconn->intr_io == NULL || iconn->ctrl_io == NULL)
+               return -ENOTCONN;
+
+       err = hidp_add_connection(idev, iconn);
+       if (err < 0)
+               return err;
+
+       iconn->intr_watch = g_io_add_watch(iconn->intr_io,
+                                       G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                       intr_watch_cb, iconn);
+       iconn->ctrl_watch = g_io_add_watch(iconn->ctrl_io,
+                                       G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                       ctrl_watch_cb, iconn);
+
+       connected = TRUE;
+       emit_property_changed(idev->conn, idev->path, INPUT_DEVICE_INTERFACE,
+                               "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+       idev->dc_id = device_add_disconnect_watch(idev->device, disconnect_cb,
+                                                       idev, NULL);
+
+       return 0;
+}
+
+static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
+                                                       gpointer user_data)
+{
+       struct input_conn *iconn = user_data;
+       struct input_device *idev = iconn->idev;
+       DBusMessage *reply;
+       int err;
+       const char *err_msg;
+
+       if (conn_err) {
+               err_msg = conn_err->message;
+               goto failed;
+       }
+
+       err = input_device_connected(idev, iconn);
+       if (err < 0) {
+               err_msg = strerror(-err);
+               goto failed;
+       }
+
+       /* Replying to the requestor */
+       g_dbus_send_reply(idev->conn, iconn->pending_connect, DBUS_TYPE_INVALID);
+
+       dbus_message_unref(iconn->pending_connect);
+       iconn->pending_connect = NULL;
+
+       return;
+
+failed:
+       error("%s", err_msg);
+       reply = btd_error_failed(iconn->pending_connect, err_msg);
+       g_dbus_send_message(idev->conn, reply);
+
+       dbus_message_unref(iconn->pending_connect);
+       iconn->pending_connect = NULL;
+
+       /* So we guarantee the interrupt channel is closed before the
+        * control channel (if we only do unref GLib will close it only
+        * after returning control to the mainloop */
+       if (!conn_err)
+               g_io_channel_shutdown(iconn->intr_io, FALSE, NULL);
+
+       g_io_channel_unref(iconn->intr_io);
+       iconn->intr_io = NULL;
+
+       if (iconn->ctrl_io) {
+               g_io_channel_unref(iconn->ctrl_io);
+               iconn->ctrl_io = NULL;
+       }
+}
+
+static void control_connect_cb(GIOChannel *chan, GError *conn_err,
+                                                       gpointer user_data)
+{
+       struct input_conn *iconn = user_data;
+       struct input_device *idev = iconn->idev;
+       DBusMessage *reply;
+       GIOChannel *io;
+       GError *err = NULL;
+
+       if (conn_err) {
+               error("%s", conn_err->message);
+               reply = btd_error_failed(iconn->pending_connect,
+                                               conn_err->message);
+               goto failed;
+       }
+
+       /* Connect to the HID interrupt channel */
+       io = bt_io_connect(BT_IO_L2CAP, interrupt_connect_cb, iconn,
+                               NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &idev->src,
+                               BT_IO_OPT_DEST_BDADDR, &idev->dst,
+                               BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("%s", err->message);
+               reply = btd_error_failed(iconn->pending_connect,
+                                                       err->message);
+               g_error_free(err);
+               goto failed;
+       }
+
+       iconn->intr_io = io;
+
+       return;
+
+failed:
+       g_io_channel_unref(iconn->ctrl_io);
+       iconn->ctrl_io = NULL;
+       g_dbus_send_message(idev->conn, reply);
+       dbus_message_unref(iconn->pending_connect);
+       iconn->pending_connect = NULL;
+}
+
+static int fake_disconnect(struct input_conn *iconn)
+{
+       struct fake_input *fake = iconn->fake;
+
+       if (!fake->io)
+               return -ENOTCONN;
+
+       g_io_channel_shutdown(fake->io, TRUE, NULL);
+       g_io_channel_unref(fake->io);
+       fake->io = NULL;
+
+       if (fake->uinput >= 0) {
+               ioctl(fake->uinput, UI_DEV_DESTROY);
+               close(fake->uinput);
+               fake->uinput = -1;
+       }
+
+       return 0;
+}
+
+/*
+ * Input Device methods
+ */
+static DBusMessage *input_device_connect(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct input_device *idev = data;
+       struct input_conn *iconn;
+       struct fake_input *fake;
+       DBusMessage *reply;
+       GError *err = NULL;
+
+       iconn = find_connection(idev->connections, HID_UUID);
+       if (!iconn)
+               return btd_error_not_supported(msg);
+
+       if (iconn->pending_connect)
+               return btd_error_in_progress(msg);
+
+       if (is_connected(iconn))
+               return btd_error_already_connected(msg);
+
+       iconn->pending_connect = dbus_message_ref(msg);
+       fake = iconn->fake;
+
+       if (fake) {
+               /* Fake input device */
+               if (fake->connect(iconn, &err))
+                       fake->flags |= FI_FLAG_CONNECTED;
+       } else {
+               /* HID devices */
+               GIOChannel *io;
+
+               if (idev->disable_sdp)
+                       bt_clear_cached_session(&idev->src, &idev->dst);
+
+               io = bt_io_connect(BT_IO_L2CAP, control_connect_cb, iconn,
+                                       NULL, &err,
+                                       BT_IO_OPT_SOURCE_BDADDR, &idev->src,
+                                       BT_IO_OPT_DEST_BDADDR, &idev->dst,
+                                       BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+                                       BT_IO_OPT_INVALID);
+               iconn->ctrl_io = io;
+       }
+
+       if (err == NULL)
+               return NULL;
+
+       error("%s", err->message);
+       dbus_message_unref(iconn->pending_connect);
+       iconn->pending_connect = NULL;
+       reply = btd_error_failed(msg, err->message);
+       g_error_free(err);
+       return reply;
+}
+
+static DBusMessage *input_device_disconnect(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct input_device *idev = data;
+       int err;
+
+       err = disconnect(idev, 0);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static void device_unregister(void *data)
+{
+       struct input_device *idev = data;
+
+       DBG("Unregistered interface %s on path %s", INPUT_DEVICE_INTERFACE,
+                                                               idev->path);
+
+       devices = g_slist_remove(devices, idev);
+       input_device_free(idev);
+}
+
+static gint connected_cmp(gpointer a, gpointer b)
+{
+       struct input_conn *iconn = a;
+
+       return !is_connected(iconn);
+}
+
+static DBusMessage *input_device_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct input_device *idev = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       dbus_bool_t connected;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       /* Connected */
+       connected = !!g_slist_find_custom(idev->connections, NULL,
+                                       (GCompareFunc) connected_cmp);
+       dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static const GDBusMethodTable device_methods[] = {
+       { GDBUS_ASYNC_METHOD("Connect",
+                               NULL, NULL, input_device_connect) },
+       { GDBUS_METHOD("Disconnect",
+                               NULL, NULL, input_device_disconnect) },
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       input_device_get_properties) },
+       { }
+};
+
+static const GDBusSignalTable device_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static struct input_device *input_device_new(DBusConnection *conn,
+                               struct btd_device *device, const char *path,
+                               const uint32_t handle, gboolean disable_sdp)
+{
+       struct btd_adapter *adapter = device_get_adapter(device);
+       struct input_device *idev;
+       char name[249], src_addr[18], dst_addr[18];
+
+       idev = g_new0(struct input_device, 1);
+       adapter_get_address(adapter, &idev->src);
+       device_get_address(device, &idev->dst, NULL);
+       idev->device = btd_device_ref(device);
+       idev->path = g_strdup(path);
+       idev->conn = dbus_connection_ref(conn);
+       idev->handle = handle;
+       idev->disable_sdp = disable_sdp;
+
+       ba2str(&idev->src, src_addr);
+       ba2str(&idev->dst, dst_addr);
+       if (read_device_name(src_addr, dst_addr, name) == 0)
+               idev->name = g_strdup(name);
+
+       if (g_dbus_register_interface(conn, idev->path, INPUT_DEVICE_INTERFACE,
+                                       device_methods, device_signals, NULL,
+                                       idev, device_unregister) == FALSE) {
+               error("Failed to register interface %s on path %s",
+                       INPUT_DEVICE_INTERFACE, path);
+               input_device_free(idev);
+               return NULL;
+       }
+
+       DBG("Registered interface %s on path %s",
+                       INPUT_DEVICE_INTERFACE, idev->path);
+
+       return idev;
+}
+
+static struct input_conn *input_conn_new(struct input_device *idev,
+                                       const char *uuid, int timeout)
+{
+       struct input_conn *iconn;
+
+       iconn = g_new0(struct input_conn, 1);
+       iconn->timeout = timeout;
+       iconn->uuid = g_strdup(uuid);
+       iconn->idev = idev;
+
+       return iconn;
+}
+
+static gboolean is_device_sdp_disable(const sdp_record_t *rec)
+{
+       sdp_data_t *data;
+
+       data = sdp_data_get(rec, SDP_ATTR_HID_SDP_DISABLE);
+
+       return data && data->val.uint8;
+}
+
+int input_device_register(DBusConnection *conn, struct btd_device *device,
+                                       const char *path, const char *uuid,
+                                       const sdp_record_t *rec, int timeout)
+{
+       struct input_device *idev;
+       struct input_conn *iconn;
+
+       idev = find_device_by_path(devices, path);
+       if (!idev) {
+               idev = input_device_new(conn, device, path, rec->handle,
+                                       is_device_sdp_disable(rec));
+               if (!idev)
+                       return -EINVAL;
+               devices = g_slist_append(devices, idev);
+       }
+
+       iconn = input_conn_new(idev, uuid, timeout);
+
+       idev->connections = g_slist_append(idev->connections, iconn);
+
+       return 0;
+}
+
+int fake_input_register(DBusConnection *conn, struct btd_device *device,
+                       const char *path, const char *uuid, uint8_t channel)
+{
+       struct input_device *idev;
+       struct input_conn *iconn;
+
+       idev = find_device_by_path(devices, path);
+       if (!idev) {
+               idev = input_device_new(conn, device, path, 0, FALSE);
+               if (!idev)
+                       return -EINVAL;
+               devices = g_slist_append(devices, idev);
+       }
+
+       iconn = input_conn_new(idev, uuid, 0);
+       iconn->fake = g_new0(struct fake_input, 1);
+       iconn->fake->ch = channel;
+       iconn->fake->connect = rfcomm_connect;
+       iconn->fake->disconnect = fake_disconnect;
+
+       idev->connections = g_slist_append(idev->connections, iconn);
+
+       return 0;
+}
+
+static struct input_device *find_device(const bdaddr_t *src,
+                                       const bdaddr_t *dst)
+{
+       GSList *list;
+
+       for (list = devices; list != NULL; list = list->next) {
+               struct input_device *idev = list->data;
+
+               if (!bacmp(&idev->src, src) && !bacmp(&idev->dst, dst))
+                       return idev;
+       }
+
+       return NULL;
+}
+
+int input_device_unregister(const char *path, const char *uuid)
+{
+       struct input_device *idev;
+       struct input_conn *iconn;
+
+       idev = find_device_by_path(devices, path);
+       if (idev == NULL)
+               return -EINVAL;
+
+       iconn = find_connection(idev->connections, uuid);
+       if (iconn == NULL)
+               return -EINVAL;
+
+       if (iconn->pending_connect) {
+               /* Pending connection running */
+               return -EBUSY;
+       }
+
+       idev->connections = g_slist_remove(idev->connections, iconn);
+       input_conn_free(iconn);
+       if (idev->connections)
+               return 0;
+
+       g_dbus_unregister_interface(idev->conn, path, INPUT_DEVICE_INTERFACE);
+
+       return 0;
+}
+
+static int input_device_connadd(struct input_device *idev,
+                               struct input_conn *iconn)
+{
+       int err;
+
+       err = input_device_connected(idev, iconn);
+       if (err < 0)
+               goto error;
+
+       return 0;
+
+error:
+       if (iconn->ctrl_io) {
+               g_io_channel_shutdown(iconn->ctrl_io, FALSE, NULL);
+               g_io_channel_unref(iconn->ctrl_io);
+               iconn->ctrl_io = NULL;
+       }
+       if (iconn->intr_io) {
+               g_io_channel_shutdown(iconn->intr_io, FALSE, NULL);
+               g_io_channel_unref(iconn->intr_io);
+               iconn->intr_io = NULL;
+       }
+
+       return err;
+}
+
+int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+                                                               GIOChannel *io)
+{
+       struct input_device *idev = find_device(src, dst);
+       struct input_conn *iconn;
+
+       if (!idev)
+               return -ENOENT;
+
+       iconn = find_connection(idev->connections, HID_UUID);
+       if (!iconn)
+               return -ENOENT;
+
+       switch (psm) {
+       case L2CAP_PSM_HIDP_CTRL:
+               if (iconn->ctrl_io)
+                       return -EALREADY;
+               iconn->ctrl_io = g_io_channel_ref(io);
+               break;
+       case L2CAP_PSM_HIDP_INTR:
+               if (iconn->intr_io)
+                       return -EALREADY;
+               iconn->intr_io = g_io_channel_ref(io);
+               break;
+       }
+
+       if (iconn->intr_io && iconn->ctrl_io)
+               input_device_connadd(idev, iconn);
+
+       return 0;
+}
+
+int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       struct input_device *idev = find_device(src, dst);
+       struct input_conn *iconn;
+
+       if (!idev)
+               return -ENOENT;
+
+       iconn = find_connection(idev->connections, HID_UUID);
+       if (!iconn)
+               return -ENOENT;
+
+       if (iconn->intr_io)
+               g_io_channel_shutdown(iconn->intr_io, TRUE, NULL);
+
+       if (iconn->ctrl_io)
+               g_io_channel_shutdown(iconn->ctrl_io, TRUE, NULL);
+
+       return 0;
+}
diff --git a/input/device.h b/input/device.h
new file mode 100644 (file)
index 0000000..509a353
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define L2CAP_PSM_HIDP_CTRL    0x11
+#define L2CAP_PSM_HIDP_INTR    0x13
+
+struct input_device;
+struct input_conn;
+
+struct fake_input {
+       int             flags;
+       GIOChannel      *io;
+       int             uinput;         /* uinput socket */
+       int             rfcomm;         /* RFCOMM socket */
+       uint8_t         ch;             /* RFCOMM channel number */
+       gboolean        (*connect) (struct input_conn *iconn, GError **err);
+       int             (*disconnect) (struct input_conn *iconn);
+       void            *priv;
+       const struct input_device *idev;
+};
+
+int fake_input_register(DBusConnection *conn, struct btd_device *device,
+                       const char *path, const char *uuid, uint8_t channel);
+int input_device_register(DBusConnection *conn, struct btd_device *device,
+                                       const char *path, const char *uuid,
+                                       const sdp_record_t *rec, int timeout);
+int input_device_unregister(const char *path, const char *uuid);
+
+int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+                                                       GIOChannel *io);
+int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst);
diff --git a/input/fakehid.c b/input/fakehid.c
new file mode 100644 (file)
index 0000000..3181538
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <glib.h>
+
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#include "log.h"
+#include "device.h"
+#include "fakehid.h"
+#include "uinput.h"
+
+enum ps3remote_special_keys {
+       PS3R_BIT_PS = 0,
+       PS3R_BIT_ENTER = 3,
+       PS3R_BIT_L2 = 8,
+       PS3R_BIT_R2 = 9,
+       PS3R_BIT_L1 = 10,
+       PS3R_BIT_R1 = 11,
+       PS3R_BIT_TRIANGLE = 12,
+       PS3R_BIT_CIRCLE = 13,
+       PS3R_BIT_CROSS = 14,
+       PS3R_BIT_SQUARE = 15,
+       PS3R_BIT_SELECT = 16,
+       PS3R_BIT_L3 = 17,
+       PS3R_BIT_R3 = 18,
+       PS3R_BIT_START = 19,
+       PS3R_BIT_UP = 20,
+       PS3R_BIT_RIGHT = 21,
+       PS3R_BIT_DOWN = 22,
+       PS3R_BIT_LEFT = 23,
+};
+
+static unsigned int ps3remote_bits[] = {
+       [PS3R_BIT_ENTER] = 0x0b,
+       [PS3R_BIT_PS] = 0x43,
+       [PS3R_BIT_SQUARE] = 0x5f,
+       [PS3R_BIT_CROSS] = 0x5e,
+       [PS3R_BIT_CIRCLE] = 0x5d,
+       [PS3R_BIT_TRIANGLE] = 0x5c,
+       [PS3R_BIT_R1] = 0x5b,
+       [PS3R_BIT_L1] = 0x5a,
+       [PS3R_BIT_R2] = 0x59,
+       [PS3R_BIT_L2] = 0x58,
+       [PS3R_BIT_LEFT] = 0x57,
+       [PS3R_BIT_DOWN] = 0x56,
+       [PS3R_BIT_RIGHT] = 0x55,
+       [PS3R_BIT_UP] = 0x54,
+       [PS3R_BIT_START] = 0x53,
+       [PS3R_BIT_R3] = 0x52,
+       [PS3R_BIT_L3] = 0x51,
+       [PS3R_BIT_SELECT] = 0x50,
+};
+
+static unsigned int ps3remote_keymap[] = {
+       [0x16] = KEY_EJECTCD,
+       [0x64] = KEY_AUDIO,
+       [0x65] = KEY_ANGLE,
+       [0x63] = KEY_SUBTITLE,
+       [0x0f] = KEY_CLEAR,
+       [0x28] = KEY_TIME,
+       [0x00] = KEY_1,
+       [0x01] = KEY_2,
+       [0x02] = KEY_3,
+       [0x03] = KEY_4,
+       [0x04] = KEY_5,
+       [0x05] = KEY_6,
+       [0x06] = KEY_7,
+       [0x07] = KEY_8,
+       [0x08] = KEY_9,
+       [0x09] = KEY_0,
+       [0x81] = KEY_RED,
+       [0x82] = KEY_GREEN,
+       [0x80] = KEY_BLUE,
+       [0x83] = KEY_YELLOW,
+       [0x70] = KEY_INFO,              /* display */
+       [0x1a] = KEY_MENU,              /* top menu */
+       [0x40] = KEY_CONTEXT_MENU,      /* pop up/menu */
+       [0x0e] = KEY_ESC,               /* return */
+       [0x5c] = KEY_OPTION,            /* options/triangle */
+       [0x5d] = KEY_BACK,              /* back/circle */
+       [0x5f] = KEY_SCREEN,            /* view/square */
+       [0x5e] = BTN_0,                 /* cross */
+       [0x54] = KEY_UP,
+       [0x56] = KEY_DOWN,
+       [0x57] = KEY_LEFT,
+       [0x55] = KEY_RIGHT,
+       [0x0b] = KEY_ENTER,
+       [0x5a] = BTN_TL,                /* L1 */
+       [0x58] = BTN_TL2,               /* L2 */
+       [0x51] = BTN_THUMBL,            /* L3 */
+       [0x5b] = BTN_TR,                /* R1 */
+       [0x59] = BTN_TR2,               /* R2 */
+       [0x52] = BTN_THUMBR,            /* R3 */
+       [0x43] = KEY_HOMEPAGE,          /* PS button */
+       [0x50] = KEY_SELECT,
+       [0x53] = BTN_START,
+       [0x33] = KEY_REWIND,            /* scan back */
+       [0x32] = KEY_PLAY,
+       [0x34] = KEY_FORWARD,           /* scan forward */
+       [0x30] = KEY_PREVIOUS,
+       [0x38] = KEY_STOP,
+       [0x31] = KEY_NEXT,
+       [0x60] = KEY_FRAMEBACK,         /* slow/step back */
+       [0x39] = KEY_PAUSE,
+       [0x61] = KEY_FRAMEFORWARD,      /* slow/step forward */
+       [0xff] = KEY_MAX,
+};
+
+static int ps3remote_decode(char *buff, int size, unsigned int *value)
+{
+       static unsigned int lastkey = 0;
+       static unsigned int lastmask = 0;
+       unsigned int i, mask;
+       int retval;
+       guint8 key;
+
+       if (size < 12) {
+               error("Got a shorter packet! (size %i)\n", size);
+               return KEY_RESERVED;
+       }
+
+       mask = (buff[2] << 16) + (buff[3] << 8) + buff[4];
+       key = buff[5];
+
+       /* first, check flags */
+       for (i = 0; i < 24; i++) {
+               if ((lastmask & (1 << i)) == (mask & (1 << i)))
+                       continue;
+               if (ps3remote_bits[i] == 0)
+                       goto error;
+               retval = ps3remote_keymap[ps3remote_bits[i]];
+               if (mask & (1 << i))
+                       /* key pressed */
+                       *value = 1;
+               else
+                       /* key released */
+                       *value = 0;
+
+               goto out;
+       }
+
+       *value = buff[11];
+       if (buff[11] == 1) {
+               retval = ps3remote_keymap[key];
+       } else
+               retval = lastkey;
+
+       if (retval == KEY_RESERVED)
+               goto error;
+       if (retval == KEY_MAX)
+               return retval;
+
+       lastkey = retval;
+
+out:
+       fflush(stdout);
+
+       lastmask = mask;
+
+       return retval;
+
+error:
+       error("ps3remote: unrecognized sequence [%#x][%#x][%#x][%#x] [%#x],"
+                       "last: [%#x][%#x][%#x][%#x]",
+                       buff[2], buff[3], buff[4], buff[5], buff[11],
+                               lastmask >> 16, lastmask >> 8 & 0xff,
+                                               lastmask & 0xff, lastkey);
+       return -1;
+}
+
+static gboolean ps3remote_event(GIOChannel *chan, GIOCondition cond,
+                               gpointer data)
+{
+       struct fake_input *fake = data;
+       struct uinput_event event;
+       unsigned int key, value = 0;
+       ssize_t size;
+       char buff[50];
+       int fd;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       if (cond & (G_IO_HUP | G_IO_ERR)) {
+               error("Hangup or error on rfcomm server socket");
+               goto failed;
+       }
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       memset(buff, 0, sizeof(buff));
+       size = read(fd, buff, sizeof(buff));
+       if (size < 0) {
+               error("IO Channel read error");
+               goto failed;
+       }
+
+       key = ps3remote_decode(buff, size, &value);
+       if (key == KEY_RESERVED) {
+               error("Got invalid key from decode");
+               goto failed;
+       } else if (key == KEY_MAX)
+               return TRUE;
+
+       memset(&event, 0, sizeof(event));
+       gettimeofday(&event.time, NULL);
+       event.type = EV_KEY;
+       event.code = key;
+       event.value = value;
+       if (write(fake->uinput, &event, sizeof(event)) != sizeof(event)) {
+               error("Error writing to uinput device");
+               goto failed;
+       }
+
+       memset(&event, 0, sizeof(event));
+       gettimeofday(&event.time, NULL);
+       event.type = EV_SYN;
+       event.code = SYN_REPORT;
+       if (write(fake->uinput, &event, sizeof(event)) != sizeof(event)) {
+               error("Error writing to uinput device");
+               goto failed;
+       }
+
+       return TRUE;
+
+failed:
+       ioctl(fake->uinput, UI_DEV_DESTROY);
+       close(fake->uinput);
+       fake->uinput = -1;
+       g_io_channel_unref(fake->io);
+
+       return FALSE;
+}
+
+static int ps3remote_setup_uinput(struct fake_input *fake,
+                                               struct fake_hid *fake_hid)
+{
+       struct uinput_dev dev;
+       int i;
+
+       fake->uinput = open("/dev/input/uinput", O_RDWR);
+       if (fake->uinput < 0) {
+               fake->uinput = open("/dev/uinput", O_RDWR);
+               if (fake->uinput < 0) {
+                       fake->uinput = open("/dev/misc/uinput", O_RDWR);
+                       if (fake->uinput < 0) {
+                               error("Error opening uinput device file");
+                               return 1;
+                       }
+               }
+       }
+
+       memset(&dev, 0, sizeof(dev));
+       snprintf(dev.name, sizeof(dev.name), "%s", "PS3 Remote Controller");
+       dev.id.bustype = BUS_BLUETOOTH;
+       dev.id.vendor = fake_hid->vendor;
+       dev.id.product = fake_hid->product;
+
+       if (write(fake->uinput, &dev, sizeof(dev)) != sizeof(dev)) {
+               error("Error creating uinput device");
+               goto err;
+       }
+
+       /* enabling key events */
+       if (ioctl(fake->uinput, UI_SET_EVBIT, EV_KEY) < 0) {
+               error("Error enabling uinput device key events");
+               goto err;
+       }
+
+       /* enabling keys */
+       for (i = 0; i < 256; i++)
+               if (ps3remote_keymap[i] != KEY_RESERVED)
+                       if (ioctl(fake->uinput, UI_SET_KEYBIT,
+                                               ps3remote_keymap[i]) < 0) {
+                               error("Error enabling uinput key %i",
+                                                       ps3remote_keymap[i]);
+                               goto err;
+                       }
+
+       /* creating the device */
+       if (ioctl(fake->uinput, UI_DEV_CREATE) < 0) {
+               error("Error creating uinput device");
+               goto err;
+       }
+
+       return 0;
+
+err:
+       close(fake->uinput);
+       return 1;
+}
+
+static gboolean fake_hid_common_connect(struct fake_input *fake, GError **err)
+{
+       return TRUE;
+}
+
+static int fake_hid_common_disconnect(struct fake_input *fake)
+{
+       return 0;
+}
+
+static struct fake_hid fake_hid_table[] = {
+       /* Sony PS3 remote device */
+       {
+               .vendor         = 0x054c,
+               .product        = 0x0306,
+               .connect        = fake_hid_common_connect,
+               .disconnect     = fake_hid_common_disconnect,
+               .event          = ps3remote_event,
+               .setup_uinput   = ps3remote_setup_uinput,
+               .devices        = NULL,
+       },
+
+       { },
+};
+
+static inline int fake_hid_match_device(uint16_t vendor, uint16_t product,
+                                                       struct fake_hid *fhid)
+{
+       return vendor == fhid->vendor && product == fhid->product;
+}
+
+struct fake_hid *get_fake_hid(uint16_t vendor, uint16_t product)
+{
+       int i;
+
+       for (i = 0; fake_hid_table[i].vendor != 0; i++)
+               if (fake_hid_match_device(vendor, product, &fake_hid_table[i]))
+                       return &fake_hid_table[i];
+
+       return NULL;
+}
+
+struct fake_input *fake_hid_connadd(struct fake_input *fake,
+                                               GIOChannel *intr_io,
+                                               struct fake_hid *fake_hid)
+{
+       GList *l;
+       struct fake_input *old = NULL;
+
+       /* Look for an already setup device */
+       for (l = fake_hid->devices; l != NULL; l = l->next) {
+               old = l->data;
+               if (old->idev == fake->idev) {
+                       g_free(fake);
+                       fake = old;
+                       fake_hid->connect(fake, NULL);
+                       break;
+               }
+               old = NULL;
+       }
+
+       /* New device? Add it to the list of known devices,
+        * and create the uinput necessary */
+       if (old == NULL || old->uinput < 0) {
+               if (fake_hid->setup_uinput(fake, fake_hid)) {
+                       error("Error setting up uinput");
+                       g_free(fake);
+                       return NULL;
+               }
+       }
+
+       if (old == NULL)
+               fake_hid->devices = g_list_append(fake_hid->devices, fake);
+
+       fake->io = g_io_channel_ref(intr_io);
+       g_io_channel_set_close_on_unref(fake->io, TRUE);
+       g_io_add_watch(fake->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       (GIOFunc) fake_hid->event, fake);
+
+       return fake;
+}
diff --git a/input/fakehid.h b/input/fakehid.h
new file mode 100644 (file)
index 0000000..3a5d7c4
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct fake_hid;
+struct fake_input;
+
+struct fake_hid {
+       uint16_t vendor;
+       uint16_t product;
+       gboolean (*connect) (struct fake_input *fake_input, GError **err);
+       int (*disconnect) (struct fake_input *fake_input);
+       gboolean (*event) (GIOChannel *chan, GIOCondition cond, gpointer data);
+       int (*setup_uinput) (struct fake_input *fake, struct fake_hid *fake_hid);
+       GList *devices;
+};
+
+struct fake_hid *get_fake_hid(uint16_t vendor, uint16_t product);
+
+struct fake_input *fake_hid_connadd(struct fake_input *fake, GIOChannel *intr_io,
+                                               struct fake_hid *fake_hid);
diff --git a/input/input.conf b/input/input.conf
new file mode 100644 (file)
index 0000000..abfb64f
--- /dev/null
@@ -0,0 +1,9 @@
+# Configuration file for the input service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# Set idle timeout (in minutes) before the connection will
+# be disconnect (defaults to 0 for no timeout)
+#IdleTimeout=30
diff --git a/input/main.c b/input/main.c
new file mode 100644 (file)
index 0000000..da09b86
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "manager.h"
+
+static GKeyFile *load_config_file(const char *file)
+{
+       GKeyFile *keyfile;
+       GError *err = NULL;
+
+       keyfile = g_key_file_new();
+
+       if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+               error("Parsing %s failed: %s", file, err->message);
+               g_error_free(err);
+               g_key_file_free(keyfile);
+               return NULL;
+       }
+
+       return keyfile;
+}
+
+static DBusConnection *connection;
+
+static int input_init(void)
+{
+       GKeyFile *config;
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -EIO;
+
+       config = load_config_file(CONFIGDIR "/input.conf");
+
+       if (input_manager_init(connection, config) < 0) {
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       if (config)
+               g_key_file_free(config);
+
+       return 0;
+}
+
+static void input_exit(void)
+{
+       input_manager_exit();
+
+       dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(input, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                                                       input_init, input_exit)
diff --git a/input/manager.c b/input/manager.c
new file mode 100644 (file)
index 0000000..5cc552b
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+
+#include <gdbus.h>
+
+#include "log.h"
+#include "../src/adapter.h"
+#include "../src/device.h"
+
+#include "device.h"
+#include "server.h"
+#include "manager.h"
+
+static int idle_timeout = 0;
+
+static DBusConnection *connection = NULL;
+static GSList *adapters = NULL;
+
+static void input_remove(struct btd_device *device, const char *uuid)
+{
+       const gchar *path = device_get_path(device);
+
+       DBG("path %s", path);
+
+       input_device_unregister(path, uuid);
+}
+
+static int hid_device_probe(struct btd_device *device, GSList *uuids)
+{
+       const gchar *path = device_get_path(device);
+       const sdp_record_t *rec = btd_device_get_record(device, uuids->data);
+
+       DBG("path %s", path);
+
+       if (!rec)
+               return -1;
+
+       return input_device_register(connection, device, path, HID_UUID, rec,
+                                                       idle_timeout * 60);
+}
+
+static void hid_device_remove(struct btd_device *device)
+{
+       input_remove(device, HID_UUID);
+}
+
+static int headset_probe(struct btd_device *device, GSList *uuids)
+{
+       const gchar *path = device_get_path(device);
+       const sdp_record_t *record;
+       sdp_list_t *protos;
+       int ch;
+
+       DBG("path %s", path);
+
+       if (!g_slist_find_custom(uuids, HSP_HS_UUID,
+                                       (GCompareFunc) strcasecmp))
+               return -EINVAL;
+
+       record = btd_device_get_record(device, uuids->data);
+
+       if (!record || sdp_get_access_protos(record, &protos) < 0) {
+               error("Invalid record");
+               return -EINVAL;
+       }
+
+       ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+       sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+       sdp_list_free(protos, NULL);
+
+       if (ch <= 0) {
+               error("Invalid RFCOMM channel");
+               return -EINVAL;
+       }
+
+       return fake_input_register(connection, device, path, HSP_HS_UUID, ch);
+}
+
+static void headset_remove(struct btd_device *device)
+{
+       input_remove(device, HSP_HS_UUID);
+}
+
+static int hid_server_probe(struct btd_adapter *adapter)
+{
+       bdaddr_t src;
+       int ret;
+
+       adapter_get_address(adapter, &src);
+
+       ret = server_start(&src);
+       if (ret < 0)
+               return ret;
+
+       adapters = g_slist_append(adapters, btd_adapter_ref(adapter));
+
+       return 0;
+}
+
+static void hid_server_remove(struct btd_adapter *adapter)
+{
+       bdaddr_t src;
+
+       adapter_get_address(adapter, &src);
+
+       server_stop(&src);
+
+       adapters = g_slist_remove(adapters, adapter);
+       btd_adapter_unref(adapter);
+}
+
+static struct btd_device_driver input_hid_driver = {
+       .name   = "input-hid",
+       .uuids  = BTD_UUIDS(HID_UUID),
+       .probe  = hid_device_probe,
+       .remove = hid_device_remove,
+};
+
+static struct btd_device_driver input_headset_driver = {
+       .name   = "input-headset",
+       .uuids  = BTD_UUIDS(HSP_HS_UUID),
+       .probe  = headset_probe,
+       .remove = headset_remove,
+};
+
+static struct btd_adapter_driver input_server_driver = {
+       .name   = "input-server",
+       .probe  = hid_server_probe,
+       .remove = hid_server_remove,
+};
+
+int input_manager_init(DBusConnection *conn, GKeyFile *config)
+{
+       GError *err = NULL;
+
+       if (config) {
+               idle_timeout = g_key_file_get_integer(config, "General",
+                                               "IdleTimeout", &err);
+               if (err) {
+                       DBG("input.conf: %s", err->message);
+                       g_error_free(err);
+               }
+       }
+
+       connection = dbus_connection_ref(conn);
+
+       btd_register_adapter_driver(&input_server_driver);
+
+       btd_register_device_driver(&input_hid_driver);
+       btd_register_device_driver(&input_headset_driver);
+
+       return 0;
+}
+
+void input_manager_exit(void)
+{
+       btd_unregister_device_driver(&input_hid_driver);
+       btd_unregister_device_driver(&input_headset_driver);
+
+       btd_unregister_adapter_driver(&input_server_driver);
+
+       dbus_connection_unref(connection);
+
+       connection = NULL;
+}
diff --git a/input/manager.h b/input/manager.h
new file mode 100644 (file)
index 0000000..7b93c5b
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int input_manager_init(DBusConnection *conn, GKeyFile *config);
+void input_manager_exit(void);
diff --git a/input/server.c b/input/server.c
new file mode 100644 (file)
index 0000000..86e2ac8
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "log.h"
+
+#include "glib-helper.h"
+#include "btio.h"
+#include "adapter.h"
+#include "device.h"
+#include "server.h"
+
+static GSList *servers = NULL;
+struct input_server {
+       bdaddr_t src;
+       GIOChannel *ctrl;
+       GIOChannel *intr;
+       GIOChannel *confirm;
+};
+
+static gint server_cmp(gconstpointer s, gconstpointer user_data)
+{
+       const struct input_server *server = s;
+       const bdaddr_t *src = user_data;
+
+       return bacmp(&server->src, src);
+}
+
+static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+       uint16_t psm;
+       bdaddr_t src, dst;
+       char address[18];
+       GError *gerr = NULL;
+       int ret;
+
+       if (err) {
+               error("%s", err->message);
+               return;
+       }
+
+       bt_io_get(chan, BT_IO_L2CAP, &gerr,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_PSM, &psm,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               g_io_channel_shutdown(chan, TRUE, NULL);
+               return;
+       }
+
+       ba2str(&dst, address);
+       DBG("Incoming connection from %s on PSM %d", address, psm);
+
+       ret = input_device_set_channel(&src, &dst, psm, chan);
+       if (ret == 0)
+               return;
+
+       error("Refusing input device connect: %s (%d)", strerror(-ret), -ret);
+
+       /* Send unplug virtual cable to unknown devices */
+       if (ret == -ENOENT && psm == L2CAP_PSM_HIDP_CTRL) {
+               unsigned char unplug = 0x15;
+               int sk = g_io_channel_unix_get_fd(chan);
+               if (write(sk, &unplug, sizeof(unplug)) < 0)
+                       error("Unable to send virtual cable unplug");
+       }
+
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void auth_callback(DBusError *derr, void *user_data)
+{
+       struct input_server *server = user_data;
+       bdaddr_t src, dst;
+       GError *err = NULL;
+
+       bt_io_get(server->confirm, BT_IO_L2CAP, &err,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               goto reject;
+       }
+
+       if (derr) {
+               error("Access denied: %s", derr->message);
+               goto reject;
+       }
+
+       if (!bt_io_accept(server->confirm, connect_event_cb, server,
+                               NULL, &err)) {
+               error("bt_io_accept: %s", err->message);
+               g_error_free(err);
+               goto reject;
+       }
+
+       g_io_channel_unref(server->confirm);
+       server->confirm = NULL;
+
+       return;
+
+reject:
+       g_io_channel_shutdown(server->confirm, TRUE, NULL);
+       g_io_channel_unref(server->confirm);
+       server->confirm = NULL;
+       input_device_close_channels(&src, &dst);
+}
+
+static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
+{
+       struct input_server *server = user_data;
+       bdaddr_t src, dst;
+       GError *err = NULL;
+       char addr[18];
+       int ret;
+
+       bt_io_get(chan, BT_IO_L2CAP, &err,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               goto drop;
+       }
+
+       if (server->confirm) {
+               char address[18];
+
+               ba2str(&dst, address);
+               error("Refusing connection from %s: setup in progress",
+                                                               address);
+               goto drop;
+       }
+
+       server->confirm = g_io_channel_ref(chan);
+
+       ret = btd_request_authorization(&src, &dst, HID_UUID,
+                                       auth_callback, server);
+       if (ret == 0)
+               return;
+
+       ba2str(&src, addr);
+       error("input: authorization for %s failed: %s (%d)",
+                                               addr, strerror(-ret), ret);
+
+       g_io_channel_unref(server->confirm);
+       server->confirm = NULL;
+
+drop:
+       input_device_close_channels(&src, &dst);
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+int server_start(const bdaddr_t *src)
+{
+       struct input_server *server;
+       GError *err = NULL;
+
+       server = g_new0(struct input_server, 1);
+       bacpy(&server->src, src);
+
+       server->ctrl = bt_io_listen(BT_IO_L2CAP, connect_event_cb, NULL,
+                               server, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, src,
+                               BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+                               BT_IO_OPT_INVALID);
+       if (!server->ctrl) {
+               error("Failed to listen on control channel");
+               g_error_free(err);
+               g_free(server);
+               return -1;
+       }
+
+       server->intr = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event_cb,
+                               server, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, src,
+                               BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+                               BT_IO_OPT_INVALID);
+       if (!server->intr) {
+               error("Failed to listen on interrupt channel");
+               g_io_channel_unref(server->ctrl);
+               g_error_free(err);
+               g_free(server);
+               return -1;
+       }
+
+       servers = g_slist_append(servers, server);
+
+       return 0;
+}
+
+void server_stop(const bdaddr_t *src)
+{
+       struct input_server *server;
+       GSList *l;
+
+       l = g_slist_find_custom(servers, src, server_cmp);
+       if (!l)
+               return;
+
+       server = l->data;
+
+       g_io_channel_shutdown(server->intr, TRUE, NULL);
+       g_io_channel_unref(server->intr);
+
+       g_io_channel_shutdown(server->ctrl, TRUE, NULL);
+       g_io_channel_unref(server->ctrl);
+
+       servers = g_slist_remove(servers, server);
+       g_free(server);
+}
diff --git a/input/server.h b/input/server.h
new file mode 100644 (file)
index 0000000..74159bb
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int server_start(const bdaddr_t *src);
+void server_stop(const bdaddr_t *src);
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..a9244eb
--- /dev/null
@@ -0,0 +1,527 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2011-01-19.21; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+nl='
+'
+IFS=" ""       $nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit=${DOITPROG-}
+if test -z "$doit"; then
+  doit_exec=exec
+else
+  doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_glob='?'
+initialize_posix_glob='
+  test "$posix_glob" != "?" || {
+    if (set -f) 2>/dev/null; then
+      posix_glob=
+    else
+      posix_glob=:
+    fi
+  }
+'
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+no_target_directory=
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve the last data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -s            $stripprog installed files.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+       shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+       case $mode in
+         *' '* | *'    '* | *'
+'*       | *'*'* | *'?'* | *'['*)
+           echo "$0: invalid mode: $mode" >&2
+           exit 1;;
+       esac
+       shift;;
+
+    -o) chowncmd="$chownprog $2"
+       shift;;
+
+    -s) stripcmd=$stripprog;;
+
+    -t) dst_arg=$2
+       # Protect names problematic for `test' and other utilities.
+       case $dst_arg in
+         -* | [=\(\)!]) dst_arg=./$dst_arg;;
+       esac
+       shift;;
+
+    -T) no_target_directory=true;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --)        shift
+       break;;
+
+    -*)        echo "$0: invalid option: $1" >&2
+       exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for `test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call `install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+       u_plus_rw=
+      else
+       u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+       u_plus_rw=
+      else
+       u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names problematic for `test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename; won't work
+    # if double slashes aren't ignored.
+    if test -d "$dst"; then
+      if test -n "$no_target_directory"; then
+       echo "$0: $dst_arg: Is a directory" >&2
+       exit 1
+      fi
+      dstdir=$dst
+      dst=$dstdir/`basename "$src"`
+      dstdir_status=0
+    else
+      # Prefer dirname, but fall back on a substitute if dirname fails.
+      dstdir=`
+       (dirname "$dst") 2>/dev/null ||
+       expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+            X"$dst" : 'X\(//\)[^/]' \| \
+            X"$dst" : 'X\(//\)$' \| \
+            X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+       echo X"$dst" |
+           sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+                  s//\1/
+                  q
+                }
+                /^X\(\/\/\)[^/].*/{
+                  s//\1/
+                  q
+                }
+                /^X\(\/\/\)$/{
+                  s//\1/
+                  q
+                }
+                /^X\(\/\).*/{
+                  s//\1/
+                  q
+                }
+                s/.*/./; q'
+      `
+
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+       # Create intermediate dirs using mode 755 as modified by the umask.
+       # This is like FreeBSD 'install' as of 1997-10-28.
+       umask=`umask`
+       case $stripcmd.$umask in
+         # Optimize common cases.
+         *[2367][2367]) mkdir_umask=$umask;;
+         .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+         *[0-7])
+           mkdir_umask=`expr $umask + 22 \
+             - $umask % 100 % 40 + $umask % 20 \
+             - $umask % 10 % 4 + $umask % 2
+           `;;
+         *) mkdir_umask=$umask,go-w;;
+       esac
+
+       # With -d, create the new directory with the user-specified mode.
+       # Otherwise, rely on $mkdir_umask.
+       if test -n "$dir_arg"; then
+         mkdir_mode=-m$mode
+       else
+         mkdir_mode=
+       fi
+
+       posix_mkdir=false
+       case $umask in
+         *[123567][0-7][0-7])
+           # POSIX mkdir -p sets u+wx bits regardless of umask, which
+           # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+           ;;
+         *)
+           tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+           trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+           if (umask $mkdir_umask &&
+               exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+           then
+             if test -z "$dir_arg" || {
+                  # Check for POSIX incompatibilities with -m.
+                  # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+                  # other-writeable bit of parent directory when it shouldn't.
+                  # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+                  ls_ld_tmpdir=`ls -ld "$tmpdir"`
+                  case $ls_ld_tmpdir in
+                    d????-?r-*) different_mode=700;;
+                    d????-?--*) different_mode=755;;
+                    *) false;;
+                  esac &&
+                  $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+                    ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+                    test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+                  }
+                }
+             then posix_mkdir=:
+             fi
+             rmdir "$tmpdir/d" "$tmpdir"
+           else
+             # Remove any dirs left behind by ancient mkdir implementations.
+             rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+           fi
+           trap '' 0;;
+       esac;;
+    esac
+
+    if
+      $posix_mkdir && (
+       umask $mkdir_umask &&
+       $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # The umask is ridiculous, or mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+       /*) prefix='/';;
+       [-=\(\)!]*) prefix='./';;
+       *)  prefix='';;
+      esac
+
+      eval "$initialize_posix_glob"
+
+      oIFS=$IFS
+      IFS=/
+      $posix_glob set -f
+      set fnord $dstdir
+      shift
+      $posix_glob set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+       test X"$d" = X && continue
+
+       prefix=$prefix$d
+       if test -d "$prefix"; then
+         prefixes=
+       else
+         if $posix_mkdir; then
+           (umask=$mkdir_umask &&
+            $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+           # Don't fail if two instances are running concurrently.
+           test -d "$prefix" || exit 1
+         else
+           case $prefix in
+             *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+             *) qprefix=$prefix;;
+           esac
+           prefixes="$prefixes '$qprefix'"
+         fi
+       fi
+       prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+       # Don't fail if two instances are running concurrently.
+       (umask $mkdir_umask &&
+        eval "\$doit_exec \$mkdirprog $prefixes") ||
+         test -d "$dstdir" || exit 1
+       obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=$dstdir/_inst.$$_
+    rmtmp=$dstdir/_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"    2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+
+       eval "$initialize_posix_glob" &&
+       $posix_glob set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       $posix_glob set +f &&
+
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+       # Now remove or move aside any old file at destination location.
+       # We try this two ways since rm can't unlink itself on some
+       # systems and the destination file might be busy for other
+       # reasons.  In this case, the final cleanup might fail but the new
+       # file should still install successfully.
+       {
+         test ! -f "$dst" ||
+         $doit $rmcmd -f "$dst" 2>/dev/null ||
+         { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+           { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+         } ||
+         { echo "$0: cannot unlink or rename $dst" >&2
+           (exit 1); exit 1
+         }
+       } &&
+
+       # Now rename the file to the real destination.
+       $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/lib/a2mp.h b/lib/a2mp.h
new file mode 100644 (file)
index 0000000..61f1c1e
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (c) 2012  Code Aurora Forum. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __A2MP_H
+#define __A2MP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A2MP Protocol */
+
+/* A2MP command codes */
+
+#define A2MP_COMMAND_REJ       0x01
+#define A2MP_DISCOVER_REQ      0x02
+#define A2MP_DISCOVER_RSP      0x03
+#define A2MP_CHANGE_NOTIFY     0x04
+#define A2MP_CHANGE_RSP                0x05
+#define A2MP_INFO_REQ          0x06
+#define A2MP_INFO_RSP          0x07
+#define A2MP_ASSOC_REQ         0x08
+#define A2MP_ASSOC_RSP         0x09
+#define A2MP_CREATE_REQ                0x0a
+#define A2MP_CREATE_RSP                0x0b
+#define A2MP_DISCONN_REQ       0x0c
+#define A2MP_DISCONN_RSP       0x0d
+
+struct a2mp_hdr {
+       uint8_t         code;
+       uint8_t         ident;
+       uint16_t        len;
+} __attribute__ ((packed));
+#define A2MP_HDR_SIZE 4
+
+struct a2mp_command_rej {
+       uint16_t        reason;
+} __attribute__ ((packed));
+
+struct a2mp_discover_req {
+       uint16_t        mtu;
+       uint16_t        mask;
+} __attribute__ ((packed));
+
+struct a2mp_ctrl {
+       uint8_t         id;
+       uint8_t         type;
+       uint8_t         status;
+} __attribute__ ((packed));
+
+struct a2mp_discover_rsp {
+       uint16_t        mtu;
+       uint16_t        mask;
+       struct a2mp_ctrl ctrl_list[0];
+} __attribute__ ((packed));
+
+struct a2mp_info_req {
+       uint8_t         id;
+} __attribute__ ((packed));
+
+struct a2mp_info_rsp {
+       uint8_t         id;
+       uint8_t         status;
+       uint32_t        total_bw;
+       uint32_t        max_bw;
+       uint32_t        min_latency;
+       uint16_t        pal_caps;
+       uint16_t        assoc_size;
+} __attribute__ ((packed));
+
+struct a2mp_assoc_req {
+       uint8_t         id;
+} __attribute__ ((packed));
+
+struct a2mp_assoc_rsp {
+       uint8_t         id;
+       uint8_t         status;
+       uint8_t         assoc_data[0];
+} __attribute__ ((packed));
+
+struct a2mp_create_req {
+       uint8_t         local_id;
+       uint8_t         remote_id;
+       uint8_t         assoc_data[0];
+} __attribute__ ((packed));
+
+struct a2mp_create_rsp {
+       uint8_t         local_id;
+       uint8_t         remote_id;
+       uint8_t         status;
+} __attribute__ ((packed));
+
+struct a2mp_disconn_req {
+       uint8_t         local_id;
+       uint8_t         remote_id;
+} __attribute__ ((packed));
+
+struct a2mp_disconn_rsp {
+       uint8_t         local_id;
+       uint8_t         remote_id;
+       uint8_t         status;
+} __attribute__ ((packed));
+
+#define A2MP_COMMAND_NOT_RECOGNIZED 0x0000
+
+/* AMP controller status */
+#define AMP_CTRL_POWERED_DOWN          0x00
+#define AMP_CTRL_BLUETOOTH_ONLY                0x01
+#define AMP_CTRL_NO_CAPACITY           0x02
+#define AMP_CTRL_LOW_CAPACITY          0x03
+#define AMP_CTRL_MEDIUM_CAPACITY       0x04
+#define AMP_CTRL_HIGH_CAPACITY         0x05
+#define AMP_CTRL_FULL_CAPACITY         0x06
+
+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS                            0x00
+#define A2MP_STATUS_INVALID_CTRL_ID                    0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION         0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS            0x02
+#define A2MP_STATUS_COLLISION_OCCURED                  0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD                  0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS                   0x05
+#define A2MP_STATUS_SECURITY_VIOLATION                 0x06
+
+#define A2MP_MAC_ADDR_TYPE             1
+#define A2MP_PREF_CHANLIST_TYPE                2
+#define A2MP_CONNECTED_CHAN            3
+#define A2MP_PAL_CAP_TYPE              4
+#define A2MP_PAL_VER_INFO              5
+
+struct a2mp_tlv {
+       uint8_t type;
+       uint16_t len;
+       uint8_t val[0];
+} __attribute__ ((packed));
+
+struct a2mp_pal_ver {
+       uint8_t ver;
+       uint16_t company_id;
+       uint16_t sub_ver;
+} __attribute__ ((packed));
+
+struct a2mp_country_triplet {
+       union {
+               struct {
+                       uint8_t first_channel;
+                       uint8_t num_channels;
+                       int8_t max_power;
+               } __attribute__ ((packed)) chans;
+               struct {
+                       uint8_t reg_extension_id;
+                       uint8_t reg_class;
+                       uint8_t coverage_class;
+               } __attribute__ ((packed)) ext;
+       };
+} __attribute__ ((packed));
+
+struct a2mp_chan_list {
+       uint8_t country_code[3];
+       struct a2mp_country_triplet triplets[0];
+} __attribute__ ((packed));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __A2MP_H */
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
new file mode 100644 (file)
index 0000000..a0be884
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "bluetooth.h"
+#include "hci.h"
+
+void baswap(bdaddr_t *dst, const bdaddr_t *src)
+{
+       register unsigned char *d = (unsigned char *) dst;
+       register const unsigned char *s = (const unsigned char *) src;
+       register int i;
+
+       for (i = 0; i < 6; i++)
+               d[i] = s[5-i];
+}
+
+char *batostr(const bdaddr_t *ba)
+{
+       char *str = bt_malloc(18);
+       if (!str)
+               return NULL;
+
+       sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+               ba->b[0], ba->b[1], ba->b[2],
+               ba->b[3], ba->b[4], ba->b[5]);
+
+       return str;
+}
+
+bdaddr_t *strtoba(const char *str)
+{
+       bdaddr_t b;
+       bdaddr_t *ba = bt_malloc(sizeof(*ba));
+
+       if (ba) {
+               str2ba(str, &b);
+               baswap(ba, &b);
+       }
+
+       return ba;
+}
+
+int ba2str(const bdaddr_t *ba, char *str)
+{
+       return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+               ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]);
+}
+
+int str2ba(const char *str, bdaddr_t *ba)
+{
+       bdaddr_t b;
+       int i;
+
+       if (bachk(str) < 0) {
+               memset(ba, 0, sizeof(*ba));
+               return -1;
+       }
+
+       for (i = 0; i < 6; i++, str += 3)
+               b.b[i] = strtol(str, NULL, 16);
+
+       baswap(ba, &b);
+
+       return 0;
+}
+
+int ba2oui(const bdaddr_t *ba, char *str)
+{
+       return sprintf(str, "%2.2X-%2.2X-%2.2X", ba->b[5], ba->b[4], ba->b[3]);
+}
+
+int bachk(const char *str)
+{
+       if (!str)
+               return -1;
+
+       if (strlen(str) != 17)
+               return -1;
+
+       while (*str) {
+               if (!isxdigit(*str++))
+                       return -1;
+
+               if (!isxdigit(*str++))
+                       return -1;
+
+               if (*str == 0)
+                       break;
+
+               if (*str++ != ':')
+                       return -1;
+       }
+
+       return 0;
+}
+
+int baprintf(const char *format, ...)
+{
+       va_list ap;
+       int len;
+
+       va_start(ap, format);
+       len = vprintf(format, ap);
+       va_end(ap);
+
+       return len;
+}
+
+int bafprintf(FILE *stream, const char *format, ...)
+{
+       va_list ap;
+       int len;
+
+       va_start(ap, format);
+       len = vfprintf(stream, format, ap);
+       va_end(ap);
+
+       return len;
+}
+
+int basprintf(char *str, const char *format, ...)
+{
+       va_list ap;
+       int len;
+
+       va_start(ap, format);
+       len = vsnprintf(str, (~0U) >> 1, format, ap);
+       va_end(ap);
+
+       return len;
+}
+
+int basnprintf(char *str, size_t size, const char *format, ...)
+{
+       va_list ap;
+       int len;
+
+       va_start(ap, format);
+       len = vsnprintf(str, size, format, ap);
+       va_end(ap);
+
+       return len;
+}
+
+void *bt_malloc(size_t size)
+{
+       return malloc(size);
+}
+
+void bt_free(void *ptr)
+{
+       free(ptr);
+}
+
+/* Bluetooth error codes to Unix errno mapping */
+int bt_error(uint16_t code)
+{
+       switch (code) {
+       case 0:
+               return 0;
+       case HCI_UNKNOWN_COMMAND:
+               return EBADRQC;
+       case HCI_NO_CONNECTION:
+               return ENOTCONN;
+       case HCI_HARDWARE_FAILURE:
+               return EIO;
+       case HCI_PAGE_TIMEOUT:
+               return EHOSTDOWN;
+       case HCI_AUTHENTICATION_FAILURE:
+               return EACCES;
+       case HCI_PIN_OR_KEY_MISSING:
+               return EINVAL;
+       case HCI_MEMORY_FULL:
+               return ENOMEM;
+       case HCI_CONNECTION_TIMEOUT:
+               return ETIMEDOUT;
+       case HCI_MAX_NUMBER_OF_CONNECTIONS:
+       case HCI_MAX_NUMBER_OF_SCO_CONNECTIONS:
+               return EMLINK;
+       case HCI_ACL_CONNECTION_EXISTS:
+               return EALREADY;
+       case HCI_COMMAND_DISALLOWED:
+       case HCI_TRANSACTION_COLLISION:
+       case HCI_ROLE_SWITCH_PENDING:
+               return EBUSY;
+       case HCI_REJECTED_LIMITED_RESOURCES:
+       case HCI_REJECTED_PERSONAL:
+       case HCI_QOS_REJECTED:
+               return ECONNREFUSED;
+       case HCI_HOST_TIMEOUT:
+               return ETIMEDOUT;
+       case HCI_UNSUPPORTED_FEATURE:
+       case HCI_QOS_NOT_SUPPORTED:
+       case HCI_PAIRING_NOT_SUPPORTED:
+       case HCI_CLASSIFICATION_NOT_SUPPORTED:
+       case HCI_UNSUPPORTED_LMP_PARAMETER_VALUE:
+       case HCI_PARAMETER_OUT_OF_RANGE:
+       case HCI_QOS_UNACCEPTABLE_PARAMETER:
+               return EOPNOTSUPP;
+       case HCI_INVALID_PARAMETERS:
+       case HCI_SLOT_VIOLATION:
+               return EINVAL;
+       case HCI_OE_USER_ENDED_CONNECTION:
+       case HCI_OE_LOW_RESOURCES:
+       case HCI_OE_POWER_OFF:
+               return ECONNRESET;
+       case HCI_CONNECTION_TERMINATED:
+               return ECONNABORTED;
+       case HCI_REPEATED_ATTEMPTS:
+               return ELOOP;
+       case HCI_REJECTED_SECURITY:
+       case HCI_PAIRING_NOT_ALLOWED:
+       case HCI_INSUFFICIENT_SECURITY:
+               return EACCES;
+       case HCI_UNSUPPORTED_REMOTE_FEATURE:
+               return EPROTONOSUPPORT;
+       case HCI_SCO_OFFSET_REJECTED:
+               return ECONNREFUSED;
+       case HCI_UNKNOWN_LMP_PDU:
+       case HCI_INVALID_LMP_PARAMETERS:
+       case HCI_LMP_ERROR_TRANSACTION_COLLISION:
+       case HCI_LMP_PDU_NOT_ALLOWED:
+       case HCI_ENCRYPTION_MODE_NOT_ACCEPTED:
+               return EPROTO;
+       default:
+               return ENOSYS;
+       }
+}
+
+char *bt_compidtostr(int compid)
+{
+       switch (compid) {
+       case 0:
+               return "Ericsson Technology Licensing";
+       case 1:
+               return "Nokia Mobile Phones";
+       case 2:
+               return "Intel Corp.";
+       case 3:
+               return "IBM Corp.";
+       case 4:
+               return "Toshiba Corp.";
+       case 5:
+               return "3Com";
+       case 6:
+               return "Microsoft";
+       case 7:
+               return "Lucent";
+       case 8:
+               return "Motorola";
+       case 9:
+               return "Infineon Technologies AG";
+       case 10:
+               return "Cambridge Silicon Radio";
+       case 11:
+               return "Silicon Wave";
+       case 12:
+               return "Digianswer A/S";
+       case 13:
+               return "Texas Instruments Inc.";
+       case 14:
+               return "Parthus Technologies Inc.";
+       case 15:
+               return "Broadcom Corporation";
+       case 16:
+               return "Mitel Semiconductor";
+       case 17:
+               return "Widcomm, Inc.";
+       case 18:
+               return "Zeevo, Inc.";
+       case 19:
+               return "Atmel Corporation";
+       case 20:
+               return "Mitsubishi Electric Corporation";
+       case 21:
+               return "RTX Telecom A/S";
+       case 22:
+               return "KC Technology Inc.";
+       case 23:
+               return "Newlogic";
+       case 24:
+               return "Transilica, Inc.";
+       case 25:
+               return "Rohde & Schwartz GmbH & Co. KG";
+       case 26:
+               return "TTPCom Limited";
+       case 27:
+               return "Signia Technologies, Inc.";
+       case 28:
+               return "Conexant Systems Inc.";
+       case 29:
+               return "Qualcomm";
+       case 30:
+               return "Inventel";
+       case 31:
+               return "AVM Berlin";
+       case 32:
+               return "BandSpeed, Inc.";
+       case 33:
+               return "Mansella Ltd";
+       case 34:
+               return "NEC Corporation";
+       case 35:
+               return "WavePlus Technology Co., Ltd.";
+       case 36:
+               return "Alcatel";
+       case 37:
+               return "Philips Semiconductors";
+       case 38:
+               return "C Technologies";
+       case 39:
+               return "Open Interface";
+       case 40:
+               return "R F Micro Devices";
+       case 41:
+               return "Hitachi Ltd";
+       case 42:
+               return "Symbol Technologies, Inc.";
+       case 43:
+               return "Tenovis";
+       case 44:
+               return "Macronix International Co. Ltd.";
+       case 45:
+               return "GCT Semiconductor";
+       case 46:
+               return "Norwood Systems";
+       case 47:
+               return "MewTel Technology Inc.";
+       case 48:
+               return "ST Microelectronics";
+       case 49:
+               return "Synopsys";
+       case 50:
+               return "Red-M (Communications) Ltd";
+       case 51:
+               return "Commil Ltd";
+       case 52:
+               return "Computer Access Technology Corporation (CATC)";
+       case 53:
+               return "Eclipse (HQ Espana) S.L.";
+       case 54:
+               return "Renesas Technology Corp.";
+       case 55:
+               return "Mobilian Corporation";
+       case 56:
+               return "Terax";
+       case 57:
+               return "Integrated System Solution Corp.";
+       case 58:
+               return "Matsushita Electric Industrial Co., Ltd.";
+       case 59:
+               return "Gennum Corporation";
+       case 60:
+               return "Research In Motion";
+       case 61:
+               return "IPextreme, Inc.";
+       case 62:
+               return "Systems and Chips, Inc";
+       case 63:
+               return "Bluetooth SIG, Inc";
+       case 64:
+               return "Seiko Epson Corporation";
+       case 65:
+               return "Integrated Silicon Solution Taiwain, Inc.";
+       case 66:
+               return "CONWISE Technology Corporation Ltd";
+       case 67:
+               return "PARROT SA";
+       case 68:
+               return "Socket Communications";
+       case 69:
+               return "Atheros Communications, Inc.";
+       case 70:
+               return "MediaTek, Inc.";
+       case 71:
+               return "Bluegiga";
+       case 72:
+               return "Marvell Technology Group Ltd.";
+       case 73:
+               return "3DSP Corporation";
+       case 74:
+               return "Accel Semiconductor Ltd.";
+       case 75:
+               return "Continental Automotive Systems";
+       case 76:
+               return "Apple, Inc.";
+       case 77:
+               return "Staccato Communications, Inc.";
+       case 78:
+               return "Avago Technologies";
+       case 79:
+               return "APT Ltd.";
+       case 80:
+               return "SiRF Technology, Inc.";
+       case 81:
+               return "Tzero Technologies, Inc.";
+       case 82:
+               return "J&M Corporation";
+       case 83:
+               return "Free2move AB";
+       case 84:
+               return "3DiJoy Corporation";
+       case 85:
+               return "Plantronics, Inc.";
+       case 86:
+               return "Sony Ericsson Mobile Communications";
+       case 87:
+               return "Harman International Industries, Inc.";
+       case 88:
+               return "Vizio, Inc.";
+       case 89:
+               return "Nordic Semiconductor ASA";
+       case 90:
+               return "EM Microelectronic-Marin SA";
+       case 91:
+               return "Ralink Technology Corporation";
+       case 92:
+               return "Belkin International, Inc.";
+       case 93:
+               return "Realtek Semiconductor Corporation";
+       case 94:
+               return "Stonestreet One, LLC";
+       case 95:
+               return "Wicentric, Inc.";
+       case 96:
+               return "RivieraWaves S.A.S";
+       case 97:
+               return "RDA Microelectronics";
+       case 98:
+               return "Gibson Guitars";
+       case 99:
+               return "MiCommand Inc.";
+       case 100:
+               return "Band XI International, LLC";
+       case 101:
+               return "Hewlett-Packard Company";
+       case 102:
+               return "9Solutions Oy";
+       case 103:
+               return "GN Netcom A/S";
+       case 104:
+               return "General Motors";
+       case 105:
+               return "A&D Engineering, Inc.";
+       case 106:
+               return "MindTree Ltd.";
+       case 107:
+               return "Polar Electro OY";
+       case 108:
+               return "Beautiful Enterprise Co., Ltd.";
+       case 109:
+               return "BriarTek, Inc.";
+       case 110:
+               return "Summit Data Communications, Inc.";
+       case 111:
+               return "Sound ID";
+       case 112:
+               return "Monster, LLC";
+       case 113:
+               return "connectBlue AB";
+       case 114:
+               return "ShangHai Super Smart Electronics Co. Ltd.";
+       case 115:
+               return "Group Sense Ltd.";
+       case 116:
+               return "Zomm, LLC";
+       case 117:
+               return "Samsung Electronics Co. Ltd.";
+       case 118:
+               return "Creative Technology Ltd.";
+       case 119:
+               return "Laird Technologies";
+       case 120:
+               return "Nike, Inc.";
+       case 121:
+               return "lesswire AG";
+       case 122:
+               return "MStar Semiconductor, Inc.";
+       case 123:
+               return "Hanlynn Technologies";
+       case 124:
+               return "A & R Cambridge";
+       case 125:
+               return "Seers Technology Co. Ltd.";
+       case 126:
+               return "Sports Tracking Technologies Ltd.";
+       case 127:
+               return "Autonet Mobile";
+       case 128:
+               return "DeLorme Publishing Company, Inc.";
+       case 65535:
+               return "internal use";
+       default:
+               return "not assigned";
+       }
+}
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
new file mode 100644 (file)
index 0000000..0fc4508
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __BLUETOOTH_H
+#define __BLUETOOTH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <netinet/in.h>
+
+#ifndef AF_BLUETOOTH
+#define AF_BLUETOOTH   31
+#define PF_BLUETOOTH   AF_BLUETOOTH
+#endif
+
+#define BTPROTO_L2CAP  0
+#define BTPROTO_HCI    1
+#define BTPROTO_SCO    2
+#define BTPROTO_RFCOMM 3
+#define BTPROTO_BNEP   4
+#define BTPROTO_CMTP   5
+#define BTPROTO_HIDP   6
+#define BTPROTO_AVDTP  7
+
+#define SOL_HCI                0
+#define SOL_L2CAP      6
+#define SOL_SCO                17
+#define SOL_RFCOMM     18
+
+#ifndef SOL_BLUETOOTH
+#define SOL_BLUETOOTH  274
+#endif
+
+#define BT_SECURITY    4
+struct bt_security {
+       uint8_t level;
+       uint8_t key_size;
+};
+#define BT_SECURITY_SDP                0
+#define BT_SECURITY_LOW                1
+#define BT_SECURITY_MEDIUM     2
+#define BT_SECURITY_HIGH       3
+
+#define BT_DEFER_SETUP 7
+
+#define BT_FLUSHABLE   8
+
+#define BT_FLUSHABLE_OFF       0
+#define BT_FLUSHABLE_ON                1
+
+#define BT_CHANNEL_POLICY      10
+
+/* BR/EDR only (default policy)
+ *   AMP controllers cannot be used.
+ *   Channel move requests from the remote device are denied.
+ *   If the L2CAP channel is currently using AMP, move the channel to BR/EDR.
+ */
+#define BT_CHANNEL_POLICY_BREDR_ONLY           0
+
+/* BR/EDR Preferred
+ *   Allow use of AMP controllers.
+ *   If the L2CAP channel is currently on AMP, move it to BR/EDR.
+ *   Channel move requests from the remote device are allowed.
+ */
+#define BT_CHANNEL_POLICY_BREDR_PREFERRED      1
+
+/* AMP Preferred
+ *   Allow use of AMP controllers
+ *   If the L2CAP channel is currently on BR/EDR and AMP controller
+ *     resources are available, initiate a channel move to AMP.
+ *   Channel move requests from the remote device are allowed.
+ *   If the L2CAP socket has not been connected yet, try to create
+ *     and configure the channel directly on an AMP controller rather
+ *     than BR/EDR.
+ */
+#define BT_CHANNEL_POLICY_AMP_PREFERRED                2
+
+/* Connection and socket states */
+enum {
+       BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
+       BT_OPEN,
+       BT_BOUND,
+       BT_LISTEN,
+       BT_CONNECT,
+       BT_CONNECT2,
+       BT_CONFIG,
+       BT_DISCONN,
+       BT_CLOSED
+};
+
+/* Byte order conversions */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htobs(d)  (d)
+#define htobl(d)  (d)
+#define htobll(d) (d)
+#define btohs(d)  (d)
+#define btohl(d)  (d)
+#define btohll(d) (d)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define htobs(d)  bswap_16(d)
+#define htobl(d)  bswap_32(d)
+#define htobll(d) bswap_64(d)
+#define btohs(d)  bswap_16(d)
+#define btohl(d)  bswap_32(d)
+#define btohll(d) bswap_64(d)
+#else
+#error "Unknown byte order"
+#endif
+
+/* Bluetooth unaligned access */
+#define bt_get_unaligned(ptr)                  \
+({                                             \
+       struct __attribute__((packed)) {        \
+               typeof(*(ptr)) __v;             \
+       } *__p = (typeof(__p)) (ptr);           \
+       __p->__v;                               \
+})
+
+#define bt_put_unaligned(val, ptr)             \
+do {                                           \
+       struct __attribute__((packed)) {        \
+               typeof(*(ptr)) __v;             \
+       } *__p = (typeof(__p)) (ptr);           \
+       __p->__v = (val);                       \
+} while(0)
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline uint64_t bt_get_le64(const void *ptr)
+{
+       return bt_get_unaligned((const uint64_t *) ptr);
+}
+
+static inline uint64_t bt_get_be64(const void *ptr)
+{
+       return bswap_64(bt_get_unaligned((const uint64_t *) ptr));
+}
+
+static inline uint32_t bt_get_le32(const void *ptr)
+{
+       return bt_get_unaligned((const uint32_t *) ptr);
+}
+
+static inline uint32_t bt_get_be32(const void *ptr)
+{
+       return bswap_32(bt_get_unaligned((const uint32_t *) ptr));
+}
+
+static inline uint16_t bt_get_le16(const void *ptr)
+{
+       return bt_get_unaligned((const uint16_t *) ptr);
+}
+
+static inline uint16_t bt_get_be16(const void *ptr)
+{
+       return bswap_16(bt_get_unaligned((const uint16_t *) ptr));
+}
+#elif __BYTE_ORDER == __BIG_ENDIAN
+static inline uint64_t bt_get_le64(const void *ptr)
+{
+       return bswap_64(bt_get_unaligned((const uint64_t *) ptr));
+}
+
+static inline uint64_t bt_get_be64(const void *ptr)
+{
+       return bt_get_unaligned((const uint64_t *) ptr);
+}
+
+static inline uint32_t bt_get_le32(const void *ptr)
+{
+       return bswap_32(bt_get_unaligned((const uint32_t *) ptr));
+}
+
+static inline uint32_t bt_get_be32(const void *ptr)
+{
+       return bt_get_unaligned((const uint32_t *) ptr);
+}
+
+static inline uint16_t bt_get_le16(const void *ptr)
+{
+       return bswap_16(bt_get_unaligned((const uint16_t *) ptr));
+}
+
+static inline uint16_t bt_get_be16(const void *ptr)
+{
+       return bt_get_unaligned((const uint16_t *) ptr);
+}
+#else
+#error "Unknown byte order"
+#endif
+
+/* BD Address */
+typedef struct {
+       uint8_t b[6];
+} __attribute__((packed)) bdaddr_t;
+
+/* BD Address type */
+#define BDADDR_BREDR           0x00
+#define BDADDR_LE_PUBLIC       0x01
+#define BDADDR_LE_RANDOM       0x02
+
+#define BDADDR_ANY   (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
+#define BDADDR_ALL   (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}})
+#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}})
+
+/* Copy, swap, convert BD Address */
+static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2)
+{
+       return memcmp(ba1, ba2, sizeof(bdaddr_t));
+}
+static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src)
+{
+       memcpy(dst, src, sizeof(bdaddr_t));
+}
+
+void baswap(bdaddr_t *dst, const bdaddr_t *src);
+bdaddr_t *strtoba(const char *str);
+char *batostr(const bdaddr_t *ba);
+int ba2str(const bdaddr_t *ba, char *str);
+int str2ba(const char *str, bdaddr_t *ba);
+int ba2oui(const bdaddr_t *ba, char *oui);
+int bachk(const char *str);
+
+int baprintf(const char *format, ...);
+int bafprintf(FILE *stream, const char *format, ...);
+int basprintf(char *str, const char *format, ...);
+int basnprintf(char *str, size_t size, const char *format, ...);
+
+void *bt_malloc(size_t size);
+void bt_free(void *ptr);
+
+int bt_error(uint16_t code);
+char *bt_compidtostr(int id);
+
+typedef struct {
+       uint8_t data[16];
+} uint128_t;
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+
+#define ntoh64(x) (x)
+
+static inline void ntoh128(const uint128_t *src, uint128_t *dst)
+{
+       memcpy(dst, src, sizeof(uint128_t));
+}
+
+static inline void btoh128(const uint128_t *src, uint128_t *dst)
+{
+       int i;
+
+       for (i = 0; i < 16; i++)
+               dst->data[15 - i] = src->data[i];
+}
+
+#else
+
+static inline uint64_t ntoh64(uint64_t n)
+{
+       uint64_t h;
+       uint64_t tmp = ntohl(n & 0x00000000ffffffff);
+
+       h = ntohl(n >> 32);
+       h |= tmp << 32;
+
+       return h;
+}
+
+static inline void ntoh128(const uint128_t *src, uint128_t *dst)
+{
+       int i;
+
+       for (i = 0; i < 16; i++)
+               dst->data[15 - i] = src->data[i];
+}
+
+static inline void btoh128(const uint128_t *src, uint128_t *dst)
+{
+       memcpy(dst, src, sizeof(uint128_t));
+}
+
+#endif
+
+#define hton64(x)     ntoh64(x)
+#define hton128(x, y) ntoh128(x, y)
+#define htob128(x, y) btoh128(x, y)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BLUETOOTH_H */
diff --git a/lib/bnep.h b/lib/bnep.h
new file mode 100644 (file)
index 0000000..2bbfb17
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __BNEP_H
+#define __BNEP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bluetooth/bluetooth.h>
+
+#ifndef ETH_ALEN
+#define ETH_ALEN       6               /* from <net/ethernet.h> */
+#endif
+
+/* BNEP UUIDs */
+#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
+#define BNEP_UUID16    0x02
+#define BNEP_UUID32    0x04
+#define BNEP_UUID128   0x16
+
+#define BNEP_SVC_PANU  0x1115
+#define BNEP_SVC_NAP   0x1116
+#define BNEP_SVC_GN    0x1117
+
+/* BNEP packet types */
+#define BNEP_GENERAL               0x00
+#define BNEP_CONTROL               0x01
+#define BNEP_COMPRESSED            0x02
+#define BNEP_COMPRESSED_SRC_ONLY   0x03
+#define BNEP_COMPRESSED_DST_ONLY   0x04
+
+/* BNEP control types */
+#define BNEP_CMD_NOT_UNDERSTOOD    0x00
+#define BNEP_SETUP_CONN_REQ        0x01
+#define BNEP_SETUP_CONN_RSP        0x02
+#define BNEP_FILTER_NET_TYPE_SET   0x03
+#define BNEP_FILTER_NET_TYPE_RSP   0x04
+#define BNEP_FILTER_MULT_ADDR_SET  0x05
+#define BNEP_FILTER_MULT_ADDR_RSP  0x06
+
+/* BNEP response messages */
+#define BNEP_SUCCESS               0x00
+
+#define BNEP_CONN_INVALID_DST      0x01
+#define BNEP_CONN_INVALID_SRC      0x02
+#define BNEP_CONN_INVALID_SVC      0x03
+#define BNEP_CONN_NOT_ALLOWED      0x04
+
+#define BNEP_FILTER_UNSUPPORTED_REQ    0x01
+#define BNEP_FILTER_INVALID_RANGE      0x02
+#define BNEP_FILTER_INVALID_MCADDR     0x02
+#define BNEP_FILTER_LIMIT_REACHED      0x03
+#define BNEP_FILTER_DENIED_SECURITY    0x04
+
+/* L2CAP settings */
+#define BNEP_MTU         1691
+#define BNEP_FLUSH_TO    0xffff
+#define BNEP_CONNECT_TO  15
+#define BNEP_FILTER_TO   15
+
+#ifndef BNEP_PSM
+#define BNEP_PSM        0x0f
+#endif
+
+/* BNEP headers */
+#define BNEP_TYPE_MASK  0x7f
+#define BNEP_EXT_HEADER         0x80
+
+struct bnep_setup_conn_req {
+       uint8_t  type;
+       uint8_t  ctrl;
+       uint8_t  uuid_size;
+       uint8_t  service[0];
+} __attribute__((packed));
+
+struct bnep_set_filter_req {
+       uint8_t  type;
+       uint8_t  ctrl;
+       uint16_t len;
+       uint8_t  list[0];
+} __attribute__((packed));
+
+struct bnep_control_rsp {
+       uint8_t  type;
+       uint8_t  ctrl;
+       uint16_t resp;
+} __attribute__((packed));
+
+struct bnep_ext_hdr {
+       uint8_t  type;
+       uint8_t  len;
+       uint8_t  data[0];
+} __attribute__((packed));
+
+/* BNEP ioctl defines */
+#define BNEPCONNADD    _IOW('B', 200, int)
+#define BNEPCONNDEL    _IOW('B', 201, int)
+#define BNEPGETCONNLIST        _IOR('B', 210, int)
+#define BNEPGETCONNINFO        _IOR('B', 211, int)
+
+struct bnep_connadd_req {
+       int      sock;          /* Connected socket */
+       uint32_t flags;
+       uint16_t role;
+       char     device[16];    /* Name of the Ethernet device */
+};
+
+struct bnep_conndel_req {
+       uint32_t flags;
+       uint8_t  dst[ETH_ALEN];
+};
+
+struct bnep_conninfo {
+       uint32_t flags;
+       uint16_t role;
+       uint16_t state;
+       uint8_t  dst[ETH_ALEN];
+       char     device[16];
+};
+
+struct bnep_connlist_req {
+       uint32_t cnum;
+       struct bnep_conninfo *ci;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BNEP_H */
diff --git a/lib/cmtp.h b/lib/cmtp.h
new file mode 100644 (file)
index 0000000..ce937bd
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CMTP_H
+#define __CMTP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* CMTP defaults */
+#define CMTP_MINIMUM_MTU 152
+#define CMTP_DEFAULT_MTU 672
+
+/* CMTP ioctl defines */
+#define CMTPCONNADD    _IOW('C', 200, int)
+#define CMTPCONNDEL    _IOW('C', 201, int)
+#define CMTPGETCONNLIST        _IOR('C', 210, int)
+#define CMTPGETCONNINFO        _IOR('C', 211, int)
+
+#define CMTP_LOOPBACK  0
+
+struct cmtp_connadd_req {
+       int sock;       /* Connected socket */
+       uint32_t flags;
+};
+
+struct cmtp_conndel_req {
+       bdaddr_t bdaddr;
+       uint32_t flags;
+};
+
+struct cmtp_conninfo {
+       bdaddr_t bdaddr;
+       uint32_t flags;
+       uint16_t state;
+       int      num;
+};
+
+struct cmtp_connlist_req {
+       uint32_t cnum;
+       struct cmtp_conninfo *ci;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CMTP_H */
diff --git a/lib/hci.c b/lib/hci.c
new file mode 100644 (file)
index 0000000..66b2d5f
--- /dev/null
+++ b/lib/hci.c
@@ -0,0 +1,2913 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "bluetooth.h"
+#include "hci.h"
+#include "hci_lib.h"
+
+#ifndef MIN
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+typedef struct {
+       char *str;
+       unsigned int val;
+} hci_map;
+
+static char *hci_bit2str(hci_map *m, unsigned int val)
+{
+       char *str = malloc(120);
+       char *ptr = str;
+
+       if (!str)
+               return NULL;
+
+       *ptr = 0;
+       while (m->str) {
+               if ((unsigned int) m->val & val)
+                       ptr += sprintf(ptr, "%s ", m->str);
+               m++;
+       }
+       return str;
+}
+
+static int hci_str2bit(hci_map *map, char *str, unsigned int *val)
+{
+       char *t, *ptr;
+       hci_map *m;
+       int set;
+
+       if (!str || !(str = ptr = strdup(str)))
+               return 0;
+
+       *val = set = 0;
+
+       while ((t = strsep(&ptr, ","))) {
+               for (m = map; m->str; m++) {
+                       if (!strcasecmp(m->str, t)) {
+                               *val |= (unsigned int) m->val;
+                               set = 1;
+                       }
+               }
+       }
+       free(str);
+
+       return set;
+}
+
+static char *hci_uint2str(hci_map *m, unsigned int val)
+{
+       char *str = malloc(50);
+       char *ptr = str;
+
+       if (!str)
+               return NULL;
+
+       *ptr = 0;
+       while (m->str) {
+               if ((unsigned int) m->val == val) {
+                       ptr += sprintf(ptr, "%s", m->str);
+                       break;
+               }
+               m++;
+       }
+       return str;
+}
+
+static int hci_str2uint(hci_map *map, char *str, unsigned int *val)
+{
+       char *t, *ptr;
+       hci_map *m;
+       int set = 0;
+
+       if (!str)
+               return 0;
+
+       str = ptr = strdup(str);
+
+       while ((t = strsep(&ptr, ","))) {
+               for (m = map; m->str; m++) {
+                       if (!strcasecmp(m->str,t)) {
+                               *val = (unsigned int) m->val;
+                               set = 1;
+                               break;
+                       }
+               }
+       }
+       free(str);
+
+       return set;
+}
+
+char *hci_bustostr(int bus)
+{
+       switch (bus) {
+       case HCI_VIRTUAL:
+               return "VIRTUAL";
+       case HCI_USB:
+               return "USB";
+       case HCI_PCCARD:
+               return "PCCARD";
+       case HCI_UART:
+               return "UART";
+       case HCI_RS232:
+               return "RS232";
+       case HCI_PCI:
+               return "PCI";
+       case HCI_SDIO:
+               return "SDIO";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+char *hci_dtypetostr(int type)
+{
+       return hci_bustostr(type & 0x0f);
+}
+
+char *hci_typetostr(int type)
+{
+       switch (type) {
+       case HCI_BREDR:
+               return "BR/EDR";
+       case HCI_AMP:
+               return "AMP";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+/* HCI dev flags mapping */
+static hci_map dev_flags_map[] = {
+       { "UP",      HCI_UP      },
+       { "INIT",    HCI_INIT    },
+       { "RUNNING", HCI_RUNNING },
+       { "RAW",     HCI_RAW     },
+       { "PSCAN",   HCI_PSCAN   },
+       { "ISCAN",   HCI_ISCAN   },
+       { "INQUIRY", HCI_INQUIRY },
+       { "AUTH",    HCI_AUTH    },
+       { "ENCRYPT", HCI_ENCRYPT },
+       { NULL }
+};
+
+char *hci_dflagstostr(uint32_t flags)
+{
+       char *str = bt_malloc(50);
+       char *ptr = str;
+       hci_map *m = dev_flags_map;
+
+       if (!str)
+               return NULL;
+
+       *ptr = 0;
+
+       if (!hci_test_bit(HCI_UP, &flags))
+               ptr += sprintf(ptr, "DOWN ");
+
+       while (m->str) {
+               if (hci_test_bit(m->val, &flags))
+                       ptr += sprintf(ptr, "%s ", m->str);
+               m++;
+       }
+       return str;
+}
+
+/* HCI packet type mapping */
+static hci_map pkt_type_map[] = {
+       { "DM1",   HCI_DM1  },
+       { "DM3",   HCI_DM3  },
+       { "DM5",   HCI_DM5  },
+       { "DH1",   HCI_DH1  },
+       { "DH3",   HCI_DH3  },
+       { "DH5",   HCI_DH5  },
+       { "HV1",   HCI_HV1  },
+       { "HV2",   HCI_HV2  },
+       { "HV3",   HCI_HV3  },
+       { "2-DH1", HCI_2DH1 },
+       { "2-DH3", HCI_2DH3 },
+       { "2-DH5", HCI_2DH5 },
+       { "3-DH1", HCI_3DH1 },
+       { "3-DH3", HCI_3DH3 },
+       { "3-DH5", HCI_3DH5 },
+       { NULL }
+};
+
+static hci_map sco_ptype_map[] = {
+       { "HV1",   0x0001   },
+       { "HV2",   0x0002   },
+       { "HV3",   0x0004   },
+       { "EV3",   HCI_EV3  },
+       { "EV4",   HCI_EV4  },
+       { "EV5",   HCI_EV5  },
+       { "2-EV3", HCI_2EV3 },
+       { "2-EV5", HCI_2EV5 },
+       { "3-EV3", HCI_3EV3 },
+       { "3-EV5", HCI_3EV5 },
+       { NULL }
+};
+
+char *hci_ptypetostr(unsigned int ptype)
+{
+       return hci_bit2str(pkt_type_map, ptype);
+}
+
+int hci_strtoptype(char *str, unsigned int *val)
+{
+       return hci_str2bit(pkt_type_map, str, val);
+}
+
+char *hci_scoptypetostr(unsigned int ptype)
+{
+       return hci_bit2str(sco_ptype_map, ptype);
+}
+
+int hci_strtoscoptype(char *str, unsigned int *val)
+{
+       return hci_str2bit(sco_ptype_map, str, val);
+}
+
+/* Link policy mapping */
+static hci_map link_policy_map[] = {
+       { "NONE",       0               },
+       { "RSWITCH",    HCI_LP_RSWITCH  },
+       { "HOLD",       HCI_LP_HOLD     },
+       { "SNIFF",      HCI_LP_SNIFF    },
+       { "PARK",       HCI_LP_PARK     },
+       { NULL }
+};
+
+char *hci_lptostr(unsigned int lp)
+{
+       return hci_bit2str(link_policy_map, lp);
+}
+
+int hci_strtolp(char *str, unsigned int *val)
+{
+       return hci_str2bit(link_policy_map, str, val);
+}
+
+/* Link mode mapping */
+static hci_map link_mode_map[] = {
+       { "NONE",       0               },
+       { "ACCEPT",     HCI_LM_ACCEPT   },
+       { "MASTER",     HCI_LM_MASTER   },
+       { "AUTH",       HCI_LM_AUTH     },
+       { "ENCRYPT",    HCI_LM_ENCRYPT  },
+       { "TRUSTED",    HCI_LM_TRUSTED  },
+       { "RELIABLE",   HCI_LM_RELIABLE },
+       { "SECURE",     HCI_LM_SECURE   },
+       { NULL }
+};
+
+char *hci_lmtostr(unsigned int lm)
+{
+       char *s, *str = bt_malloc(50);
+       if (!str)
+               return NULL;
+
+       *str = 0;
+       if (!(lm & HCI_LM_MASTER))
+               strcpy(str, "SLAVE ");
+
+       s = hci_bit2str(link_mode_map, lm);
+       if (!s) {
+               bt_free(str);
+               return NULL;
+       }
+
+       strcat(str, s);
+       free(s);
+       return str;
+}
+
+int hci_strtolm(char *str, unsigned int *val)
+{
+       return hci_str2bit(link_mode_map, str, val);
+}
+
+/* Command mapping */
+static hci_map commands_map[] = {
+       { "Inquiry",                                    0   },
+       { "Inquiry Cancel",                             1   },
+       { "Periodic Inquiry Mode",                      2   },
+       { "Exit Periodic Inquiry Mode",                 3   },
+       { "Create Connection",                          4   },
+       { "Disconnect",                                 5   },
+       { "Add SCO Connection",                         6   },
+       { "Cancel Create Connection",                   7   },
+
+       { "Accept Connection Request",                  8   },
+       { "Reject Connection Request",                  9   },
+       { "Link Key Request Reply",                     10  },
+       { "Link Key Request Negative Reply",            11  },
+       { "PIN Code Request Reply",                     12  },
+       { "PIN Code Request Negative Reply",            13  },
+       { "Change Connection Packet Type",              14  },
+       { "Authentication Requested",                   15  },
+
+       { "Set Connection Encryption",                  16  },
+       { "Change Connection Link Key",                 17  },
+       { "Master Link Key",                            18  },
+       { "Remote Name Request",                        19  },
+       { "Cancel Remote Name Request",                 20  },
+       { "Read Remote Supported Features",             21  },
+       { "Read Remote Extended Features",              22  },
+       { "Read Remote Version Information",            23  },
+
+       { "Read Clock Offset",                          24  },
+       { "Read LMP Handle",                            25  },
+       { "Reserved",                                   26  },
+       { "Reserved",                                   27  },
+       { "Reserved",                                   28  },
+       { "Reserved",                                   29  },
+       { "Reserved",                                   30  },
+       { "Reserved",                                   31  },
+
+       { "Reserved",                                   32  },
+       { "Hold Mode",                                  33  },
+       { "Sniff Mode",                                 34  },
+       { "Exit Sniff Mode",                            35  },
+       { "Park State",                                 36  },
+       { "Exit Park State",                            37  },
+       { "QoS Setup",                                  38  },
+       { "Role Discovery",                             39  },
+
+       { "Switch Role",                                40  },
+       { "Read Link Policy Settings",                  41  },
+       { "Write Link Policy Settings",                 42  },
+       { "Read Default Link Policy Settings",          43  },
+       { "Write Default Link Policy Settings",         44  },
+       { "Flow Specification",                         45  },
+       { "Set Event Mask",                             46  },
+       { "Reset",                                      47  },
+
+       { "Set Event Filter",                           48  },
+       { "Flush",                                      49  },
+       { "Read PIN Type",                              50  },
+       { "Write PIN Type",                             51  },
+       { "Create New Unit Key",                        52  },
+       { "Read Stored Link Key",                       53  },
+       { "Write Stored Link Key",                      54  },
+       { "Delete Stored Link Key",                     55  },
+
+       { "Write Local Name",                           56  },
+       { "Read Local Name",                            57  },
+       { "Read Connection Accept Timeout",             58  },
+       { "Write Connection Accept Timeout",            59  },
+       { "Read Page Timeout",                          60  },
+       { "Write Page Timeout",                         61  },
+       { "Read Scan Enable",                           62  },
+       { "Write Scan Enable",                          63  },
+
+       { "Read Page Scan Activity",                    64  },
+       { "Write Page Scan Activity",                   65  },
+       { "Read Inquiry Scan Activity",                 66  },
+       { "Write Inquiry Scan Activity",                67  },
+       { "Read Authentication Enable",                 68  },
+       { "Write Authentication Enable",                69  },
+       { "Read Encryption Mode",                       70  },
+       { "Write Encryption Mode",                      71  },
+
+       { "Read Class Of Device",                       72  },
+       { "Write Class Of Device",                      73  },
+       { "Read Voice Setting",                         74  },
+       { "Write Voice Setting",                        75  },
+       { "Read Automatic Flush Timeout",               76  },
+       { "Write Automatic Flush Timeout",              77  },
+       { "Read Num Broadcast Retransmissions",         78  },
+       { "Write Num Broadcast Retransmissions",        79  },
+
+       { "Read Hold Mode Activity",                    80  },
+       { "Write Hold Mode Activity",                   81  },
+       { "Read Transmit Power Level",                  82  },
+       { "Read Synchronous Flow Control Enable",       83  },
+       { "Write Synchronous Flow Control Enable",      84  },
+       { "Set Host Controller To Host Flow Control",   85  },
+       { "Host Buffer Size",                           86  },
+       { "Host Number Of Completed Packets",           87  },
+
+       { "Read Link Supervision Timeout",              88  },
+       { "Write Link Supervision Timeout",             89  },
+       { "Read Number of Supported IAC",               90  },
+       { "Read Current IAC LAP",                       91  },
+       { "Write Current IAC LAP",                      92  },
+       { "Read Page Scan Period Mode",                 93  },
+       { "Write Page Scan Period Mode",                94  },
+       { "Read Page Scan Mode",                        95  },
+
+       { "Write Page Scan Mode",                       96  },
+       { "Set AFH Channel Classification",             97  },
+       { "Reserved",                                   98  },
+       { "Reserved",                                   99  },
+       { "Read Inquiry Scan Type",                     100 },
+       { "Write Inquiry Scan Type",                    101 },
+       { "Read Inquiry Mode",                          102 },
+       { "Write Inquiry Mode",                         103 },
+
+       { "Read Page Scan Type",                        104 },
+       { "Write Page Scan Type",                       105 },
+       { "Read AFH Channel Assessment Mode",           106 },
+       { "Write AFH Channel Assessment Mode",          107 },
+       { "Reserved",                                   108 },
+       { "Reserved",                                   109 },
+       { "Reserved",                                   110 },
+       { "Reserved",                                   111 },
+
+       { "Reserved",                                   112 },
+       { "Reserved",                                   113 },
+       { "Reserved",                                   114 },
+       { "Read Local Version Information",             115 },
+       { "Read Local Supported Commands",              116 },
+       { "Read Local Supported Features",              117 },
+       { "Read Local Extended Features",               118 },
+       { "Read Buffer Size",                           119 },
+
+       { "Read Country Code",                          120 },
+       { "Read BD ADDR",                               121 },
+       { "Read Failed Contact Counter",                122 },
+       { "Reset Failed Contact Counter",               123 },
+       { "Get Link Quality",                           124 },
+       { "Read RSSI",                                  125 },
+       { "Read AFH Channel Map",                       126 },
+       { "Read BD Clock",                              127 },
+
+       { "Read Loopback Mode",                         128 },
+       { "Write Loopback Mode",                        129 },
+       { "Enable Device Under Test Mode",              130 },
+       { "Setup Synchronous Connection",               131 },
+       { "Accept Synchronous Connection",              132 },
+       { "Reject Synchronous Connection",              133 },
+       { "Reserved",                                   134 },
+       { "Reserved",                                   135 },
+
+       { "Read Extended Inquiry Response",             136 },
+       { "Write Extended Inquiry Response",            137 },
+       { "Refresh Encryption Key",                     138 },
+       { "Reserved",                                   139 },
+       { "Sniff Subrating",                            140 },
+       { "Read Simple Pairing Mode",                   141 },
+       { "Write Simple Pairing Mode",                  142 },
+       { "Read Local OOB Data",                        143 },
+
+       { "Read Inquiry Response Transmit Power Level", 144 },
+       { "Write Inquiry Transmit Power Level",         145 },
+       { "Read Default Erroneous Data Reporting",      146 },
+       { "Write Default Erroneous Data Reporting",     147 },
+       { "Reserved",                                   148 },
+       { "Reserved",                                   149 },
+       { "Reserved",                                   150 },
+       { "IO Capability Request Reply",                151 },
+
+       { "User Confirmation Request Reply",            152 },
+       { "User Confirmation Request Negative Reply",   153 },
+       { "User Passkey Request Reply",                 154 },
+       { "User Passkey Request Negative Reply",        155 },
+       { "Remote OOB Data Request Reply",              156 },
+       { "Write Simple Pairing Debug Mode",            157 },
+       { "Enhanced Flush",                             158 },
+       { "Remote OOB Data Request Negative Reply",     159 },
+
+       { "Reserved",                                   160 },
+       { "Reserved",                                   161 },
+       { "Send Keypress Notification",                 162 },
+       { "IO Capability Request Negative Reply",       163 },
+       { "Read Encryption Key Size",                   164 },
+       { "Reserved",                                   165 },
+       { "Reserved",                                   166 },
+       { "Reserved",                                   167 },
+
+       { "Create Physical Link",                       168 },
+       { "Accept Physical Link",                       169 },
+       { "Disconnect Physical Link",                   170 },
+       { "Create Logical Link",                        171 },
+       { "Accept Logical Link",                        172 },
+       { "Disconnect Logical Link",                    173 },
+       { "Logical Link Cancel",                        174 },
+       { "Flow Specification Modify",                  175 },
+
+       { "Read Logical Link Accept Timeout",           176 },
+       { "Write Logical Link Accept Timeout",          177 },
+       { "Set Event Mask Page 2",                      178 },
+       { "Read Location Data",                         179 },
+       { "Write Location Data",                        180 },
+       { "Read Local AMP Info",                        181 },
+       { "Read Local AMP_ASSOC",                       182 },
+       { "Write Remote AMP_ASSOC",                     183 },
+
+       { "Read Flow Control Mode",                     184 },
+       { "Write Flow Control Mode",                    185 },
+       { "Read Data Block Size",                       186 },
+       { "Reserved",                                   187 },
+       { "Reserved",                                   188 },
+       { "Enable AMP Receiver Reports",                189 },
+       { "AMP Test End",                               190 },
+       { "AMP Test Command",                           191 },
+
+       { "Read Enhanced Transmit Power Level",         192 },
+       { "Reserved",                                   193 },
+       { "Read Best Effort Flush Timeout",             194 },
+       { "Write Best Effort Flush Timeout",            195 },
+       { "Short Range Mode",                           196 },
+       { "Read LE Host Support",                       197 },
+       { "Write LE Host Support",                      198 },
+       { "Reserved",                                   199 },
+
+       { "LE Set Event Mask",                          200 },
+       { "LE Read Buffer Size",                        201 },
+       { "LE Read Local Supported Features",           202 },
+       { "Reserved",                                   203 },
+       { "LE Set Random Address",                      204 },
+       { "LE Set Advertising Parameters",              205 },
+       { "LE Read Advertising Channel TX Power",       206 },
+       { "LE Set Advertising Data",                    207 },
+
+       { "LE Set Scan Response Data",                  208 },
+       { "LE Set Advertise Enable",                    209 },
+       { "LE Set Scan Parameters",                     210 },
+       { "LE Set Scan Enable",                         211 },
+       { "LE Create Connection",                       212 },
+       { "LE Create Connection Cancel",                213 },
+       { "LE Read White List Size",                    214 },
+       { "LE Clear White List",                        215 },
+
+       { "LE Add Device To White List",                216 },
+       { "LE Remove Device From White List",           217 },
+       { "LE Connection Update",                       218 },
+       { "LE Set Host Channel Classification",         219 },
+       { "LE Read Channel Map",                        220 },
+       { "LE Read Remote Used Features",               221 },
+       { "LE Encrypt",                                 222 },
+       { "LE Rand",                                    223 },
+
+       { "LE Start Encryption",                        224 },
+       { "LE Long Term Key Request Reply",             225 },
+       { "LE Long Term Key Request Negative Reply",    226 },
+       { "LE Read Supported States",                   227 },
+       { "LE Receiver Test",                           228 },
+       { "LE Transmitter Test",                        229 },
+       { "LE Test End",                                230 },
+       { "Reserved",                                   231 },
+
+       { NULL }
+};
+
+char *hci_cmdtostr(unsigned int cmd)
+{
+       return hci_uint2str(commands_map, cmd);
+}
+
+char *hci_commandstostr(uint8_t *commands, char *pref, int width)
+{
+       unsigned int maxwidth = width - 3;
+       hci_map *m;
+       char *off, *ptr, *str;
+       int size = 10;
+
+       m = commands_map;
+
+       while (m->str) {
+               if (commands[m->val / 8] & (1 << (m->val % 8)))
+                       size += strlen(m->str) + (pref ? strlen(pref) : 0) + 3;
+               m++;
+       }
+
+       str = bt_malloc(size);
+       if (!str)
+               return NULL;
+
+       ptr = str; *ptr = '\0';
+
+       if (pref)
+               ptr += sprintf(ptr, "%s", pref);
+
+       off = ptr;
+
+       m = commands_map;
+
+       while (m->str) {
+               if (commands[m->val / 8] & (1 << (m->val % 8))) {
+                       if (strlen(off) + strlen(m->str) > maxwidth) {
+                               ptr += sprintf(ptr, "\n%s", pref ? pref : "");
+                               off = ptr;
+                       }
+                       ptr += sprintf(ptr, "'%s' ", m->str);
+               }
+               m++;
+       }
+
+       return str;
+}
+
+/* Version mapping */
+static hci_map ver_map[] = {
+       { "1.0b",       0x00 },
+       { "1.1",        0x01 },
+       { "1.2",        0x02 },
+       { "2.0",        0x03 },
+       { "2.1",        0x04 },
+       { "3.0",        0x05 },
+       { "4.0",        0x06 },
+       { NULL }
+};
+
+char *hci_vertostr(unsigned int ver)
+{
+       return hci_uint2str(ver_map, ver);
+}
+
+int hci_strtover(char *str, unsigned int *ver)
+{
+       return hci_str2uint(ver_map, str, ver);
+}
+
+char *lmp_vertostr(unsigned int ver)
+{
+       return hci_uint2str(ver_map, ver);
+}
+
+int lmp_strtover(char *str, unsigned int *ver)
+{
+       return hci_str2uint(ver_map, str, ver);
+}
+
+/* LMP features mapping */
+static hci_map lmp_features_map[8][9] = {
+       {       /* Byte 0 */
+               { "<3-slot packets>",   LMP_3SLOT       },      /* Bit 0 */
+               { "<5-slot packets>",   LMP_5SLOT       },      /* Bit 1 */
+               { "<encryption>",       LMP_ENCRYPT     },      /* Bit 2 */
+               { "<slot offset>",      LMP_SOFFSET     },      /* Bit 3 */
+               { "<timing accuracy>",  LMP_TACCURACY   },      /* Bit 4 */
+               { "<role switch>",      LMP_RSWITCH     },      /* Bit 5 */
+               { "<hold mode>",        LMP_HOLD        },      /* Bit 6 */
+               { "<sniff mode>",       LMP_SNIFF       },      /* Bit 7 */
+               { NULL }
+       },
+       {       /* Byte 1 */
+               { "<park state>",       LMP_PARK        },      /* Bit 0 */
+               { "<RSSI>",             LMP_RSSI        },      /* Bit 1 */
+               { "<channel quality>",  LMP_QUALITY     },      /* Bit 2 */
+               { "<SCO link>",         LMP_SCO         },      /* Bit 3 */
+               { "<HV2 packets>",      LMP_HV2         },      /* Bit 4 */
+               { "<HV3 packets>",      LMP_HV3         },      /* Bit 5 */
+               { "<u-law log>",        LMP_ULAW        },      /* Bit 6 */
+               { "<A-law log>",        LMP_ALAW        },      /* Bit 7 */
+               { NULL }
+       },
+       {       /* Byte 2 */
+               { "<CVSD>",             LMP_CVSD        },      /* Bit 0 */
+               { "<paging scheme>",    LMP_PSCHEME     },      /* Bit 1 */
+               { "<power control>",    LMP_PCONTROL    },      /* Bit 2 */
+               { "<transparent SCO>",  LMP_TRSP_SCO    },      /* Bit 3 */
+               { "<broadcast encrypt>",LMP_BCAST_ENC   },      /* Bit 7 */
+               { NULL }
+       },
+       {       /* Byte 3 */
+               { "<no. 24>",           0x01            },      /* Bit 0 */
+               { "<EDR ACL 2 Mbps>",   LMP_EDR_ACL_2M  },      /* Bit 1 */
+               { "<EDR ACL 3 Mbps>",   LMP_EDR_ACL_3M  },      /* Bit 2 */
+               { "<enhanced iscan>",   LMP_ENH_ISCAN   },      /* Bit 3 */
+               { "<interlaced iscan>", LMP_ILACE_ISCAN },      /* Bit 4 */
+               { "<interlaced pscan>", LMP_ILACE_PSCAN },      /* Bit 5 */
+               { "<inquiry with RSSI>",LMP_RSSI_INQ    },      /* Bit 6 */
+               { "<extended SCO>",     LMP_ESCO        },      /* Bit 7 */
+               { NULL }
+       },
+       {       /* Byte 4 */
+               { "<EV4 packets>",      LMP_EV4         },      /* Bit 0 */
+               { "<EV5 packets>",      LMP_EV5         },      /* Bit 1 */
+               { "<no. 34>",           0x04            },      /* Bit 2 */
+               { "<AFH cap. slave>",   LMP_AFH_CAP_SLV },      /* Bit 3 */
+               { "<AFH class. slave>", LMP_AFH_CLS_SLV },      /* Bit 4 */
+               { "<BR/EDR not supp.>", LMP_NO_BREDR    },      /* Bit 5 */
+               { "<LE support>",       LMP_LE          },      /* Bit 6 */
+               { "<3-slot EDR ACL>",   LMP_EDR_3SLOT   },      /* Bit 7 */
+               { NULL }
+       },
+       {       /* Byte 5 */
+               { "<5-slot EDR ACL>",   LMP_EDR_5SLOT   },      /* Bit 0 */
+               { "<sniff subrating>",  LMP_SNIFF_SUBR  },      /* Bit 1 */
+               { "<pause encryption>", LMP_PAUSE_ENC   },      /* Bit 2 */
+               { "<AFH cap. master>",  LMP_AFH_CAP_MST },      /* Bit 3 */
+               { "<AFH class. master>",LMP_AFH_CLS_MST },      /* Bit 4 */
+               { "<EDR eSCO 2 Mbps>",  LMP_EDR_ESCO_2M },      /* Bit 5 */
+               { "<EDR eSCO 3 Mbps>",  LMP_EDR_ESCO_3M },      /* Bit 6 */
+               { "<3-slot EDR eSCO>",  LMP_EDR_3S_ESCO },      /* Bit 7 */
+               { NULL }
+       },
+       {       /* Byte 6 */
+               { "<extended inquiry>", LMP_EXT_INQ     },      /* Bit 0 */
+               { "<LE and BR/EDR>",    LMP_LE_BREDR    },      /* Bit 1 */
+               { "<no. 50>",           0x04            },      /* Bit 2 */
+               { "<simple pairing>",   LMP_SIMPLE_PAIR },      /* Bit 3 */
+               { "<encapsulated PDU>", LMP_ENCAPS_PDU  },      /* Bit 4 */
+               { "<err. data report>", LMP_ERR_DAT_REP },      /* Bit 5 */
+               { "<non-flush flag>",   LMP_NFLUSH_PKTS },      /* Bit 6 */
+               { "<no. 55>",           0x80            },      /* Bit 7 */
+               { NULL }
+       },
+       {       /* Byte 7 */
+               { "<LSTO>",             LMP_LSTO        },      /* Bit 1 */
+               { "<inquiry TX power>", LMP_INQ_TX_PWR  },      /* Bit 1 */
+               { "<EPC>",              LMP_EPC         },      /* Bit 2 */
+               { "<no. 59>",           0x08            },      /* Bit 3 */
+               { "<no. 60>",           0x10            },      /* Bit 4 */
+               { "<no. 61>",           0x20            },      /* Bit 5 */
+               { "<no. 62>",           0x40            },      /* Bit 6 */
+               { "<extended features>",LMP_EXT_FEAT    },      /* Bit 7 */
+               { NULL }
+       },
+};
+
+char *lmp_featurestostr(uint8_t *features, char *pref, int width)
+{
+       unsigned int maxwidth = width - 1;
+       char *off, *ptr, *str;
+       int i, size = 10;
+
+       for (i = 0; i < 8; i++) {
+               hci_map *m = lmp_features_map[i];
+
+               while (m->str) {
+                       if (m->val & features[i])
+                               size += strlen(m->str) +
+                                               (pref ? strlen(pref) : 0) + 1;
+                       m++;
+               }
+       }
+
+       str = bt_malloc(size);
+       if (!str)
+               return NULL;
+
+       ptr = str; *ptr = '\0';
+
+       if (pref)
+               ptr += sprintf(ptr, "%s", pref);
+
+       off = ptr;
+
+       for (i = 0; i < 8; i++) {
+               hci_map *m = lmp_features_map[i];
+
+               while (m->str) {
+                       if (m->val & features[i]) {
+                               if (strlen(off) + strlen(m->str) > maxwidth) {
+                                       ptr += sprintf(ptr, "\n%s",
+                                                       pref ? pref : "");
+                                       off = ptr;
+                               }
+                               ptr += sprintf(ptr, "%s ", m->str);
+                       }
+                       m++;
+               }
+       }
+
+       return str;
+}
+
+/* HCI functions that do not require open device */
+int hci_for_each_dev(int flag, int (*func)(int dd, int dev_id, long arg),
+                       long arg)
+{
+       struct hci_dev_list_req *dl;
+       struct hci_dev_req *dr;
+       int dev_id = -1;
+       int i, sk, err = 0;
+
+       sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       if (sk < 0)
+               return -1;
+
+       dl = malloc(HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl));
+       if (!dl) {
+               err = errno;
+               goto done;
+       }
+
+       memset(dl, 0, HCI_MAX_DEV * sizeof(*dr) + sizeof(*dl));
+
+       dl->dev_num = HCI_MAX_DEV;
+       dr = dl->dev_req;
+
+       if (ioctl(sk, HCIGETDEVLIST, (void *) dl) < 0) {
+               err = errno;
+               goto free;
+       }
+
+       for (i = 0; i < dl->dev_num; i++, dr++) {
+               if (hci_test_bit(flag, &dr->dev_opt))
+                       if (!func || func(sk, dr->dev_id, arg)) {
+                               dev_id = dr->dev_id;
+                               break;
+                       }
+       }
+
+       if (dev_id < 0)
+               err = ENODEV;
+
+free:
+       free(dl);
+
+done:
+       close(sk);
+       errno = err;
+
+       return dev_id;
+}
+
+static int __other_bdaddr(int dd, int dev_id, long arg)
+{
+       struct hci_dev_info di = { .dev_id = dev_id };
+
+       if (ioctl(dd, HCIGETDEVINFO, (void *) &di))
+               return 0;
+
+       if (hci_test_bit(HCI_RAW, &di.flags))
+               return 0;
+
+       return bacmp((bdaddr_t *) arg, &di.bdaddr);
+}
+
+static int __same_bdaddr(int dd, int dev_id, long arg)
+{
+       struct hci_dev_info di = { .dev_id = dev_id };
+
+       if (ioctl(dd, HCIGETDEVINFO, (void *) &di))
+               return 0;
+
+       return !bacmp((bdaddr_t *) arg, &di.bdaddr);
+}
+
+int hci_get_route(bdaddr_t *bdaddr)
+{
+       return hci_for_each_dev(HCI_UP, __other_bdaddr,
+                               (long) (bdaddr ? bdaddr : BDADDR_ANY));
+}
+
+int hci_devid(const char *str)
+{
+       bdaddr_t ba;
+       int id = -1;
+
+       if (!strncmp(str, "hci", 3) && strlen(str) >= 4) {
+               id = atoi(str + 3);
+               if (hci_devba(id, &ba) < 0)
+                       return -1;
+       } else {
+               errno = ENODEV;
+               str2ba(str, &ba);
+               id = hci_for_each_dev(HCI_UP, __same_bdaddr, (long) &ba);
+       }
+
+       return id;
+}
+
+int hci_devinfo(int dev_id, struct hci_dev_info *di)
+{
+       int dd, err, ret;
+
+       dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       if (dd < 0)
+               return dd;
+
+       memset(di, 0, sizeof(struct hci_dev_info));
+
+       di->dev_id = dev_id;
+       ret = ioctl(dd, HCIGETDEVINFO, (void *) di);
+
+       err = errno;
+       close(dd);
+       errno = err;
+
+       return ret;
+}
+
+int hci_devba(int dev_id, bdaddr_t *bdaddr)
+{
+       struct hci_dev_info di;
+
+       memset(&di, 0, sizeof(di));
+
+       if (hci_devinfo(dev_id, &di))
+               return -1;
+
+       if (!hci_test_bit(HCI_UP, &di.flags)) {
+               errno = ENETDOWN;
+               return -1;
+       }
+
+       bacpy(bdaddr, &di.bdaddr);
+
+       return 0;
+}
+
+int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap,
+               inquiry_info **ii, long flags)
+{
+       struct hci_inquiry_req *ir;
+       uint8_t num_rsp = nrsp;
+       void *buf;
+       int dd, size, err, ret = -1;
+
+       if (nrsp <= 0) {
+               num_rsp = 0;
+               nrsp = 255;
+       }
+
+       if (dev_id < 0) {
+               dev_id = hci_get_route(NULL);
+               if (dev_id < 0) {
+                       errno = ENODEV;
+                       return -1;
+               }
+       }
+
+       dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       if (dd < 0)
+               return dd;
+
+       buf = malloc(sizeof(*ir) + (sizeof(inquiry_info) * (nrsp)));
+       if (!buf)
+               goto done;
+
+       ir = buf;
+       ir->dev_id  = dev_id;
+       ir->num_rsp = num_rsp;
+       ir->length  = len;
+       ir->flags   = flags;
+
+       if (lap) {
+               memcpy(ir->lap, lap, 3);
+       } else {
+               ir->lap[0] = 0x33;
+               ir->lap[1] = 0x8b;
+               ir->lap[2] = 0x9e;
+       }
+
+       ret = ioctl(dd, HCIINQUIRY, (unsigned long) buf);
+       if (ret < 0)
+               goto free;
+
+       size = sizeof(inquiry_info) * ir->num_rsp;
+
+       if (!*ii)
+               *ii = malloc(size);
+
+       if (*ii) {
+               memcpy((void *) *ii, buf + sizeof(*ir), size);
+               ret = ir->num_rsp;
+       } else
+               ret = -1;
+
+free:
+       free(buf);
+
+done:
+       err = errno;
+       close(dd);
+       errno = err;
+
+       return ret;
+}
+
+/* Open HCI device.
+ * Returns device descriptor (dd). */
+int hci_open_dev(int dev_id)
+{
+       struct sockaddr_hci a;
+       int dd, err;
+
+       /* Create HCI socket */
+       dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       if (dd < 0)
+               return dd;
+
+       /* Bind socket to the HCI device */
+       memset(&a, 0, sizeof(a));
+       a.hci_family = AF_BLUETOOTH;
+       a.hci_dev = dev_id;
+       if (bind(dd, (struct sockaddr *) &a, sizeof(a)) < 0)
+               goto failed;
+
+       return dd;
+
+failed:
+       err = errno;
+       close(dd);
+       errno = err;
+
+       return -1;
+}
+
+int hci_close_dev(int dd)
+{
+       return close(dd);
+}
+
+/* HCI functions that require open device
+ * dd - Device descriptor returned by hci_open_dev. */
+
+int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
+{
+       uint8_t type = HCI_COMMAND_PKT;
+       hci_command_hdr hc;
+       struct iovec iv[3];
+       int ivn;
+
+       hc.opcode = htobs(cmd_opcode_pack(ogf, ocf));
+       hc.plen= plen;
+
+       iv[0].iov_base = &type;
+       iv[0].iov_len  = 1;
+       iv[1].iov_base = &hc;
+       iv[1].iov_len  = HCI_COMMAND_HDR_SIZE;
+       ivn = 2;
+
+       if (plen) {
+               iv[2].iov_base = param;
+               iv[2].iov_len  = plen;
+               ivn = 3;
+       }
+
+       while (writev(dd, iv, ivn) < 0) {
+               if (errno == EAGAIN || errno == EINTR)
+                       continue;
+               return -1;
+       }
+       return 0;
+}
+
+int hci_send_req(int dd, struct hci_request *r, int to)
+{
+       unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
+       uint16_t opcode = htobs(cmd_opcode_pack(r->ogf, r->ocf));
+       struct hci_filter nf, of;
+       socklen_t olen;
+       hci_event_hdr *hdr;
+       int err, try;
+
+       olen = sizeof(of);
+       if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0)
+               return -1;
+
+       hci_filter_clear(&nf);
+       hci_filter_set_ptype(HCI_EVENT_PKT,  &nf);
+       hci_filter_set_event(EVT_CMD_STATUS, &nf);
+       hci_filter_set_event(EVT_CMD_COMPLETE, &nf);
+       hci_filter_set_event(EVT_LE_META_EVENT, &nf);
+       hci_filter_set_event(r->event, &nf);
+       hci_filter_set_opcode(opcode, &nf);
+       if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0)
+               return -1;
+
+       if (hci_send_cmd(dd, r->ogf, r->ocf, r->clen, r->cparam) < 0)
+               goto failed;
+
+       try = 10;
+       while (try--) {
+               evt_cmd_complete *cc;
+               evt_cmd_status *cs;
+               evt_remote_name_req_complete *rn;
+               evt_le_meta_event *me;
+               remote_name_req_cp *cp;
+               int len;
+
+               if (to) {
+                       struct pollfd p;
+                       int n;
+
+                       p.fd = dd; p.events = POLLIN;
+                       while ((n = poll(&p, 1, to)) < 0) {
+                               if (errno == EAGAIN || errno == EINTR)
+                                       continue;
+                               goto failed;
+                       }
+
+                       if (!n) {
+                               errno = ETIMEDOUT;
+                               goto failed;
+                       }
+
+                       to -= 10;
+                       if (to < 0)
+                               to = 0;
+
+               }
+
+               while ((len = read(dd, buf, sizeof(buf))) < 0) {
+                       if (errno == EAGAIN || errno == EINTR)
+                               continue;
+                       goto failed;
+               }
+
+               hdr = (void *) (buf + 1);
+               ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+               len -= (1 + HCI_EVENT_HDR_SIZE);
+
+               switch (hdr->evt) {
+               case EVT_CMD_STATUS:
+                       cs = (void *) ptr;
+
+                       if (cs->opcode != opcode)
+                               continue;
+
+                       if (r->event != EVT_CMD_STATUS) {
+                               if (cs->status) {
+                                       errno = EIO;
+                                       goto failed;
+                               }
+                               break;
+                       }
+
+                       r->rlen = MIN(len, r->rlen);
+                       memcpy(r->rparam, ptr, r->rlen);
+                       goto done;
+
+               case EVT_CMD_COMPLETE:
+                       cc = (void *) ptr;
+
+                       if (cc->opcode != opcode)
+                               continue;
+
+                       ptr += EVT_CMD_COMPLETE_SIZE;
+                       len -= EVT_CMD_COMPLETE_SIZE;
+
+                       r->rlen = MIN(len, r->rlen);
+                       memcpy(r->rparam, ptr, r->rlen);
+                       goto done;
+
+               case EVT_REMOTE_NAME_REQ_COMPLETE:
+                       if (hdr->evt != r->event)
+                               break;
+
+                       rn = (void *) ptr;
+                       cp = r->cparam;
+
+                       if (bacmp(&rn->bdaddr, &cp->bdaddr))
+                               continue;
+
+                       r->rlen = MIN(len, r->rlen);
+                       memcpy(r->rparam, ptr, r->rlen);
+                       goto done;
+
+               case EVT_LE_META_EVENT:
+                       me = (void *) ptr;
+
+                       if (me->subevent != r->event)
+                               continue;
+
+                       len -= 1;
+                       r->rlen = MIN(len, r->rlen);
+                       memcpy(r->rparam, me->data, r->rlen);
+                       goto done;
+
+               default:
+                       if (hdr->evt != r->event)
+                               break;
+
+                       r->rlen = MIN(len, r->rlen);
+                       memcpy(r->rparam, ptr, r->rlen);
+                       goto done;
+               }
+       }
+       errno = ETIMEDOUT;
+
+failed:
+       err = errno;
+       setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
+       errno = err;
+       return -1;
+
+done:
+       setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
+       return 0;
+}
+
+int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype,
+                               uint16_t clkoffset, uint8_t rswitch,
+                               uint16_t *handle, int to)
+{
+       evt_conn_complete rp;
+       create_conn_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+       cp.pkt_type       = ptype;
+       cp.pscan_rep_mode = 0x02;
+       cp.clock_offset   = clkoffset;
+       cp.role_switch    = rswitch;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_CREATE_CONN;
+       rq.event  = EVT_CONN_COMPLETE;
+       rq.cparam = &cp;
+       rq.clen   = CREATE_CONN_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_CONN_COMPLETE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *handle = rp.handle;
+       return 0;
+}
+
+int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to)
+{
+       evt_disconn_complete rp;
+       disconnect_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = handle;
+       cp.reason = reason;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_DISCONNECT;
+       rq.event  = EVT_DISCONN_COMPLETE;
+       rq.cparam = &cp;
+       rq.clen   = DISCONNECT_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_DISCONN_COMPLETE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+       return 0;
+}
+
+int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to)
+{
+       struct hci_request rq;
+       le_add_device_to_white_list_cp cp;
+       uint8_t status;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.bdaddr_type = type;
+       bacpy(&cp.bdaddr, bdaddr);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_ADD_DEVICE_TO_WHITE_LIST;
+       rq.cparam = &cp;
+       rq.clen = LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE;
+       rq.rparam = &status;
+       rq.rlen = 1;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to)
+{
+       struct hci_request rq;
+       le_remove_device_from_white_list_cp cp;
+       uint8_t status;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.bdaddr_type = type;
+       bacpy(&cp.bdaddr, bdaddr);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST;
+       rq.cparam = &cp;
+       rq.clen = LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE;
+       rq.rparam = &status;
+       rq.rlen = 1;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_le_read_white_list_size(int dd, uint8_t *size, int to)
+{
+       struct hci_request rq;
+       le_read_white_list_size_rp rp;
+
+       memset(&rp, 0, sizeof(rp));
+       memset(&rq, 0, sizeof(rq));
+
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_READ_WHITE_LIST_SIZE;
+       rq.rparam = &rp;
+       rq.rlen = LE_READ_WHITE_LIST_SIZE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       if (size)
+               *size = rp.size;
+
+       return 0;
+}
+
+int hci_le_clear_white_list(int dd, int to)
+{
+       struct hci_request rq;
+       uint8_t status;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_CLEAR_WHITE_LIST;
+       rq.rparam = &status;
+       rq.rlen = 1;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_read_local_name(int dd, int len, char *name, int to)
+{
+       read_local_name_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_LOCAL_NAME;
+       rq.rparam = &rp;
+       rq.rlen   = READ_LOCAL_NAME_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       rp.name[247] = '\0';
+       strncpy(name, (char *) rp.name, len);
+       return 0;
+}
+
+int hci_write_local_name(int dd, const char *name, int to)
+{
+       change_local_name_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       strncpy((char *) cp.name, name, sizeof(cp.name));
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_CHANGE_LOCAL_NAME;
+       rq.cparam = &cp;
+       rq.clen   = CHANGE_LOCAL_NAME_CP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       return 0;
+}
+
+int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr,
+                                               uint8_t pscan_rep_mode,
+                                               uint16_t clkoffset,
+                                               int len, char *name, int to)
+{
+       evt_remote_name_req_complete rn;
+       remote_name_req_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+       cp.pscan_rep_mode = pscan_rep_mode;
+       cp.clock_offset   = clkoffset;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_REMOTE_NAME_REQ;
+       rq.cparam = &cp;
+       rq.clen   = REMOTE_NAME_REQ_CP_SIZE;
+       rq.event  = EVT_REMOTE_NAME_REQ_COMPLETE;
+       rq.rparam = &rn;
+       rq.rlen   = EVT_REMOTE_NAME_REQ_COMPLETE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rn.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       rn.name[247] = '\0';
+       strncpy(name, (char *) rn.name, len);
+       return 0;
+}
+
+int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name,
+                               int to)
+{
+       return hci_read_remote_name_with_clock_offset(dd, bdaddr, 0x02, 0x0000,
+                                                       len, name, to);
+}
+
+int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to)
+{
+       remote_name_req_cancel_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_REMOTE_NAME_REQ_CANCEL;
+       rq.cparam = &cp;
+       rq.clen   = REMOTE_NAME_REQ_CANCEL_CP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       return 0;
+}
+
+int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver,
+                               int to)
+{
+       evt_read_remote_version_complete rp;
+       read_remote_version_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = handle;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_READ_REMOTE_VERSION;
+       rq.event  = EVT_READ_REMOTE_VERSION_COMPLETE;
+       rq.cparam = &cp;
+       rq.clen   = READ_REMOTE_VERSION_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_READ_REMOTE_VERSION_COMPLETE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       ver->manufacturer = btohs(rp.manufacturer);
+       ver->lmp_ver      = rp.lmp_ver;
+       ver->lmp_subver   = btohs(rp.lmp_subver);
+       return 0;
+}
+
+int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to)
+{
+       evt_read_remote_features_complete rp;
+       read_remote_features_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = handle;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_READ_REMOTE_FEATURES;
+       rq.event  = EVT_READ_REMOTE_FEATURES_COMPLETE;
+       rq.cparam = &cp;
+       rq.clen   = READ_REMOTE_FEATURES_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       if (features)
+               memcpy(features, rp.features, 8);
+
+       return 0;
+}
+
+int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page,
+                                       uint8_t *max_page, uint8_t *features,
+                                       int to)
+{
+       evt_read_remote_ext_features_complete rp;
+       read_remote_ext_features_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle   = handle;
+       cp.page_num = page;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_READ_REMOTE_EXT_FEATURES;
+       rq.event  = EVT_READ_REMOTE_EXT_FEATURES_COMPLETE;
+       rq.cparam = &cp;
+       rq.clen   = READ_REMOTE_EXT_FEATURES_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       if (max_page)
+               *max_page = rp.max_page_num;
+
+       if (features)
+               memcpy(features, rp.features, 8);
+
+       return 0;
+}
+
+int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to)
+{
+       evt_read_clock_offset_complete rp;
+       read_clock_offset_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = handle;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_READ_CLOCK_OFFSET;
+       rq.event  = EVT_READ_CLOCK_OFFSET_COMPLETE;
+       rq.cparam = &cp;
+       rq.clen   = READ_CLOCK_OFFSET_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *clkoffset = rp.clock_offset;
+       return 0;
+}
+
+int hci_read_local_version(int dd, struct hci_version *ver, int to)
+{
+       read_local_version_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_INFO_PARAM;
+       rq.ocf    = OCF_READ_LOCAL_VERSION;
+       rq.rparam = &rp;
+       rq.rlen   = READ_LOCAL_VERSION_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       ver->manufacturer = btohs(rp.manufacturer);
+       ver->hci_ver      = rp.hci_ver;
+       ver->hci_rev      = btohs(rp.hci_rev);
+       ver->lmp_ver      = rp.lmp_ver;
+       ver->lmp_subver   = btohs(rp.lmp_subver);
+       return 0;
+}
+
+int hci_read_local_commands(int dd, uint8_t *commands, int to)
+{
+       read_local_commands_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_INFO_PARAM;
+       rq.ocf    = OCF_READ_LOCAL_COMMANDS;
+       rq.rparam = &rp;
+       rq.rlen   = READ_LOCAL_COMMANDS_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       if (commands)
+               memcpy(commands, rp.commands, 64);
+
+       return 0;
+}
+
+int hci_read_local_features(int dd, uint8_t *features, int to)
+{
+       read_local_features_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_INFO_PARAM;
+       rq.ocf    = OCF_READ_LOCAL_FEATURES;
+       rq.rparam = &rp;
+       rq.rlen   = READ_LOCAL_FEATURES_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       if (features)
+               memcpy(features, rp.features, 8);
+
+       return 0;
+}
+
+int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page,
+                               uint8_t *features, int to)
+{
+       read_local_ext_features_cp cp;
+       read_local_ext_features_rp rp;
+       struct hci_request rq;
+
+       cp.page_num = page;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_INFO_PARAM;
+       rq.ocf    = OCF_READ_LOCAL_EXT_FEATURES;
+       rq.cparam = &cp;
+       rq.clen   = READ_LOCAL_EXT_FEATURES_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = READ_LOCAL_EXT_FEATURES_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       if (max_page)
+               *max_page = rp.max_page_num;
+
+       if (features)
+               memcpy(features, rp.features, 8);
+
+       return 0;
+}
+
+int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to)
+{
+       read_bd_addr_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_INFO_PARAM;
+       rq.ocf    = OCF_READ_BD_ADDR;
+       rq.rparam = &rp;
+       rq.rlen   = READ_BD_ADDR_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       if (bdaddr)
+               bacpy(bdaddr, &rp.bdaddr);
+
+       return 0;
+}
+
+int hci_read_class_of_dev(int dd, uint8_t *cls, int to)
+{
+       read_class_of_dev_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_CLASS_OF_DEV;
+       rq.rparam = &rp;
+       rq.rlen   = READ_CLASS_OF_DEV_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       memcpy(cls, rp.dev_class, 3);
+       return 0;
+}
+
+int hci_write_class_of_dev(int dd, uint32_t cls, int to)
+{
+       write_class_of_dev_cp cp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       cp.dev_class[0] = cls & 0xff;
+       cp.dev_class[1] = (cls >> 8) & 0xff;
+       cp.dev_class[2] = (cls >> 16) & 0xff;
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_CLASS_OF_DEV;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_CLASS_OF_DEV_CP_SIZE;
+       return hci_send_req(dd, &rq, to);
+}
+
+int hci_read_voice_setting(int dd, uint16_t *vs, int to)
+{
+       read_voice_setting_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_VOICE_SETTING;
+       rq.rparam = &rp;
+       rq.rlen   = READ_VOICE_SETTING_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *vs = rp.voice_setting;
+       return 0;
+}
+
+int hci_write_voice_setting(int dd, uint16_t vs, int to)
+{
+       write_voice_setting_cp cp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       cp.voice_setting = vs;
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_VOICE_SETTING;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_VOICE_SETTING_CP_SIZE;
+
+       return hci_send_req(dd, &rq, to);
+}
+
+int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to)
+{
+       read_current_iac_lap_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_CURRENT_IAC_LAP;
+       rq.rparam = &rp;
+       rq.rlen   = READ_CURRENT_IAC_LAP_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *num_iac = rp.num_current_iac;
+       memcpy(lap, rp.lap, rp.num_current_iac * 3);
+       return 0;
+}
+
+int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to)
+{
+       write_current_iac_lap_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.num_current_iac = num_iac;
+       memcpy(&cp.lap, lap, num_iac * 3);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_CURRENT_IAC_LAP;
+       rq.cparam = &cp;
+       rq.clen   = num_iac * 3 + 1;
+
+       return hci_send_req(dd, &rq, to);
+}
+
+int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to)
+{
+       read_stored_link_key_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+       cp.read_all = all;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_STORED_LINK_KEY;
+       rq.cparam = &cp;
+       rq.clen   = READ_STORED_LINK_KEY_CP_SIZE;
+
+       return hci_send_req(dd, &rq, to);
+}
+
+int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to)
+{
+       unsigned char cp[WRITE_STORED_LINK_KEY_CP_SIZE + 6 + 16];
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp[0] = 1;
+       bacpy((bdaddr_t *) (cp + 1), bdaddr);
+       memcpy(cp + 7, key, 16);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_STORED_LINK_KEY;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_STORED_LINK_KEY_CP_SIZE + 6 + 16;
+
+       return hci_send_req(dd, &rq, to);
+}
+
+int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to)
+{
+       delete_stored_link_key_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+       cp.delete_all = all;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_DELETE_STORED_LINK_KEY;
+       rq.cparam = &cp;
+       rq.clen   = DELETE_STORED_LINK_KEY_CP_SIZE;
+
+       return hci_send_req(dd, &rq, to);
+}
+
+int hci_authenticate_link(int dd, uint16_t handle, int to)
+{
+       auth_requested_cp cp;
+       evt_auth_complete rp;
+       struct hci_request rq;
+
+       cp.handle = handle;
+
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_AUTH_REQUESTED;
+       rq.event  = EVT_AUTH_COMPLETE;
+       rq.cparam = &cp;
+       rq.clen   = AUTH_REQUESTED_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_AUTH_COMPLETE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to)
+{
+       set_conn_encrypt_cp cp;
+       evt_encrypt_change rp;
+       struct hci_request rq;
+
+       cp.handle  = handle;
+       cp.encrypt = encrypt;
+
+       rq.ogf     = OGF_LINK_CTL;
+       rq.ocf     = OCF_SET_CONN_ENCRYPT;
+       rq.event   = EVT_ENCRYPT_CHANGE;
+       rq.cparam  = &cp;
+       rq.clen    = SET_CONN_ENCRYPT_CP_SIZE;
+       rq.rparam  = &rp;
+       rq.rlen    = EVT_ENCRYPT_CHANGE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_change_link_key(int dd, uint16_t handle, int to)
+{
+       change_conn_link_key_cp cp;
+       evt_change_conn_link_key_complete rp;
+       struct hci_request rq;
+
+       cp.handle = handle;
+
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_CHANGE_CONN_LINK_KEY;
+       rq.event  = EVT_CHANGE_CONN_LINK_KEY_COMPLETE;
+       rq.cparam = &cp;
+       rq.clen   = CHANGE_CONN_LINK_KEY_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to)
+{
+       switch_role_cp cp;
+       evt_role_change rp;
+       struct hci_request rq;
+
+       bacpy(&cp.bdaddr, bdaddr);
+       cp.role   = role;
+       rq.ogf    = OGF_LINK_POLICY;
+       rq.ocf    = OCF_SWITCH_ROLE;
+       rq.cparam = &cp;
+       rq.clen   = SWITCH_ROLE_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_ROLE_CHANGE_SIZE;
+       rq.event  = EVT_ROLE_CHANGE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval,
+                       uint16_t min_interval, int to)
+{
+       park_mode_cp cp;
+       evt_mode_change rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof (cp));
+       cp.handle       = handle;
+       cp.max_interval = max_interval;
+       cp.min_interval = min_interval;
+
+       memset(&rq, 0, sizeof (rq));
+       rq.ogf    = OGF_LINK_POLICY;
+       rq.ocf    = OCF_PARK_MODE;
+       rq.event  = EVT_MODE_CHANGE;
+       rq.cparam = &cp;
+       rq.clen   = PARK_MODE_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_MODE_CHANGE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_exit_park_mode(int dd, uint16_t handle, int to)
+{
+       exit_park_mode_cp cp;
+       evt_mode_change rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof (cp));
+       cp.handle = handle;
+
+       memset (&rq, 0, sizeof (rq));
+       rq.ogf    = OGF_LINK_POLICY;
+       rq.ocf    = OCF_EXIT_PARK_MODE;
+       rq.event  = EVT_MODE_CHANGE;
+       rq.cparam = &cp;
+       rq.clen   = EXIT_PARK_MODE_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_MODE_CHANGE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to)
+{
+       read_inquiry_scan_type_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_INQUIRY_SCAN_TYPE;
+       rq.rparam = &rp;
+       rq.rlen   = READ_INQUIRY_SCAN_TYPE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *type = rp.type;
+       return 0;
+}
+
+int hci_write_inquiry_scan_type(int dd, uint8_t type, int to)
+{
+       write_inquiry_scan_type_cp cp;
+       write_inquiry_scan_type_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.type = type;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_INQUIRY_SCAN_TYPE;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_INQUIRY_SCAN_TYPE_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = WRITE_INQUIRY_SCAN_TYPE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_read_inquiry_mode(int dd, uint8_t *mode, int to)
+{
+       read_inquiry_mode_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_INQUIRY_MODE;
+       rq.rparam = &rp;
+       rq.rlen   = READ_INQUIRY_MODE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *mode = rp.mode;
+       return 0;
+}
+
+int hci_write_inquiry_mode(int dd, uint8_t mode, int to)
+{
+       write_inquiry_mode_cp cp;
+       write_inquiry_mode_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.mode = mode;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_INQUIRY_MODE;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_INQUIRY_MODE_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = WRITE_INQUIRY_MODE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_read_afh_mode(int dd, uint8_t *mode, int to)
+{
+       read_afh_mode_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_AFH_MODE;
+       rq.rparam = &rp;
+       rq.rlen   = READ_AFH_MODE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *mode = rp.mode;
+       return 0;
+}
+
+int hci_write_afh_mode(int dd, uint8_t mode, int to)
+{
+       write_afh_mode_cp cp;
+       write_afh_mode_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.mode = mode;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_AFH_MODE;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_AFH_MODE_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = WRITE_AFH_MODE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to)
+{
+       read_ext_inquiry_response_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_EXT_INQUIRY_RESPONSE;
+       rq.rparam = &rp;
+       rq.rlen   = READ_EXT_INQUIRY_RESPONSE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *fec = rp.fec;
+       memcpy(data, rp.data, HCI_MAX_EIR_LENGTH);
+
+       return 0;
+}
+
+int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to)
+{
+       write_ext_inquiry_response_cp cp;
+       write_ext_inquiry_response_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.fec = fec;
+       memcpy(cp.data, data, HCI_MAX_EIR_LENGTH);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_EXT_INQUIRY_RESPONSE;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to)
+{
+       read_simple_pairing_mode_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_SIMPLE_PAIRING_MODE;
+       rq.rparam = &rp;
+       rq.rlen   = READ_SIMPLE_PAIRING_MODE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *mode = rp.mode;
+       return 0;
+}
+
+int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to)
+{
+       write_simple_pairing_mode_cp cp;
+       write_simple_pairing_mode_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.mode = mode;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_SIMPLE_PAIRING_MODE;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_SIMPLE_PAIRING_MODE_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = WRITE_SIMPLE_PAIRING_MODE_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to)
+{
+       read_local_oob_data_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_LOCAL_OOB_DATA;
+       rq.rparam = &rp;
+       rq.rlen   = READ_LOCAL_OOB_DATA_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       memcpy(hash, rp.hash, 16);
+       memcpy(randomizer, rp.randomizer, 16);
+       return 0;
+}
+
+int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to)
+{
+       read_inq_response_tx_power_level_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL;
+       rq.rparam = &rp;
+       rq.rlen   = READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *level = rp.level;
+       return 0;
+}
+
+int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to)
+{
+       return hci_read_inq_response_tx_power_level(dd, level, to);
+}
+
+int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to)
+{
+       write_inquiry_transmit_power_level_cp cp;
+       write_inquiry_transmit_power_level_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.level = level;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type,
+                                       int8_t *level, int to)
+{
+       read_transmit_power_level_cp cp;
+       read_transmit_power_level_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = handle;
+       cp.type   = type;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_TRANSMIT_POWER_LEVEL;
+       rq.cparam = &cp;
+       rq.clen   = READ_TRANSMIT_POWER_LEVEL_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = READ_TRANSMIT_POWER_LEVEL_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *level = rp.level;
+       return 0;
+}
+
+int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to)
+{
+       read_link_policy_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_POLICY;
+       rq.ocf    = OCF_READ_LINK_POLICY;
+       rq.cparam = &handle;
+       rq.clen   = 2;
+       rq.rparam = &rp;
+       rq.rlen   = READ_LINK_POLICY_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *policy = rp.policy;
+       return 0;
+}
+
+int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to)
+{
+       write_link_policy_cp cp;
+       write_link_policy_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = handle;
+       cp.policy = policy;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_POLICY;
+       rq.ocf    = OCF_WRITE_LINK_POLICY;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_LINK_POLICY_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = WRITE_LINK_POLICY_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_read_link_supervision_timeout(int dd, uint16_t handle,
+                                       uint16_t *timeout, int to)
+{
+       read_link_supervision_timeout_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_READ_LINK_SUPERVISION_TIMEOUT;
+       rq.cparam = &handle;
+       rq.clen   = 2;
+       rq.rparam = &rp;
+       rq.rlen   = READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *timeout = rp.timeout;
+       return 0;
+}
+
+int hci_write_link_supervision_timeout(int dd, uint16_t handle,
+                                       uint16_t timeout, int to)
+{
+       write_link_supervision_timeout_cp cp;
+       write_link_supervision_timeout_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle  = handle;
+       cp.timeout = timeout;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_WRITE_LINK_SUPERVISION_TIMEOUT;
+       rq.cparam = &cp;
+       rq.clen   = WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_set_afh_classification(int dd, uint8_t *map, int to)
+{
+       set_afh_classification_cp cp;
+       set_afh_classification_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(cp.map, map, 10);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_HOST_CTL;
+       rq.ocf    = OCF_SET_AFH_CLASSIFICATION;
+       rq.cparam = &cp;
+       rq.clen   = SET_AFH_CLASSIFICATION_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = SET_AFH_CLASSIFICATION_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality,
+                               int to)
+{
+       read_link_quality_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_STATUS_PARAM;
+       rq.ocf    = OCF_READ_LINK_QUALITY;
+       rq.cparam = &handle;
+       rq.clen   = 2;
+       rq.rparam = &rp;
+       rq.rlen   = READ_LINK_QUALITY_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *link_quality = rp.link_quality;
+       return 0;
+}
+
+int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to)
+{
+       read_rssi_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_STATUS_PARAM;
+       rq.ocf    = OCF_READ_RSSI;
+       rq.cparam = &handle;
+       rq.clen   = 2;
+       rq.rparam = &rp;
+       rq.rlen   = READ_RSSI_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *rssi = rp.rssi;
+       return 0;
+}
+
+int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map,
+                       int to)
+{
+       read_afh_map_rp rp;
+       struct hci_request rq;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_STATUS_PARAM;
+       rq.ocf    = OCF_READ_AFH_MAP;
+       rq.cparam = &handle;
+       rq.clen   = 2;
+       rq.rparam = &rp;
+       rq.rlen   = READ_AFH_MAP_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *mode = rp.mode;
+       memcpy(map, rp.map, 10);
+       return 0;
+}
+
+int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock,
+                       uint16_t *accuracy, int to)
+{
+       read_clock_cp cp;
+       read_clock_rp rp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle      = handle;
+       cp.which_clock = which;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_STATUS_PARAM;
+       rq.ocf    = OCF_READ_CLOCK;
+       rq.cparam = &cp;
+       rq.clen   = READ_CLOCK_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = READ_CLOCK_RP_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       *clock    = rp.clock;
+       *accuracy = rp.accuracy;
+       return 0;
+}
+
+int hci_le_set_scan_enable(int dd, uint8_t enable, uint8_t filter_dup, int to)
+{
+       struct hci_request rq;
+       le_set_scan_enable_cp scan_cp;
+       uint8_t status;
+
+       memset(&scan_cp, 0, sizeof(scan_cp));
+       scan_cp.enable = enable;
+       scan_cp.filter_dup = filter_dup;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_SET_SCAN_ENABLE;
+       rq.cparam = &scan_cp;
+       rq.clen = LE_SET_SCAN_ENABLE_CP_SIZE;
+       rq.rparam = &status;
+       rq.rlen = 1;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_le_set_scan_parameters(int dd, uint8_t type,
+                                       uint16_t interval, uint16_t window,
+                                       uint8_t own_type, uint8_t filter, int to)
+{
+       struct hci_request rq;
+       le_set_scan_parameters_cp param_cp;
+       uint8_t status;
+
+       memset(&param_cp, 0, sizeof(param_cp));
+       param_cp.type = type;
+       param_cp.interval = interval;
+       param_cp.window = window;
+       param_cp.own_bdaddr_type = own_type;
+       param_cp.filter = filter;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_SET_SCAN_PARAMETERS;
+       rq.cparam = &param_cp;
+       rq.clen = LE_SET_SCAN_PARAMETERS_CP_SIZE;
+       rq.rparam = &status;
+       rq.rlen = 1;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_le_set_advertise_enable(int dd, uint8_t enable, int to)
+{
+       struct hci_request rq;
+       le_set_advertise_enable_cp adv_cp;
+       uint8_t status;
+
+       memset(&adv_cp, 0, sizeof(adv_cp));
+       adv_cp.enable = enable;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
+       rq.cparam = &adv_cp;
+       rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
+       rq.rparam = &status;
+       rq.rlen = 1;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int hci_le_create_conn(int dd, uint16_t interval, uint16_t window,
+               uint8_t initiator_filter, uint8_t peer_bdaddr_type,
+               bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type,
+               uint16_t min_interval, uint16_t max_interval,
+               uint16_t latency, uint16_t supervision_timeout,
+               uint16_t min_ce_length, uint16_t max_ce_length,
+               uint16_t *handle, int to)
+{
+       struct hci_request rq;
+       le_create_connection_cp create_conn_cp;
+       evt_le_connection_complete conn_complete_rp;
+
+       memset(&create_conn_cp, 0, sizeof(create_conn_cp));
+       create_conn_cp.interval = interval;
+       create_conn_cp.window = window;
+       create_conn_cp.initiator_filter = initiator_filter;
+       create_conn_cp.peer_bdaddr_type = peer_bdaddr_type;
+       create_conn_cp.peer_bdaddr = peer_bdaddr;
+       create_conn_cp.own_bdaddr_type = own_bdaddr_type;
+       create_conn_cp.min_interval = min_interval;
+       create_conn_cp.max_interval = max_interval;
+       create_conn_cp.latency = latency;
+       create_conn_cp.supervision_timeout = supervision_timeout;
+       create_conn_cp.min_ce_length = min_ce_length;
+       create_conn_cp.max_ce_length = max_ce_length;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_CREATE_CONN;
+       rq.event = EVT_LE_CONN_COMPLETE;
+       rq.cparam = &create_conn_cp;
+       rq.clen = LE_CREATE_CONN_CP_SIZE;
+       rq.rparam = &conn_complete_rp;
+       rq.rlen = EVT_CONN_COMPLETE_SIZE;
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (conn_complete_rp.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       if (handle)
+               *handle = conn_complete_rp.handle;
+
+       return 0;
+}
+
+int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval,
+                       uint16_t max_interval, uint16_t latency,
+                       uint16_t supervision_timeout, int to)
+{
+       evt_le_connection_update_complete evt;
+       le_connection_update_cp cp;
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = handle;
+       cp.min_interval = min_interval;
+       cp.max_interval = max_interval;
+       cp.latency = latency;
+       cp.supervision_timeout = supervision_timeout;
+       cp.min_ce_length = htobs(0x0001);
+       cp.max_ce_length = htobs(0x0001);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_CONN_UPDATE;
+       rq.cparam = &cp;
+       rq.clen = LE_CONN_UPDATE_CP_SIZE;
+       rq.event = EVT_LE_CONN_UPDATE_COMPLETE;
+       rq.rparam = &evt;
+       rq.rlen = sizeof(evt);
+
+       if (hci_send_req(dd, &rq, to) < 0)
+               return -1;
+
+       if (evt.status) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/lib/hci.h b/lib/hci.h
new file mode 100644 (file)
index 0000000..f7be92d
--- /dev/null
+++ b/lib/hci.h
@@ -0,0 +1,2391 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __HCI_H
+#define __HCI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+#define HCI_MAX_DEV    16
+
+#define HCI_MAX_ACL_SIZE       1024
+#define HCI_MAX_SCO_SIZE       255
+#define HCI_MAX_EVENT_SIZE     260
+#define HCI_MAX_FRAME_SIZE     (HCI_MAX_ACL_SIZE + 4)
+
+/* HCI dev events */
+#define HCI_DEV_REG    1
+#define HCI_DEV_UNREG  2
+#define HCI_DEV_UP     3
+#define HCI_DEV_DOWN   4
+#define HCI_DEV_SUSPEND        5
+#define HCI_DEV_RESUME 6
+
+/* HCI bus types */
+#define HCI_VIRTUAL    0
+#define HCI_USB                1
+#define HCI_PCCARD     2
+#define HCI_UART       3
+#define HCI_RS232      4
+#define HCI_PCI                5
+#define HCI_SDIO       6
+
+/* HCI controller types */
+#define HCI_BREDR      0x00
+#define HCI_AMP                0x01
+
+/* HCI device flags */
+enum {
+       HCI_UP,
+       HCI_INIT,
+       HCI_RUNNING,
+
+       HCI_PSCAN,
+       HCI_ISCAN,
+       HCI_AUTH,
+       HCI_ENCRYPT,
+       HCI_INQUIRY,
+
+       HCI_RAW,
+};
+
+/* LE address type */
+enum {
+       LE_PUBLIC_ADDRESS = 0x00,
+       LE_RANDOM_ADDRESS = 0x01
+};
+
+/* HCI ioctl defines */
+#define HCIDEVUP       _IOW('H', 201, int)
+#define HCIDEVDOWN     _IOW('H', 202, int)
+#define HCIDEVRESET    _IOW('H', 203, int)
+#define HCIDEVRESTAT   _IOW('H', 204, int)
+
+#define HCIGETDEVLIST  _IOR('H', 210, int)
+#define HCIGETDEVINFO  _IOR('H', 211, int)
+#define HCIGETCONNLIST _IOR('H', 212, int)
+#define HCIGETCONNINFO _IOR('H', 213, int)
+#define HCIGETAUTHINFO _IOR('H', 215, int)
+
+#define HCISETRAW      _IOW('H', 220, int)
+#define HCISETSCAN     _IOW('H', 221, int)
+#define HCISETAUTH     _IOW('H', 222, int)
+#define HCISETENCRYPT  _IOW('H', 223, int)
+#define HCISETPTYPE    _IOW('H', 224, int)
+#define HCISETLINKPOL  _IOW('H', 225, int)
+#define HCISETLINKMODE _IOW('H', 226, int)
+#define HCISETACLMTU   _IOW('H', 227, int)
+#define HCISETSCOMTU   _IOW('H', 228, int)
+
+#define HCIBLOCKADDR   _IOW('H', 230, int)
+#define HCIUNBLOCKADDR _IOW('H', 231, int)
+
+#define HCIINQUIRY     _IOR('H', 240, int)
+
+#ifndef __NO_HCI_DEFS
+
+/* HCI Packet types */
+#define HCI_COMMAND_PKT                0x01
+#define HCI_ACLDATA_PKT                0x02
+#define HCI_SCODATA_PKT                0x03
+#define HCI_EVENT_PKT          0x04
+#define HCI_VENDOR_PKT         0xff
+
+/* HCI Packet types */
+#define HCI_2DH1       0x0002
+#define HCI_3DH1       0x0004
+#define HCI_DM1                0x0008
+#define HCI_DH1                0x0010
+#define HCI_2DH3       0x0100
+#define HCI_3DH3       0x0200
+#define HCI_DM3                0x0400
+#define HCI_DH3                0x0800
+#define HCI_2DH5       0x1000
+#define HCI_3DH5       0x2000
+#define HCI_DM5                0x4000
+#define HCI_DH5                0x8000
+
+#define HCI_HV1                0x0020
+#define HCI_HV2                0x0040
+#define HCI_HV3                0x0080
+
+#define HCI_EV3                0x0008
+#define HCI_EV4                0x0010
+#define HCI_EV5                0x0020
+#define HCI_2EV3       0x0040
+#define HCI_3EV3       0x0080
+#define HCI_2EV5       0x0100
+#define HCI_3EV5       0x0200
+
+#define SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3)
+#define ACL_PTYPE_MASK (HCI_DM1 | HCI_DH1 | HCI_DM3 | HCI_DH3 | HCI_DM5 | HCI_DH5)
+
+/* HCI Error codes */
+#define HCI_UNKNOWN_COMMAND                    0x01
+#define HCI_NO_CONNECTION                      0x02
+#define HCI_HARDWARE_FAILURE                   0x03
+#define HCI_PAGE_TIMEOUT                       0x04
+#define HCI_AUTHENTICATION_FAILURE             0x05
+#define HCI_PIN_OR_KEY_MISSING                 0x06
+#define HCI_MEMORY_FULL                                0x07
+#define HCI_CONNECTION_TIMEOUT                 0x08
+#define HCI_MAX_NUMBER_OF_CONNECTIONS          0x09
+#define HCI_MAX_NUMBER_OF_SCO_CONNECTIONS      0x0a
+#define HCI_ACL_CONNECTION_EXISTS              0x0b
+#define HCI_COMMAND_DISALLOWED                 0x0c
+#define HCI_REJECTED_LIMITED_RESOURCES         0x0d
+#define HCI_REJECTED_SECURITY                  0x0e
+#define HCI_REJECTED_PERSONAL                  0x0f
+#define HCI_HOST_TIMEOUT                       0x10
+#define HCI_UNSUPPORTED_FEATURE                        0x11
+#define HCI_INVALID_PARAMETERS                 0x12
+#define HCI_OE_USER_ENDED_CONNECTION           0x13
+#define HCI_OE_LOW_RESOURCES                   0x14
+#define HCI_OE_POWER_OFF                       0x15
+#define HCI_CONNECTION_TERMINATED              0x16
+#define HCI_REPEATED_ATTEMPTS                  0x17
+#define HCI_PAIRING_NOT_ALLOWED                        0x18
+#define HCI_UNKNOWN_LMP_PDU                    0x19
+#define HCI_UNSUPPORTED_REMOTE_FEATURE         0x1a
+#define HCI_SCO_OFFSET_REJECTED                        0x1b
+#define HCI_SCO_INTERVAL_REJECTED              0x1c
+#define HCI_AIR_MODE_REJECTED                  0x1d
+#define HCI_INVALID_LMP_PARAMETERS             0x1e
+#define HCI_UNSPECIFIED_ERROR                  0x1f
+#define HCI_UNSUPPORTED_LMP_PARAMETER_VALUE    0x20
+#define HCI_ROLE_CHANGE_NOT_ALLOWED            0x21
+#define HCI_LMP_RESPONSE_TIMEOUT               0x22
+#define HCI_LMP_ERROR_TRANSACTION_COLLISION    0x23
+#define HCI_LMP_PDU_NOT_ALLOWED                        0x24
+#define HCI_ENCRYPTION_MODE_NOT_ACCEPTED       0x25
+#define HCI_UNIT_LINK_KEY_USED                 0x26
+#define HCI_QOS_NOT_SUPPORTED                  0x27
+#define HCI_INSTANT_PASSED                     0x28
+#define HCI_PAIRING_NOT_SUPPORTED              0x29
+#define HCI_TRANSACTION_COLLISION              0x2a
+#define HCI_QOS_UNACCEPTABLE_PARAMETER         0x2c
+#define HCI_QOS_REJECTED                       0x2d
+#define HCI_CLASSIFICATION_NOT_SUPPORTED       0x2e
+#define HCI_INSUFFICIENT_SECURITY              0x2f
+#define HCI_PARAMETER_OUT_OF_RANGE             0x30
+#define HCI_ROLE_SWITCH_PENDING                        0x32
+#define HCI_SLOT_VIOLATION                     0x34
+#define HCI_ROLE_SWITCH_FAILED                 0x35
+#define HCI_EIR_TOO_LARGE                      0x36
+#define HCI_SIMPLE_PAIRING_NOT_SUPPORTED       0x37
+#define HCI_HOST_BUSY_PAIRING                  0x38
+
+/* ACL flags */
+#define ACL_START_NO_FLUSH     0x00
+#define ACL_CONT               0x01
+#define ACL_START              0x02
+#define ACL_ACTIVE_BCAST       0x04
+#define ACL_PICO_BCAST         0x08
+
+/* Baseband links */
+#define SCO_LINK       0x00
+#define ACL_LINK       0x01
+#define ESCO_LINK      0x02
+
+/* LMP features */
+#define LMP_3SLOT      0x01
+#define LMP_5SLOT      0x02
+#define LMP_ENCRYPT    0x04
+#define LMP_SOFFSET    0x08
+#define LMP_TACCURACY  0x10
+#define LMP_RSWITCH    0x20
+#define LMP_HOLD       0x40
+#define LMP_SNIFF      0x80
+
+#define LMP_PARK       0x01
+#define LMP_RSSI       0x02
+#define LMP_QUALITY    0x04
+#define LMP_SCO                0x08
+#define LMP_HV2                0x10
+#define LMP_HV3                0x20
+#define LMP_ULAW       0x40
+#define LMP_ALAW       0x80
+
+#define LMP_CVSD       0x01
+#define LMP_PSCHEME    0x02
+#define LMP_PCONTROL   0x04
+#define LMP_TRSP_SCO   0x08
+#define LMP_BCAST_ENC  0x80
+
+#define LMP_EDR_ACL_2M 0x02
+#define LMP_EDR_ACL_3M 0x04
+#define LMP_ENH_ISCAN  0x08
+#define LMP_ILACE_ISCAN        0x10
+#define LMP_ILACE_PSCAN        0x20
+#define LMP_RSSI_INQ   0x40
+#define LMP_ESCO       0x80
+
+#define LMP_EV4                0x01
+#define LMP_EV5                0x02
+#define LMP_AFH_CAP_SLV        0x08
+#define LMP_AFH_CLS_SLV        0x10
+#define LMP_NO_BREDR   0x20
+#define LMP_LE         0x40
+#define LMP_EDR_3SLOT  0x80
+
+#define LMP_EDR_5SLOT  0x01
+#define LMP_SNIFF_SUBR 0x02
+#define LMP_PAUSE_ENC  0x04
+#define LMP_AFH_CAP_MST        0x08
+#define LMP_AFH_CLS_MST        0x10
+#define LMP_EDR_ESCO_2M        0x20
+#define LMP_EDR_ESCO_3M        0x40
+#define LMP_EDR_3S_ESCO        0x80
+
+#define LMP_EXT_INQ    0x01
+#define LMP_LE_BREDR   0x02
+#define LMP_SIMPLE_PAIR        0x08
+#define LMP_ENCAPS_PDU 0x10
+#define LMP_ERR_DAT_REP        0x20
+#define LMP_NFLUSH_PKTS        0x40
+
+#define LMP_LSTO       0x01
+#define LMP_INQ_TX_PWR 0x02
+#define LMP_EPC                0x04
+#define LMP_EXT_FEAT   0x80
+
+/* Extended LMP features */
+#define LMP_HOST_SSP           0x01
+#define LMP_HOST_LE            0x02
+#define LMP_HOST_LE_BREDR      0x04
+
+/* Link policies */
+#define HCI_LP_RSWITCH 0x0001
+#define HCI_LP_HOLD    0x0002
+#define HCI_LP_SNIFF   0x0004
+#define HCI_LP_PARK    0x0008
+
+/* Link mode */
+#define HCI_LM_ACCEPT  0x8000
+#define HCI_LM_MASTER  0x0001
+#define HCI_LM_AUTH    0x0002
+#define HCI_LM_ENCRYPT 0x0004
+#define HCI_LM_TRUSTED 0x0008
+#define HCI_LM_RELIABLE        0x0010
+#define HCI_LM_SECURE  0x0020
+
+/* Link Key types */
+#define HCI_LK_COMBINATION             0x00
+#define HCI_LK_LOCAL_UNIT              0x01
+#define HCI_LK_REMOTE_UNIT             0x02
+#define HCI_LK_DEBUG_COMBINATION       0x03
+#define HCI_LK_UNAUTH_COMBINATION      0x04
+#define HCI_LK_AUTH_COMBINATION                0x05
+#define HCI_LK_CHANGED_COMBINATION     0x06
+#define HCI_LK_INVALID                 0xFF
+
+/* -----  HCI Commands ----- */
+
+/* Link Control */
+#define OGF_LINK_CTL           0x01
+
+#define OCF_INQUIRY                    0x0001
+typedef struct {
+       uint8_t         lap[3];
+       uint8_t         length;         /* 1.28s units */
+       uint8_t         num_rsp;
+} __attribute__ ((packed)) inquiry_cp;
+#define INQUIRY_CP_SIZE 5
+
+typedef struct {
+       uint8_t         status;
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) status_bdaddr_rp;
+#define STATUS_BDADDR_RP_SIZE 7
+
+#define OCF_INQUIRY_CANCEL             0x0002
+
+#define OCF_PERIODIC_INQUIRY           0x0003
+typedef struct {
+       uint16_t        max_period;     /* 1.28s units */
+       uint16_t        min_period;     /* 1.28s units */
+       uint8_t         lap[3];
+       uint8_t         length;         /* 1.28s units */
+       uint8_t         num_rsp;
+} __attribute__ ((packed)) periodic_inquiry_cp;
+#define PERIODIC_INQUIRY_CP_SIZE 9
+
+#define OCF_EXIT_PERIODIC_INQUIRY      0x0004
+
+#define OCF_CREATE_CONN                        0x0005
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint16_t        pkt_type;
+       uint8_t         pscan_rep_mode;
+       uint8_t         pscan_mode;
+       uint16_t        clock_offset;
+       uint8_t         role_switch;
+} __attribute__ ((packed)) create_conn_cp;
+#define CREATE_CONN_CP_SIZE 13
+
+#define OCF_DISCONNECT                 0x0006
+typedef struct {
+       uint16_t        handle;
+       uint8_t         reason;
+} __attribute__ ((packed)) disconnect_cp;
+#define DISCONNECT_CP_SIZE 3
+
+#define OCF_ADD_SCO                    0x0007
+typedef struct {
+       uint16_t        handle;
+       uint16_t        pkt_type;
+} __attribute__ ((packed)) add_sco_cp;
+#define ADD_SCO_CP_SIZE 4
+
+#define OCF_CREATE_CONN_CANCEL         0x0008
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) create_conn_cancel_cp;
+#define CREATE_CONN_CANCEL_CP_SIZE 6
+
+#define OCF_ACCEPT_CONN_REQ            0x0009
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         role;
+} __attribute__ ((packed)) accept_conn_req_cp;
+#define ACCEPT_CONN_REQ_CP_SIZE        7
+
+#define OCF_REJECT_CONN_REQ            0x000A
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         reason;
+} __attribute__ ((packed)) reject_conn_req_cp;
+#define REJECT_CONN_REQ_CP_SIZE        7
+
+#define OCF_LINK_KEY_REPLY             0x000B
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         link_key[16];
+} __attribute__ ((packed)) link_key_reply_cp;
+#define LINK_KEY_REPLY_CP_SIZE 22
+
+#define OCF_LINK_KEY_NEG_REPLY         0x000C
+
+#define OCF_PIN_CODE_REPLY             0x000D
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         pin_len;
+       uint8_t         pin_code[16];
+} __attribute__ ((packed)) pin_code_reply_cp;
+#define PIN_CODE_REPLY_CP_SIZE 23
+
+#define OCF_PIN_CODE_NEG_REPLY         0x000E
+
+#define OCF_SET_CONN_PTYPE             0x000F
+typedef struct {
+       uint16_t         handle;
+       uint16_t         pkt_type;
+} __attribute__ ((packed)) set_conn_ptype_cp;
+#define SET_CONN_PTYPE_CP_SIZE 4
+
+#define OCF_AUTH_REQUESTED             0x0011
+typedef struct {
+       uint16_t         handle;
+} __attribute__ ((packed)) auth_requested_cp;
+#define AUTH_REQUESTED_CP_SIZE 2
+
+#define OCF_SET_CONN_ENCRYPT           0x0013
+typedef struct {
+       uint16_t        handle;
+       uint8_t         encrypt;
+} __attribute__ ((packed)) set_conn_encrypt_cp;
+#define SET_CONN_ENCRYPT_CP_SIZE 3
+
+#define OCF_CHANGE_CONN_LINK_KEY       0x0015
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) change_conn_link_key_cp;
+#define CHANGE_CONN_LINK_KEY_CP_SIZE 2
+
+#define OCF_MASTER_LINK_KEY            0x0017
+typedef struct {
+       uint8_t         key_flag;
+} __attribute__ ((packed)) master_link_key_cp;
+#define MASTER_LINK_KEY_CP_SIZE 1
+
+#define OCF_REMOTE_NAME_REQ            0x0019
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         pscan_rep_mode;
+       uint8_t         pscan_mode;
+       uint16_t        clock_offset;
+} __attribute__ ((packed)) remote_name_req_cp;
+#define REMOTE_NAME_REQ_CP_SIZE 10
+
+#define OCF_REMOTE_NAME_REQ_CANCEL     0x001A
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) remote_name_req_cancel_cp;
+#define REMOTE_NAME_REQ_CANCEL_CP_SIZE 6
+
+#define OCF_READ_REMOTE_FEATURES       0x001B
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) read_remote_features_cp;
+#define READ_REMOTE_FEATURES_CP_SIZE 2
+
+#define OCF_READ_REMOTE_EXT_FEATURES   0x001C
+typedef struct {
+       uint16_t        handle;
+       uint8_t         page_num;
+} __attribute__ ((packed)) read_remote_ext_features_cp;
+#define READ_REMOTE_EXT_FEATURES_CP_SIZE 3
+
+#define OCF_READ_REMOTE_VERSION                0x001D
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) read_remote_version_cp;
+#define READ_REMOTE_VERSION_CP_SIZE 2
+
+#define OCF_READ_CLOCK_OFFSET          0x001F
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) read_clock_offset_cp;
+#define READ_CLOCK_OFFSET_CP_SIZE 2
+
+#define OCF_READ_LMP_HANDLE            0x0020
+
+#define OCF_SETUP_SYNC_CONN            0x0028
+typedef struct {
+       uint16_t        handle;
+       uint32_t        tx_bandwith;
+       uint32_t        rx_bandwith;
+       uint16_t        max_latency;
+       uint16_t        voice_setting;
+       uint8_t         retrans_effort;
+       uint16_t        pkt_type;
+} __attribute__ ((packed)) setup_sync_conn_cp;
+#define SETUP_SYNC_CONN_CP_SIZE 17
+
+#define OCF_ACCEPT_SYNC_CONN_REQ       0x0029
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint32_t        tx_bandwith;
+       uint32_t        rx_bandwith;
+       uint16_t        max_latency;
+       uint16_t        voice_setting;
+       uint8_t         retrans_effort;
+       uint16_t        pkt_type;
+} __attribute__ ((packed)) accept_sync_conn_req_cp;
+#define ACCEPT_SYNC_CONN_REQ_CP_SIZE 21
+
+#define OCF_REJECT_SYNC_CONN_REQ       0x002A
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         reason;
+} __attribute__ ((packed)) reject_sync_conn_req_cp;
+#define REJECT_SYNC_CONN_REQ_CP_SIZE 7
+
+#define OCF_IO_CAPABILITY_REPLY                0x002B
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         capability;
+       uint8_t         oob_data;
+       uint8_t         authentication;
+} __attribute__ ((packed)) io_capability_reply_cp;
+#define IO_CAPABILITY_REPLY_CP_SIZE 9
+
+#define OCF_USER_CONFIRM_REPLY         0x002C
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) user_confirm_reply_cp;
+#define USER_CONFIRM_REPLY_CP_SIZE 6
+
+#define OCF_USER_CONFIRM_NEG_REPLY     0x002D
+
+#define OCF_USER_PASSKEY_REPLY         0x002E
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint32_t        passkey;
+} __attribute__ ((packed)) user_passkey_reply_cp;
+#define USER_PASSKEY_REPLY_CP_SIZE 10
+
+#define OCF_USER_PASSKEY_NEG_REPLY     0x002F
+
+#define OCF_REMOTE_OOB_DATA_REPLY      0x0030
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         hash[16];
+       uint8_t         randomizer[16];
+} __attribute__ ((packed)) remote_oob_data_reply_cp;
+#define REMOTE_OOB_DATA_REPLY_CP_SIZE 38
+
+#define OCF_REMOTE_OOB_DATA_NEG_REPLY  0x0033
+
+#define OCF_IO_CAPABILITY_NEG_REPLY    0x0034
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         reason;
+} __attribute__ ((packed)) io_capability_neg_reply_cp;
+#define IO_CAPABILITY_NEG_REPLY_CP_SIZE 7
+
+#define OCF_CREATE_PHYSICAL_LINK               0x0035
+typedef struct {
+       uint8_t         handle;
+       uint8_t         key_length;
+       uint8_t         key_type;
+       uint8_t         key[32];
+} __attribute__ ((packed)) create_physical_link_cp;
+#define CREATE_PHYSICAL_LINK_CP_SIZE 35
+
+#define OCF_ACCEPT_PHYSICAL_LINK               0x0036
+
+#define OCF_DISCONNECT_PHYSICAL_LINK           0x0037
+typedef struct {
+       uint8_t         handle;
+       uint8_t         reason;
+} __attribute__ ((packed)) disconnect_physical_link_cp;
+#define DISCONNECT_PHYSICAL_LINK_CP_SIZE 2
+
+#define OCF_CREATE_LOGICAL_LINK                0x0038
+typedef struct {
+       uint8_t         handle;
+       uint8_t         tx_flow[16];
+       uint8_t         rx_flow[16];
+} __attribute__ ((packed)) create_logical_link_cp;
+#define CREATE_LOGICAL_LINK_CP_SIZE 33
+
+#define OCF_ACCEPT_LOGICAL_LINK                0x0039
+
+#define OCF_DISCONNECT_LOGICAL_LINK            0x003A
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) disconnect_logical_link_cp;
+#define DISCONNECT_LOGICAL_LINK_CP_SIZE 2
+
+#define OCF_LOGICAL_LINK_CANCEL                0x003B
+typedef struct {
+       uint8_t         handle;
+       uint8_t         tx_flow_id;
+} __attribute__ ((packed)) cancel_logical_link_cp;
+#define LOGICAL_LINK_CANCEL_CP_SIZE 2
+typedef struct {
+       uint8_t         status;
+       uint8_t         handle;
+       uint8_t         tx_flow_id;
+} __attribute__ ((packed)) cancel_logical_link_rp;
+#define LOGICAL_LINK_CANCEL_RP_SIZE 3
+
+#define OCF_FLOW_SPEC_MODIFY           0x003C
+
+/* Link Policy */
+#define OGF_LINK_POLICY                0x02
+
+#define OCF_HOLD_MODE                  0x0001
+typedef struct {
+       uint16_t        handle;
+       uint16_t        max_interval;
+       uint16_t        min_interval;
+} __attribute__ ((packed)) hold_mode_cp;
+#define HOLD_MODE_CP_SIZE 6
+
+#define OCF_SNIFF_MODE                 0x0003
+typedef struct {
+       uint16_t        handle;
+       uint16_t        max_interval;
+       uint16_t        min_interval;
+       uint16_t        attempt;
+       uint16_t        timeout;
+} __attribute__ ((packed)) sniff_mode_cp;
+#define SNIFF_MODE_CP_SIZE 10
+
+#define OCF_EXIT_SNIFF_MODE            0x0004
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) exit_sniff_mode_cp;
+#define EXIT_SNIFF_MODE_CP_SIZE 2
+
+#define OCF_PARK_MODE                  0x0005
+typedef struct {
+       uint16_t        handle;
+       uint16_t        max_interval;
+       uint16_t        min_interval;
+} __attribute__ ((packed)) park_mode_cp;
+#define PARK_MODE_CP_SIZE 6
+
+#define OCF_EXIT_PARK_MODE             0x0006
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) exit_park_mode_cp;
+#define EXIT_PARK_MODE_CP_SIZE 2
+
+#define OCF_QOS_SETUP                  0x0007
+typedef struct {
+       uint8_t         service_type;           /* 1 = best effort */
+       uint32_t        token_rate;             /* Byte per seconds */
+       uint32_t        peak_bandwidth;         /* Byte per seconds */
+       uint32_t        latency;                /* Microseconds */
+       uint32_t        delay_variation;        /* Microseconds */
+} __attribute__ ((packed)) hci_qos;
+#define HCI_QOS_CP_SIZE 17
+typedef struct {
+       uint16_t        handle;
+       uint8_t         flags;                  /* Reserved */
+       hci_qos         qos;
+} __attribute__ ((packed)) qos_setup_cp;
+#define QOS_SETUP_CP_SIZE (3 + HCI_QOS_CP_SIZE)
+
+#define OCF_ROLE_DISCOVERY             0x0009
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) role_discovery_cp;
+#define ROLE_DISCOVERY_CP_SIZE 2
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         role;
+} __attribute__ ((packed)) role_discovery_rp;
+#define ROLE_DISCOVERY_RP_SIZE 4
+
+#define OCF_SWITCH_ROLE                        0x000B
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         role;
+} __attribute__ ((packed)) switch_role_cp;
+#define SWITCH_ROLE_CP_SIZE 7
+
+#define OCF_READ_LINK_POLICY           0x000C
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) read_link_policy_cp;
+#define READ_LINK_POLICY_CP_SIZE 2
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint16_t        policy;
+} __attribute__ ((packed)) read_link_policy_rp;
+#define READ_LINK_POLICY_RP_SIZE 5
+
+#define OCF_WRITE_LINK_POLICY          0x000D
+typedef struct {
+       uint16_t        handle;
+       uint16_t        policy;
+} __attribute__ ((packed)) write_link_policy_cp;
+#define WRITE_LINK_POLICY_CP_SIZE 4
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+} __attribute__ ((packed)) write_link_policy_rp;
+#define WRITE_LINK_POLICY_RP_SIZE 3
+
+#define OCF_READ_DEFAULT_LINK_POLICY   0x000E
+
+#define OCF_WRITE_DEFAULT_LINK_POLICY  0x000F
+
+#define OCF_FLOW_SPECIFICATION         0x0010
+
+#define OCF_SNIFF_SUBRATING            0x0011
+typedef struct {
+       uint16_t        handle;
+       uint16_t        max_latency;
+       uint16_t        min_remote_timeout;
+       uint16_t        min_local_timeout;
+} __attribute__ ((packed)) sniff_subrating_cp;
+#define SNIFF_SUBRATING_CP_SIZE 8
+
+/* Host Controller and Baseband */
+#define OGF_HOST_CTL           0x03
+
+#define OCF_SET_EVENT_MASK             0x0001
+typedef struct {
+       uint8_t         mask[8];
+} __attribute__ ((packed)) set_event_mask_cp;
+#define SET_EVENT_MASK_CP_SIZE 8
+
+#define OCF_RESET                      0x0003
+
+#define OCF_SET_EVENT_FLT              0x0005
+typedef struct {
+       uint8_t         flt_type;
+       uint8_t         cond_type;
+       uint8_t         condition[0];
+} __attribute__ ((packed)) set_event_flt_cp;
+#define SET_EVENT_FLT_CP_SIZE 2
+
+/* Filter types */
+#define FLT_CLEAR_ALL                  0x00
+#define FLT_INQ_RESULT                 0x01
+#define FLT_CONN_SETUP                 0x02
+/* INQ_RESULT Condition types */
+#define INQ_RESULT_RETURN_ALL          0x00
+#define INQ_RESULT_RETURN_CLASS                0x01
+#define INQ_RESULT_RETURN_BDADDR       0x02
+/* CONN_SETUP Condition types */
+#define CONN_SETUP_ALLOW_ALL           0x00
+#define CONN_SETUP_ALLOW_CLASS         0x01
+#define CONN_SETUP_ALLOW_BDADDR                0x02
+/* CONN_SETUP Conditions */
+#define CONN_SETUP_AUTO_OFF            0x01
+#define CONN_SETUP_AUTO_ON             0x02
+
+#define OCF_FLUSH                      0x0008
+
+#define OCF_READ_PIN_TYPE              0x0009
+typedef struct {
+       uint8_t         status;
+       uint8_t         pin_type;
+} __attribute__ ((packed)) read_pin_type_rp;
+#define READ_PIN_TYPE_RP_SIZE 2
+
+#define OCF_WRITE_PIN_TYPE             0x000A
+typedef struct {
+       uint8_t         pin_type;
+} __attribute__ ((packed)) write_pin_type_cp;
+#define WRITE_PIN_TYPE_CP_SIZE 1
+
+#define OCF_CREATE_NEW_UNIT_KEY                0x000B
+
+#define OCF_READ_STORED_LINK_KEY       0x000D
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         read_all;
+} __attribute__ ((packed)) read_stored_link_key_cp;
+#define READ_STORED_LINK_KEY_CP_SIZE 7
+typedef struct {
+       uint8_t         status;
+       uint16_t        max_keys;
+       uint16_t        num_keys;
+} __attribute__ ((packed)) read_stored_link_key_rp;
+#define READ_STORED_LINK_KEY_RP_SIZE 5
+
+#define OCF_WRITE_STORED_LINK_KEY      0x0011
+typedef struct {
+       uint8_t         num_keys;
+       /* variable length part */
+} __attribute__ ((packed)) write_stored_link_key_cp;
+#define WRITE_STORED_LINK_KEY_CP_SIZE 1
+typedef struct {
+       uint8_t         status;
+       uint8_t         num_keys;
+} __attribute__ ((packed)) write_stored_link_key_rp;
+#define READ_WRITE_LINK_KEY_RP_SIZE 2
+
+#define OCF_DELETE_STORED_LINK_KEY     0x0012
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         delete_all;
+} __attribute__ ((packed)) delete_stored_link_key_cp;
+#define DELETE_STORED_LINK_KEY_CP_SIZE 7
+typedef struct {
+       uint8_t         status;
+       uint16_t        num_keys;
+} __attribute__ ((packed)) delete_stored_link_key_rp;
+#define DELETE_STORED_LINK_KEY_RP_SIZE 3
+
+#define HCI_MAX_NAME_LENGTH            248
+
+#define OCF_CHANGE_LOCAL_NAME          0x0013
+typedef struct {
+       uint8_t         name[HCI_MAX_NAME_LENGTH];
+} __attribute__ ((packed)) change_local_name_cp;
+#define CHANGE_LOCAL_NAME_CP_SIZE 248
+
+#define OCF_READ_LOCAL_NAME            0x0014
+typedef struct {
+       uint8_t         status;
+       uint8_t         name[HCI_MAX_NAME_LENGTH];
+} __attribute__ ((packed)) read_local_name_rp;
+#define READ_LOCAL_NAME_RP_SIZE 249
+
+#define OCF_READ_CONN_ACCEPT_TIMEOUT   0x0015
+typedef struct {
+       uint8_t         status;
+       uint16_t        timeout;
+} __attribute__ ((packed)) read_conn_accept_timeout_rp;
+#define READ_CONN_ACCEPT_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_CONN_ACCEPT_TIMEOUT  0x0016
+typedef struct {
+       uint16_t        timeout;
+} __attribute__ ((packed)) write_conn_accept_timeout_cp;
+#define WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE 2
+
+#define OCF_READ_PAGE_TIMEOUT          0x0017
+typedef struct {
+       uint8_t         status;
+       uint16_t        timeout;
+} __attribute__ ((packed)) read_page_timeout_rp;
+#define READ_PAGE_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_PAGE_TIMEOUT         0x0018
+typedef struct {
+       uint16_t        timeout;
+} __attribute__ ((packed)) write_page_timeout_cp;
+#define WRITE_PAGE_TIMEOUT_CP_SIZE 2
+
+#define OCF_READ_SCAN_ENABLE           0x0019
+typedef struct {
+       uint8_t         status;
+       uint8_t         enable;
+} __attribute__ ((packed)) read_scan_enable_rp;
+#define READ_SCAN_ENABLE_RP_SIZE 2
+
+#define OCF_WRITE_SCAN_ENABLE          0x001A
+       #define SCAN_DISABLED           0x00
+       #define SCAN_INQUIRY            0x01
+       #define SCAN_PAGE               0x02
+
+#define OCF_READ_PAGE_ACTIVITY         0x001B
+typedef struct {
+       uint8_t         status;
+       uint16_t        interval;
+       uint16_t        window;
+} __attribute__ ((packed)) read_page_activity_rp;
+#define READ_PAGE_ACTIVITY_RP_SIZE 5
+
+#define OCF_WRITE_PAGE_ACTIVITY                0x001C
+typedef struct {
+       uint16_t        interval;
+       uint16_t        window;
+} __attribute__ ((packed)) write_page_activity_cp;
+#define WRITE_PAGE_ACTIVITY_CP_SIZE 4
+
+#define OCF_READ_INQ_ACTIVITY          0x001D
+typedef struct {
+       uint8_t         status;
+       uint16_t        interval;
+       uint16_t        window;
+} __attribute__ ((packed)) read_inq_activity_rp;
+#define READ_INQ_ACTIVITY_RP_SIZE 5
+
+#define OCF_WRITE_INQ_ACTIVITY         0x001E
+typedef struct {
+       uint16_t        interval;
+       uint16_t        window;
+} __attribute__ ((packed)) write_inq_activity_cp;
+#define WRITE_INQ_ACTIVITY_CP_SIZE 4
+
+#define OCF_READ_AUTH_ENABLE           0x001F
+
+#define OCF_WRITE_AUTH_ENABLE          0x0020
+       #define AUTH_DISABLED           0x00
+       #define AUTH_ENABLED            0x01
+
+#define OCF_READ_ENCRYPT_MODE          0x0021
+
+#define OCF_WRITE_ENCRYPT_MODE         0x0022
+       #define ENCRYPT_DISABLED        0x00
+       #define ENCRYPT_P2P             0x01
+       #define ENCRYPT_BOTH            0x02
+
+#define OCF_READ_CLASS_OF_DEV          0x0023
+typedef struct {
+       uint8_t         status;
+       uint8_t         dev_class[3];
+} __attribute__ ((packed)) read_class_of_dev_rp;
+#define READ_CLASS_OF_DEV_RP_SIZE 4
+
+#define OCF_WRITE_CLASS_OF_DEV         0x0024
+typedef struct {
+       uint8_t         dev_class[3];
+} __attribute__ ((packed)) write_class_of_dev_cp;
+#define WRITE_CLASS_OF_DEV_CP_SIZE 3
+
+#define OCF_READ_VOICE_SETTING         0x0025
+typedef struct {
+       uint8_t         status;
+       uint16_t        voice_setting;
+} __attribute__ ((packed)) read_voice_setting_rp;
+#define READ_VOICE_SETTING_RP_SIZE 3
+
+#define OCF_WRITE_VOICE_SETTING                0x0026
+typedef struct {
+       uint16_t        voice_setting;
+} __attribute__ ((packed)) write_voice_setting_cp;
+#define WRITE_VOICE_SETTING_CP_SIZE 2
+
+#define OCF_READ_AUTOMATIC_FLUSH_TIMEOUT       0x0027
+
+#define OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT      0x0028
+
+#define OCF_READ_NUM_BROADCAST_RETRANS 0x0029
+
+#define OCF_WRITE_NUM_BROADCAST_RETRANS        0x002A
+
+#define OCF_READ_HOLD_MODE_ACTIVITY    0x002B
+
+#define OCF_WRITE_HOLD_MODE_ACTIVITY   0x002C
+
+#define OCF_READ_TRANSMIT_POWER_LEVEL  0x002D
+typedef struct {
+       uint16_t        handle;
+       uint8_t         type;
+} __attribute__ ((packed)) read_transmit_power_level_cp;
+#define READ_TRANSMIT_POWER_LEVEL_CP_SIZE 3
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       int8_t          level;
+} __attribute__ ((packed)) read_transmit_power_level_rp;
+#define READ_TRANSMIT_POWER_LEVEL_RP_SIZE 4
+
+#define OCF_READ_SYNC_FLOW_ENABLE      0x002E
+
+#define OCF_WRITE_SYNC_FLOW_ENABLE     0x002F
+
+#define OCF_SET_CONTROLLER_TO_HOST_FC  0x0031
+
+#define OCF_HOST_BUFFER_SIZE           0x0033
+typedef struct {
+       uint16_t        acl_mtu;
+       uint8_t         sco_mtu;
+       uint16_t        acl_max_pkt;
+       uint16_t        sco_max_pkt;
+} __attribute__ ((packed)) host_buffer_size_cp;
+#define HOST_BUFFER_SIZE_CP_SIZE 7
+
+#define OCF_HOST_NUM_COMP_PKTS         0x0035
+typedef struct {
+       uint8_t         num_hndl;
+       /* variable length part */
+} __attribute__ ((packed)) host_num_comp_pkts_cp;
+#define HOST_NUM_COMP_PKTS_CP_SIZE 1
+
+#define OCF_READ_LINK_SUPERVISION_TIMEOUT      0x0036
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint16_t        timeout;
+} __attribute__ ((packed)) read_link_supervision_timeout_rp;
+#define READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE 5
+
+#define OCF_WRITE_LINK_SUPERVISION_TIMEOUT     0x0037
+typedef struct {
+       uint16_t        handle;
+       uint16_t        timeout;
+} __attribute__ ((packed)) write_link_supervision_timeout_cp;
+#define WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE 4
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+} __attribute__ ((packed)) write_link_supervision_timeout_rp;
+#define WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE 3
+
+#define OCF_READ_NUM_SUPPORTED_IAC     0x0038
+
+#define MAX_IAC_LAP 0x40
+#define OCF_READ_CURRENT_IAC_LAP       0x0039
+typedef struct {
+       uint8_t         status;
+       uint8_t         num_current_iac;
+       uint8_t         lap[MAX_IAC_LAP][3];
+} __attribute__ ((packed)) read_current_iac_lap_rp;
+#define READ_CURRENT_IAC_LAP_RP_SIZE 2+3*MAX_IAC_LAP
+
+#define OCF_WRITE_CURRENT_IAC_LAP      0x003A
+typedef struct {
+       uint8_t         num_current_iac;
+       uint8_t         lap[MAX_IAC_LAP][3];
+} __attribute__ ((packed)) write_current_iac_lap_cp;
+#define WRITE_CURRENT_IAC_LAP_CP_SIZE 1+3*MAX_IAC_LAP
+
+#define OCF_READ_PAGE_SCAN_PERIOD_MODE 0x003B
+
+#define OCF_WRITE_PAGE_SCAN_PERIOD_MODE        0x003C
+
+#define OCF_READ_PAGE_SCAN_MODE                0x003D
+
+#define OCF_WRITE_PAGE_SCAN_MODE       0x003E
+
+#define OCF_SET_AFH_CLASSIFICATION     0x003F
+typedef struct {
+       uint8_t         map[10];
+} __attribute__ ((packed)) set_afh_classification_cp;
+#define SET_AFH_CLASSIFICATION_CP_SIZE 10
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) set_afh_classification_rp;
+#define SET_AFH_CLASSIFICATION_RP_SIZE 1
+
+#define OCF_READ_INQUIRY_SCAN_TYPE     0x0042
+typedef struct {
+       uint8_t         status;
+       uint8_t         type;
+} __attribute__ ((packed)) read_inquiry_scan_type_rp;
+#define READ_INQUIRY_SCAN_TYPE_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_SCAN_TYPE    0x0043
+typedef struct {
+       uint8_t         type;
+} __attribute__ ((packed)) write_inquiry_scan_type_cp;
+#define WRITE_INQUIRY_SCAN_TYPE_CP_SIZE 1
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) write_inquiry_scan_type_rp;
+#define WRITE_INQUIRY_SCAN_TYPE_RP_SIZE 1
+
+#define OCF_READ_INQUIRY_MODE          0x0044
+typedef struct {
+       uint8_t         status;
+       uint8_t         mode;
+} __attribute__ ((packed)) read_inquiry_mode_rp;
+#define READ_INQUIRY_MODE_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_MODE         0x0045
+typedef struct {
+       uint8_t         mode;
+} __attribute__ ((packed)) write_inquiry_mode_cp;
+#define WRITE_INQUIRY_MODE_CP_SIZE 1
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) write_inquiry_mode_rp;
+#define WRITE_INQUIRY_MODE_RP_SIZE 1
+
+#define OCF_READ_PAGE_SCAN_TYPE                0x0046
+
+#define OCF_WRITE_PAGE_SCAN_TYPE       0x0047
+       #define PAGE_SCAN_TYPE_STANDARD         0x00
+       #define PAGE_SCAN_TYPE_INTERLACED       0x01
+
+#define OCF_READ_AFH_MODE              0x0048
+typedef struct {
+       uint8_t         status;
+       uint8_t         mode;
+} __attribute__ ((packed)) read_afh_mode_rp;
+#define READ_AFH_MODE_RP_SIZE 2
+
+#define OCF_WRITE_AFH_MODE             0x0049
+typedef struct {
+       uint8_t         mode;
+} __attribute__ ((packed)) write_afh_mode_cp;
+#define WRITE_AFH_MODE_CP_SIZE 1
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) write_afh_mode_rp;
+#define WRITE_AFH_MODE_RP_SIZE 1
+
+#define HCI_MAX_EIR_LENGTH             240
+
+#define OCF_READ_EXT_INQUIRY_RESPONSE  0x0051
+typedef struct {
+       uint8_t         status;
+       uint8_t         fec;
+       uint8_t         data[HCI_MAX_EIR_LENGTH];
+} __attribute__ ((packed)) read_ext_inquiry_response_rp;
+#define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242
+
+#define OCF_WRITE_EXT_INQUIRY_RESPONSE 0x0052
+typedef struct {
+       uint8_t         fec;
+       uint8_t         data[HCI_MAX_EIR_LENGTH];
+} __attribute__ ((packed)) write_ext_inquiry_response_cp;
+#define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) write_ext_inquiry_response_rp;
+#define WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE 1
+
+#define OCF_REFRESH_ENCRYPTION_KEY     0x0053
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) refresh_encryption_key_cp;
+#define REFRESH_ENCRYPTION_KEY_CP_SIZE 2
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) refresh_encryption_key_rp;
+#define REFRESH_ENCRYPTION_KEY_RP_SIZE 1
+
+#define OCF_READ_SIMPLE_PAIRING_MODE   0x0055
+typedef struct {
+       uint8_t         status;
+       uint8_t         mode;
+} __attribute__ ((packed)) read_simple_pairing_mode_rp;
+#define READ_SIMPLE_PAIRING_MODE_RP_SIZE 2
+
+#define OCF_WRITE_SIMPLE_PAIRING_MODE  0x0056
+typedef struct {
+       uint8_t         mode;
+} __attribute__ ((packed)) write_simple_pairing_mode_cp;
+#define WRITE_SIMPLE_PAIRING_MODE_CP_SIZE 1
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) write_simple_pairing_mode_rp;
+#define WRITE_SIMPLE_PAIRING_MODE_RP_SIZE 1
+
+#define OCF_READ_LOCAL_OOB_DATA                0x0057
+typedef struct {
+       uint8_t         status;
+       uint8_t         hash[16];
+       uint8_t         randomizer[16];
+} __attribute__ ((packed)) read_local_oob_data_rp;
+#define READ_LOCAL_OOB_DATA_RP_SIZE 33
+
+#define OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL   0x0058
+typedef struct {
+       uint8_t         status;
+       int8_t          level;
+} __attribute__ ((packed)) read_inq_response_tx_power_level_rp;
+#define READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE 2
+
+#define OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL  0x0058
+typedef struct {
+       uint8_t         status;
+       int8_t          level;
+} __attribute__ ((packed)) read_inquiry_transmit_power_level_rp;
+#define READ_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE 2
+
+#define OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL 0x0059
+typedef struct {
+       int8_t          level;
+} __attribute__ ((packed)) write_inquiry_transmit_power_level_cp;
+#define WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE 1
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) write_inquiry_transmit_power_level_rp;
+#define WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE 1
+
+#define OCF_READ_DEFAULT_ERROR_DATA_REPORTING  0x005A
+typedef struct {
+       uint8_t         status;
+       uint8_t         reporting;
+} __attribute__ ((packed)) read_default_error_data_reporting_rp;
+#define READ_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE 2
+
+#define OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING 0x005B
+typedef struct {
+       uint8_t         reporting;
+} __attribute__ ((packed)) write_default_error_data_reporting_cp;
+#define WRITE_DEFAULT_ERROR_DATA_REPORTING_CP_SIZE 1
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) write_default_error_data_reporting_rp;
+#define WRITE_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE 1
+
+#define OCF_ENHANCED_FLUSH             0x005F
+typedef struct {
+       uint16_t        handle;
+       uint8_t         type;
+} __attribute__ ((packed)) enhanced_flush_cp;
+#define ENHANCED_FLUSH_CP_SIZE 3
+
+#define OCF_SEND_KEYPRESS_NOTIFY       0x0060
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         type;
+} __attribute__ ((packed)) send_keypress_notify_cp;
+#define SEND_KEYPRESS_NOTIFY_CP_SIZE 7
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) send_keypress_notify_rp;
+#define SEND_KEYPRESS_NOTIFY_RP_SIZE 1
+
+#define OCF_READ_LOGICAL_LINK_ACCEPT_TIMEOUT    0x0061
+typedef struct {
+       uint8_t         status;
+       uint16_t        timeout;
+} __attribute__ ((packed)) read_log_link_accept_timeout_rp;
+#define READ_LOGICAL_LINK_ACCEPT_TIMEOUT_RP_SIZE 3
+
+#define OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT  0x0062
+typedef struct {
+       uint16_t        timeout;
+} __attribute__ ((packed)) write_log_link_accept_timeout_cp;
+#define WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_CP_SIZE 2
+
+#define OCF_SET_EVENT_MASK_PAGE_2      0x0063
+
+#define OCF_READ_LOCATION_DATA         0x0064
+
+#define OCF_WRITE_LOCATION_DATA        0x0065
+
+#define OCF_READ_FLOW_CONTROL_MODE     0x0066
+
+#define OCF_WRITE_FLOW_CONTROL_MODE    0x0067
+
+#define OCF_READ_ENHANCED_TRANSMIT_POWER_LEVEL 0x0068
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       int8_t          level_gfsk;
+       int8_t          level_dqpsk;
+       int8_t          level_8dpsk;
+} __attribute__ ((packed)) read_enhanced_transmit_power_level_rp;
+#define READ_ENHANCED_TRANSMIT_POWER_LEVEL_RP_SIZE 6
+
+#define OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT     0x0069
+typedef struct {
+       uint8_t         status;
+       uint32_t        timeout;
+} __attribute__ ((packed)) read_best_effort_flush_timeout_rp;
+#define READ_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 5
+
+#define OCF_WRITE_BEST_EFFORT_FLUSH_TIMEOUT    0x006A
+typedef struct {
+       uint16_t        handle;
+       uint32_t        timeout;
+} __attribute__ ((packed)) write_best_effort_flush_timeout_cp;
+#define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_CP_SIZE 6
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) write_best_effort_flush_timeout_rp;
+#define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 1
+
+#define OCF_READ_LE_HOST_SUPPORTED     0x006C
+typedef struct {
+       uint8_t         status;
+       uint8_t         le;
+       uint8_t         simul;
+} __attribute__ ((packed)) read_le_host_supported_rp;
+#define READ_LE_HOST_SUPPORTED_RP_SIZE 3
+
+#define OCF_WRITE_LE_HOST_SUPPORTED    0x006D
+typedef struct {
+       uint8_t         le;
+       uint8_t         simul;
+} __attribute__ ((packed)) write_le_host_supported_cp;
+#define WRITE_LE_HOST_SUPPORTED_CP_SIZE 2
+
+/* Informational Parameters */
+#define OGF_INFO_PARAM         0x04
+
+#define OCF_READ_LOCAL_VERSION         0x0001
+typedef struct {
+       uint8_t         status;
+       uint8_t         hci_ver;
+       uint16_t        hci_rev;
+       uint8_t         lmp_ver;
+       uint16_t        manufacturer;
+       uint16_t        lmp_subver;
+} __attribute__ ((packed)) read_local_version_rp;
+#define READ_LOCAL_VERSION_RP_SIZE 9
+
+#define OCF_READ_LOCAL_COMMANDS                0x0002
+typedef struct {
+       uint8_t         status;
+       uint8_t         commands[64];
+} __attribute__ ((packed)) read_local_commands_rp;
+#define READ_LOCAL_COMMANDS_RP_SIZE 65
+
+#define OCF_READ_LOCAL_FEATURES                0x0003
+typedef struct {
+       uint8_t         status;
+       uint8_t         features[8];
+} __attribute__ ((packed)) read_local_features_rp;
+#define READ_LOCAL_FEATURES_RP_SIZE 9
+
+#define OCF_READ_LOCAL_EXT_FEATURES    0x0004
+typedef struct {
+       uint8_t         page_num;
+} __attribute__ ((packed)) read_local_ext_features_cp;
+#define READ_LOCAL_EXT_FEATURES_CP_SIZE 1
+typedef struct {
+       uint8_t         status;
+       uint8_t         page_num;
+       uint8_t         max_page_num;
+       uint8_t         features[8];
+} __attribute__ ((packed)) read_local_ext_features_rp;
+#define READ_LOCAL_EXT_FEATURES_RP_SIZE 11
+
+#define OCF_READ_BUFFER_SIZE           0x0005
+typedef struct {
+       uint8_t         status;
+       uint16_t        acl_mtu;
+       uint8_t         sco_mtu;
+       uint16_t        acl_max_pkt;
+       uint16_t        sco_max_pkt;
+} __attribute__ ((packed)) read_buffer_size_rp;
+#define READ_BUFFER_SIZE_RP_SIZE 8
+
+#define OCF_READ_COUNTRY_CODE          0x0007
+
+#define OCF_READ_BD_ADDR               0x0009
+typedef struct {
+       uint8_t         status;
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) read_bd_addr_rp;
+#define READ_BD_ADDR_RP_SIZE 7
+
+/* Status params */
+#define OGF_STATUS_PARAM       0x05
+
+#define OCF_READ_FAILED_CONTACT_COUNTER                0x0001
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         counter;
+} __attribute__ ((packed)) read_failed_contact_counter_rp;
+#define READ_FAILED_CONTACT_COUNTER_RP_SIZE 4
+
+#define OCF_RESET_FAILED_CONTACT_COUNTER       0x0002
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+} __attribute__ ((packed)) reset_failed_contact_counter_rp;
+#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 4
+
+#define OCF_READ_LINK_QUALITY          0x0003
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         link_quality;
+} __attribute__ ((packed)) read_link_quality_rp;
+#define READ_LINK_QUALITY_RP_SIZE 4
+
+#define OCF_READ_RSSI                  0x0005
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       int8_t          rssi;
+} __attribute__ ((packed)) read_rssi_rp;
+#define READ_RSSI_RP_SIZE 4
+
+#define OCF_READ_AFH_MAP               0x0006
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         mode;
+       uint8_t         map[10];
+} __attribute__ ((packed)) read_afh_map_rp;
+#define READ_AFH_MAP_RP_SIZE 14
+
+#define OCF_READ_CLOCK                 0x0007
+typedef struct {
+       uint16_t        handle;
+       uint8_t         which_clock;
+} __attribute__ ((packed)) read_clock_cp;
+#define READ_CLOCK_CP_SIZE 3
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint32_t        clock;
+       uint16_t        accuracy;
+} __attribute__ ((packed)) read_clock_rp;
+#define READ_CLOCK_RP_SIZE 9
+
+#define OCF_READ_LOCAL_AMP_INFO        0x0009
+typedef struct {
+       uint8_t         status;
+       uint8_t         amp_status;
+       uint32_t        total_bandwidth;
+       uint32_t        max_guaranteed_bandwidth;
+       uint32_t        min_latency;
+       uint32_t        max_pdu_size;
+       uint8_t         controller_type;
+       uint16_t        pal_caps;
+       uint16_t        max_amp_assoc_length;
+       uint32_t        max_flush_timeout;
+       uint32_t        best_effort_flush_timeout;
+} __attribute__ ((packed)) read_local_amp_info_rp;
+#define READ_LOCAL_AMP_INFO_RP_SIZE 31
+
+#define OCF_READ_LOCAL_AMP_ASSOC       0x000A
+typedef struct {
+       uint8_t         handle;
+       uint16_t        len_so_far;
+       uint16_t        max_len;
+} __attribute__ ((packed)) read_local_amp_assoc_cp;
+
+typedef struct {
+       uint8_t         status;
+       uint8_t         handle;
+       uint16_t        rem_len;
+       uint8_t         frag[0];
+} __attribute__ ((packed)) read_local_amp_assoc_rp;
+
+#define OCF_WRITE_REMOTE_AMP_ASSOC     0x000B
+typedef struct {
+       uint8_t         handle;
+       uint16_t        length_so_far;
+       uint16_t        assoc_length;
+       uint8_t         fragment[HCI_MAX_NAME_LENGTH];
+} __attribute__ ((packed)) write_remote_amp_assoc_cp;
+#define WRITE_REMOTE_AMP_ASSOC_CP_SIZE 253
+typedef struct {
+       uint8_t         status;
+       uint8_t         handle;
+} __attribute__ ((packed)) write_remote_amp_assoc_rp;
+#define WRITE_REMOTE_AMP_ASSOC_RP_SIZE 2
+
+/* Testing commands */
+#define OGF_TESTING_CMD                0x3e
+
+#define OCF_READ_LOOPBACK_MODE                 0x0001
+
+#define OCF_WRITE_LOOPBACK_MODE                        0x0002
+
+#define OCF_ENABLE_DEVICE_UNDER_TEST_MODE      0x0003
+
+#define OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE    0x0004
+typedef struct {
+       uint8_t         mode;
+} __attribute__ ((packed)) write_simple_pairing_debug_mode_cp;
+#define WRITE_SIMPLE_PAIRING_DEBUG_MODE_CP_SIZE 1
+typedef struct {
+       uint8_t         status;
+} __attribute__ ((packed)) write_simple_pairing_debug_mode_rp;
+#define WRITE_SIMPLE_PAIRING_DEBUG_MODE_RP_SIZE 1
+
+/* LE commands */
+#define OGF_LE_CTL             0x08
+
+#define OCF_LE_SET_EVENT_MASK                  0x0001
+typedef struct {
+       uint8_t         mask[8];
+} __attribute__ ((packed)) le_set_event_mask_cp;
+#define LE_SET_EVENT_MASK_CP_SIZE 8
+
+#define OCF_LE_READ_BUFFER_SIZE                        0x0002
+typedef struct {
+       uint8_t         status;
+       uint16_t        pkt_len;
+       uint8_t         max_pkt;
+} __attribute__ ((packed)) le_read_buffer_size_rp;
+#define LE_READ_BUFFER_SIZE_RP_SIZE 4
+
+#define OCF_LE_READ_LOCAL_SUPPORTED_FEATURES   0x0003
+typedef struct {
+       uint8_t         status;
+       uint8_t         features[8];
+} __attribute__ ((packed)) le_read_local_supported_features_rp;
+#define LE_READ_LOCAL_SUPPORTED_FEATURES_RP_SIZE 9
+
+#define OCF_LE_SET_RANDOM_ADDRESS              0x0005
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) le_set_random_address_cp;
+#define LE_SET_RANDOM_ADDRESS_CP_SIZE 6
+
+#define OCF_LE_SET_ADVERTISING_PARAMETERS      0x0006
+typedef struct {
+       uint16_t        min_interval;
+       uint16_t        max_interval;
+       uint8_t         advtype;
+       uint8_t         own_bdaddr_type;
+       uint8_t         direct_bdaddr_type;
+       bdaddr_t        direct_bdaddr;
+       uint8_t         chan_map;
+       uint8_t         filter;
+} __attribute__ ((packed)) le_set_advertising_parameters_cp;
+#define LE_SET_ADVERTISING_PARAMETERS_CP_SIZE 15
+
+#define OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER       0x0007
+typedef struct {
+       uint8_t         status;
+       uint8_t         level;
+} __attribute__ ((packed)) le_read_advertising_channel_tx_power_rp;
+#define LE_READ_ADVERTISING_CHANNEL_TX_POWER_RP_SIZE 2
+
+#define OCF_LE_SET_ADVERTISING_DATA            0x0008
+typedef struct {
+       uint8_t         length;
+       uint8_t         data[31];
+} __attribute__ ((packed)) le_set_advertising_data_cp;
+#define LE_SET_ADVERTISING_DATA_CP_SIZE 32
+
+#define OCF_LE_SET_SCAN_RESPONSE_DATA          0x0009
+typedef struct {
+       uint8_t         length;
+       uint8_t         data[31];
+} __attribute__ ((packed)) le_set_scan_response_data_cp;
+#define LE_SET_SCAN_RESPONSE_DATA_CP_SIZE 32
+
+#define OCF_LE_SET_ADVERTISE_ENABLE            0x000A
+typedef struct {
+       uint8_t         enable;
+} __attribute__ ((packed)) le_set_advertise_enable_cp;
+#define LE_SET_ADVERTISE_ENABLE_CP_SIZE 1
+
+#define OCF_LE_SET_SCAN_PARAMETERS             0x000B
+typedef struct {
+       uint8_t         type;
+       uint16_t        interval;
+       uint16_t        window;
+       uint8_t         own_bdaddr_type;
+       uint8_t         filter;
+} __attribute__ ((packed)) le_set_scan_parameters_cp;
+#define LE_SET_SCAN_PARAMETERS_CP_SIZE 7
+
+#define OCF_LE_SET_SCAN_ENABLE                 0x000C
+typedef struct {
+       uint8_t         enable;
+       uint8_t         filter_dup;
+} __attribute__ ((packed)) le_set_scan_enable_cp;
+#define LE_SET_SCAN_ENABLE_CP_SIZE 2
+
+#define OCF_LE_CREATE_CONN                     0x000D
+typedef struct {
+       uint16_t        interval;
+       uint16_t        window;
+       uint8_t         initiator_filter;
+       uint8_t         peer_bdaddr_type;
+       bdaddr_t        peer_bdaddr;
+       uint8_t         own_bdaddr_type;
+       uint16_t        min_interval;
+       uint16_t        max_interval;
+       uint16_t        latency;
+       uint16_t        supervision_timeout;
+       uint16_t        min_ce_length;
+       uint16_t        max_ce_length;
+} __attribute__ ((packed)) le_create_connection_cp;
+#define LE_CREATE_CONN_CP_SIZE 25
+
+#define OCF_LE_CREATE_CONN_CANCEL              0x000E
+
+#define OCF_LE_READ_WHITE_LIST_SIZE            0x000F
+typedef struct {
+       uint8_t         status;
+       uint8_t         size;
+} __attribute__ ((packed)) le_read_white_list_size_rp;
+#define LE_READ_WHITE_LIST_SIZE_RP_SIZE 2
+
+#define OCF_LE_CLEAR_WHITE_LIST                        0x0010
+
+#define OCF_LE_ADD_DEVICE_TO_WHITE_LIST                0x0011
+typedef struct {
+       uint8_t         bdaddr_type;
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) le_add_device_to_white_list_cp;
+#define LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE 7
+
+#define OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST   0x0012
+typedef struct {
+       uint8_t         bdaddr_type;
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) le_remove_device_from_white_list_cp;
+#define LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE 7
+
+#define OCF_LE_CONN_UPDATE                     0x0013
+typedef struct {
+       uint16_t        handle;
+       uint16_t        min_interval;
+       uint16_t        max_interval;
+       uint16_t        latency;
+       uint16_t        supervision_timeout;
+       uint16_t        min_ce_length;
+       uint16_t        max_ce_length;
+} __attribute__ ((packed)) le_connection_update_cp;
+#define LE_CONN_UPDATE_CP_SIZE 14
+
+#define OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION 0x0014
+typedef struct {
+       uint8_t         map[5];
+} __attribute__ ((packed)) le_set_host_channel_classification_cp;
+#define LE_SET_HOST_CHANNEL_CLASSIFICATION_CP_SIZE 5
+
+#define OCF_LE_READ_CHANNEL_MAP                        0x0015
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) le_read_channel_map_cp;
+#define LE_READ_CHANNEL_MAP_CP_SIZE 2
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         map[5];
+} __attribute__ ((packed)) le_read_channel_map_rp;
+#define LE_READ_CHANNEL_MAP_RP_SIZE 8
+
+#define OCF_LE_READ_REMOTE_USED_FEATURES       0x0016
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) le_read_remote_used_features_cp;
+#define LE_READ_REMOTE_USED_FEATURES_CP_SIZE 2
+
+#define OCF_LE_ENCRYPT                         0x0017
+typedef struct {
+       uint8_t         key[16];
+       uint8_t         plaintext[16];
+} __attribute__ ((packed)) le_encrypt_cp;
+#define LE_ENCRYPT_CP_SIZE 32
+typedef struct {
+       uint8_t         status;
+       uint8_t         data[16];
+} __attribute__ ((packed)) le_encrypt_rp;
+#define LE_ENCRYPT_RP_SIZE 17
+
+#define OCF_LE_RAND                            0x0018
+typedef struct {
+       uint8_t         status;
+       uint64_t        random;
+} __attribute__ ((packed)) le_rand_rp;
+#define LE_RAND_RP_SIZE 9
+
+#define OCF_LE_START_ENCRYPTION                        0x0019
+typedef struct {
+       uint16_t        handle;
+       uint64_t        random;
+       uint16_t        diversifier;
+       uint8_t         key[16];
+} __attribute__ ((packed)) le_start_encryption_cp;
+#define LE_START_ENCRYPTION_CP_SIZE 28
+
+#define OCF_LE_LTK_REPLY                       0x001A
+typedef struct {
+       uint16_t        handle;
+       uint8_t         key[16];
+} __attribute__ ((packed)) le_ltk_reply_cp;
+#define LE_LTK_REPLY_CP_SIZE 18
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+} __attribute__ ((packed)) le_ltk_reply_rp;
+#define LE_LTK_REPLY_RP_SIZE 3
+
+#define OCF_LE_LTK_NEG_REPLY                   0x001B
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) le_ltk_neg_reply_cp;
+#define LE_LTK_NEG_REPLY_CP_SIZE 2
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+} __attribute__ ((packed)) le_ltk_neg_reply_rp;
+#define LE_LTK_NEG_REPLY_RP_SIZE 3
+
+#define OCF_LE_READ_SUPPORTED_STATES           0x001C
+typedef struct {
+       uint8_t         status;
+       uint64_t        states;
+} __attribute__ ((packed)) le_read_supported_states_rp;
+#define LE_READ_SUPPORTED_STATES_RP_SIZE 9
+
+#define OCF_LE_RECEIVER_TEST                   0x001D
+typedef struct {
+       uint8_t         frequency;
+} __attribute__ ((packed)) le_receiver_test_cp;
+#define LE_RECEIVER_TEST_CP_SIZE 1
+
+#define OCF_LE_TRANSMITTER_TEST                        0x001E
+typedef struct {
+       uint8_t         frequency;
+       uint8_t         length;
+       uint8_t         payload;
+} __attribute__ ((packed)) le_transmitter_test_cp;
+#define LE_TRANSMITTER_TEST_CP_SIZE 3
+
+#define OCF_LE_TEST_END                                0x001F
+typedef struct {
+       uint8_t         status;
+       uint16_t        num_pkts;
+} __attribute__ ((packed)) le_test_end_rp;
+#define LE_TEST_END_RP_SIZE 3
+
+/* Vendor specific commands */
+#define OGF_VENDOR_CMD         0x3f
+
+/* ---- HCI Events ---- */
+
+#define EVT_INQUIRY_COMPLETE           0x01
+
+#define EVT_INQUIRY_RESULT             0x02
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         pscan_rep_mode;
+       uint8_t         pscan_period_mode;
+       uint8_t         pscan_mode;
+       uint8_t         dev_class[3];
+       uint16_t        clock_offset;
+} __attribute__ ((packed)) inquiry_info;
+#define INQUIRY_INFO_SIZE 14
+
+#define EVT_CONN_COMPLETE              0x03
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       bdaddr_t        bdaddr;
+       uint8_t         link_type;
+       uint8_t         encr_mode;
+} __attribute__ ((packed)) evt_conn_complete;
+#define EVT_CONN_COMPLETE_SIZE 13
+
+#define EVT_CONN_REQUEST               0x04
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         dev_class[3];
+       uint8_t         link_type;
+} __attribute__ ((packed)) evt_conn_request;
+#define EVT_CONN_REQUEST_SIZE 10
+
+#define EVT_DISCONN_COMPLETE           0x05
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         reason;
+} __attribute__ ((packed)) evt_disconn_complete;
+#define EVT_DISCONN_COMPLETE_SIZE 4
+
+#define EVT_AUTH_COMPLETE              0x06
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+} __attribute__ ((packed)) evt_auth_complete;
+#define EVT_AUTH_COMPLETE_SIZE 3
+
+#define EVT_REMOTE_NAME_REQ_COMPLETE   0x07
+typedef struct {
+       uint8_t         status;
+       bdaddr_t        bdaddr;
+       uint8_t         name[HCI_MAX_NAME_LENGTH];
+} __attribute__ ((packed)) evt_remote_name_req_complete;
+#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255
+
+#define EVT_ENCRYPT_CHANGE             0x08
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         encrypt;
+} __attribute__ ((packed)) evt_encrypt_change;
+#define EVT_ENCRYPT_CHANGE_SIZE 5
+
+#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE      0x09
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+}  __attribute__ ((packed)) evt_change_conn_link_key_complete;
+#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE 3
+
+#define EVT_MASTER_LINK_KEY_COMPLETE           0x0A
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         key_flag;
+} __attribute__ ((packed)) evt_master_link_key_complete;
+#define EVT_MASTER_LINK_KEY_COMPLETE_SIZE 4
+
+#define EVT_READ_REMOTE_FEATURES_COMPLETE      0x0B
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         features[8];
+} __attribute__ ((packed)) evt_read_remote_features_complete;
+#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11
+
+#define EVT_READ_REMOTE_VERSION_COMPLETE       0x0C
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         lmp_ver;
+       uint16_t        manufacturer;
+       uint16_t        lmp_subver;
+} __attribute__ ((packed)) evt_read_remote_version_complete;
+#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8
+
+#define EVT_QOS_SETUP_COMPLETE         0x0D
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         flags;                  /* Reserved */
+       hci_qos         qos;
+} __attribute__ ((packed)) evt_qos_setup_complete;
+#define EVT_QOS_SETUP_COMPLETE_SIZE (4 + HCI_QOS_CP_SIZE)
+
+#define EVT_CMD_COMPLETE               0x0E
+typedef struct {
+       uint8_t         ncmd;
+       uint16_t        opcode;
+} __attribute__ ((packed)) evt_cmd_complete;
+#define EVT_CMD_COMPLETE_SIZE 3
+
+#define EVT_CMD_STATUS                         0x0F
+typedef struct {
+       uint8_t         status;
+       uint8_t         ncmd;
+       uint16_t        opcode;
+} __attribute__ ((packed)) evt_cmd_status;
+#define EVT_CMD_STATUS_SIZE 4
+
+#define EVT_HARDWARE_ERROR             0x10
+typedef struct {
+       uint8_t         code;
+} __attribute__ ((packed)) evt_hardware_error;
+#define EVT_HARDWARE_ERROR_SIZE 1
+
+#define EVT_FLUSH_OCCURRED             0x11
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) evt_flush_occured;
+#define EVT_FLUSH_OCCURRED_SIZE 2
+
+#define EVT_ROLE_CHANGE                        0x12
+typedef struct {
+       uint8_t         status;
+       bdaddr_t        bdaddr;
+       uint8_t         role;
+} __attribute__ ((packed)) evt_role_change;
+#define EVT_ROLE_CHANGE_SIZE 8
+
+#define EVT_NUM_COMP_PKTS              0x13
+typedef struct {
+       uint8_t         num_hndl;
+       /* variable length part */
+} __attribute__ ((packed)) evt_num_comp_pkts;
+#define EVT_NUM_COMP_PKTS_SIZE 1
+
+#define EVT_MODE_CHANGE                        0x14
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         mode;
+       uint16_t        interval;
+} __attribute__ ((packed)) evt_mode_change;
+#define EVT_MODE_CHANGE_SIZE 6
+
+#define EVT_RETURN_LINK_KEYS           0x15
+typedef struct {
+       uint8_t         num_keys;
+       /* variable length part */
+} __attribute__ ((packed)) evt_return_link_keys;
+#define EVT_RETURN_LINK_KEYS_SIZE 1
+
+#define EVT_PIN_CODE_REQ               0x16
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) evt_pin_code_req;
+#define EVT_PIN_CODE_REQ_SIZE 6
+
+#define EVT_LINK_KEY_REQ               0x17
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) evt_link_key_req;
+#define EVT_LINK_KEY_REQ_SIZE 6
+
+#define EVT_LINK_KEY_NOTIFY            0x18
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         link_key[16];
+       uint8_t         key_type;
+} __attribute__ ((packed)) evt_link_key_notify;
+#define EVT_LINK_KEY_NOTIFY_SIZE 23
+
+#define EVT_LOOPBACK_COMMAND           0x19
+
+#define EVT_DATA_BUFFER_OVERFLOW       0x1A
+typedef struct {
+       uint8_t         link_type;
+} __attribute__ ((packed)) evt_data_buffer_overflow;
+#define EVT_DATA_BUFFER_OVERFLOW_SIZE 1
+
+#define EVT_MAX_SLOTS_CHANGE           0x1B
+typedef struct {
+       uint16_t        handle;
+       uint8_t         max_slots;
+} __attribute__ ((packed)) evt_max_slots_change;
+#define EVT_MAX_SLOTS_CHANGE_SIZE 3
+
+#define EVT_READ_CLOCK_OFFSET_COMPLETE 0x1C
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint16_t        clock_offset;
+} __attribute__ ((packed)) evt_read_clock_offset_complete;
+#define EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE 5
+
+#define EVT_CONN_PTYPE_CHANGED         0x1D
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint16_t        ptype;
+} __attribute__ ((packed)) evt_conn_ptype_changed;
+#define EVT_CONN_PTYPE_CHANGED_SIZE 5
+
+#define EVT_QOS_VIOLATION              0x1E
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) evt_qos_violation;
+#define EVT_QOS_VIOLATION_SIZE 2
+
+#define EVT_PSCAN_REP_MODE_CHANGE      0x20
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         pscan_rep_mode;
+} __attribute__ ((packed)) evt_pscan_rep_mode_change;
+#define EVT_PSCAN_REP_MODE_CHANGE_SIZE 7
+
+#define EVT_FLOW_SPEC_COMPLETE         0x21
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         flags;
+       uint8_t         direction;
+       hci_qos         qos;
+} __attribute__ ((packed)) evt_flow_spec_complete;
+#define EVT_FLOW_SPEC_COMPLETE_SIZE (5 + HCI_QOS_CP_SIZE)
+
+#define EVT_INQUIRY_RESULT_WITH_RSSI   0x22
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         pscan_rep_mode;
+       uint8_t         pscan_period_mode;
+       uint8_t         dev_class[3];
+       uint16_t        clock_offset;
+       int8_t          rssi;
+} __attribute__ ((packed)) inquiry_info_with_rssi;
+#define INQUIRY_INFO_WITH_RSSI_SIZE 14
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         pscan_rep_mode;
+       uint8_t         pscan_period_mode;
+       uint8_t         pscan_mode;
+       uint8_t         dev_class[3];
+       uint16_t        clock_offset;
+       int8_t          rssi;
+} __attribute__ ((packed)) inquiry_info_with_rssi_and_pscan_mode;
+#define INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE 15
+
+#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE  0x23
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         page_num;
+       uint8_t         max_page_num;
+       uint8_t         features[8];
+} __attribute__ ((packed)) evt_read_remote_ext_features_complete;
+#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE 13
+
+#define EVT_SYNC_CONN_COMPLETE         0x2C
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       bdaddr_t        bdaddr;
+       uint8_t         link_type;
+       uint8_t         trans_interval;
+       uint8_t         retrans_window;
+       uint16_t        rx_pkt_len;
+       uint16_t        tx_pkt_len;
+       uint8_t         air_mode;
+} __attribute__ ((packed)) evt_sync_conn_complete;
+#define EVT_SYNC_CONN_COMPLETE_SIZE 17
+
+#define EVT_SYNC_CONN_CHANGED          0x2D
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         trans_interval;
+       uint8_t         retrans_window;
+       uint16_t        rx_pkt_len;
+       uint16_t        tx_pkt_len;
+} __attribute__ ((packed)) evt_sync_conn_changed;
+#define EVT_SYNC_CONN_CHANGED_SIZE 9
+
+#define EVT_SNIFF_SUBRATING            0x2E
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint16_t        max_tx_latency;
+       uint16_t        max_rx_latency;
+       uint16_t        min_remote_timeout;
+       uint16_t        min_local_timeout;
+} __attribute__ ((packed)) evt_sniff_subrating;
+#define EVT_SNIFF_SUBRATING_SIZE 11
+
+#define EVT_EXTENDED_INQUIRY_RESULT    0x2F
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         pscan_rep_mode;
+       uint8_t         pscan_period_mode;
+       uint8_t         dev_class[3];
+       uint16_t        clock_offset;
+       int8_t          rssi;
+       uint8_t         data[HCI_MAX_EIR_LENGTH];
+} __attribute__ ((packed)) extended_inquiry_info;
+#define EXTENDED_INQUIRY_INFO_SIZE 254
+
+#define EVT_ENCRYPTION_KEY_REFRESH_COMPLETE    0x30
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+} __attribute__ ((packed)) evt_encryption_key_refresh_complete;
+#define EVT_ENCRYPTION_KEY_REFRESH_COMPLETE_SIZE 3
+
+#define EVT_IO_CAPABILITY_REQUEST      0x31
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) evt_io_capability_request;
+#define EVT_IO_CAPABILITY_REQUEST_SIZE 6
+
+#define EVT_IO_CAPABILITY_RESPONSE     0x32
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         capability;
+       uint8_t         oob_data;
+       uint8_t         authentication;
+} __attribute__ ((packed)) evt_io_capability_response;
+#define EVT_IO_CAPABILITY_RESPONSE_SIZE 9
+
+#define EVT_USER_CONFIRM_REQUEST       0x33
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint32_t        passkey;
+} __attribute__ ((packed)) evt_user_confirm_request;
+#define EVT_USER_CONFIRM_REQUEST_SIZE 10
+
+#define EVT_USER_PASSKEY_REQUEST       0x34
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) evt_user_passkey_request;
+#define EVT_USER_PASSKEY_REQUEST_SIZE 6
+
+#define EVT_REMOTE_OOB_DATA_REQUEST    0x35
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) evt_remote_oob_data_request;
+#define EVT_REMOTE_OOB_DATA_REQUEST_SIZE 6
+
+#define EVT_SIMPLE_PAIRING_COMPLETE    0x36
+typedef struct {
+       uint8_t         status;
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) evt_simple_pairing_complete;
+#define EVT_SIMPLE_PAIRING_COMPLETE_SIZE 7
+
+#define EVT_LINK_SUPERVISION_TIMEOUT_CHANGED   0x38
+typedef struct {
+       uint16_t        handle;
+       uint16_t        timeout;
+} __attribute__ ((packed)) evt_link_supervision_timeout_changed;
+#define EVT_LINK_SUPERVISION_TIMEOUT_CHANGED_SIZE 4
+
+#define EVT_ENHANCED_FLUSH_COMPLETE    0x39
+typedef struct {
+       uint16_t        handle;
+} __attribute__ ((packed)) evt_enhanced_flush_complete;
+#define EVT_ENHANCED_FLUSH_COMPLETE_SIZE 2
+
+#define EVT_USER_PASSKEY_NOTIFY                0x3B
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint32_t        passkey;
+} __attribute__ ((packed)) evt_user_passkey_notify;
+#define EVT_USER_PASSKEY_NOTIFY_SIZE 10
+
+#define EVT_KEYPRESS_NOTIFY            0x3C
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         type;
+} __attribute__ ((packed)) evt_keypress_notify;
+#define EVT_KEYPRESS_NOTIFY_SIZE 7
+
+#define EVT_REMOTE_HOST_FEATURES_NOTIFY        0x3D
+typedef struct {
+       bdaddr_t        bdaddr;
+       uint8_t         features[8];
+} __attribute__ ((packed)) evt_remote_host_features_notify;
+#define EVT_REMOTE_HOST_FEATURES_NOTIFY_SIZE 14
+
+#define EVT_LE_META_EVENT      0x3E
+typedef struct {
+       uint8_t         subevent;
+       uint8_t         data[0];
+} __attribute__ ((packed)) evt_le_meta_event;
+#define EVT_LE_META_EVENT_SIZE 1
+
+#define EVT_LE_CONN_COMPLETE   0x01
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         role;
+       uint8_t         peer_bdaddr_type;
+       bdaddr_t        peer_bdaddr;
+       uint16_t        interval;
+       uint16_t        latency;
+       uint16_t        supervision_timeout;
+       uint8_t         master_clock_accuracy;
+} __attribute__ ((packed)) evt_le_connection_complete;
+#define EVT_LE_CONN_COMPLETE_SIZE 18
+
+#define EVT_LE_ADVERTISING_REPORT      0x02
+typedef struct {
+       uint8_t         evt_type;
+       uint8_t         bdaddr_type;
+       bdaddr_t        bdaddr;
+       uint8_t         length;
+       uint8_t         data[0];
+} __attribute__ ((packed)) le_advertising_info;
+#define LE_ADVERTISING_INFO_SIZE 9
+
+#define EVT_LE_CONN_UPDATE_COMPLETE    0x03
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint16_t        interval;
+       uint16_t        latency;
+       uint16_t        supervision_timeout;
+} __attribute__ ((packed)) evt_le_connection_update_complete;
+#define EVT_LE_CONN_UPDATE_COMPLETE_SIZE 9
+
+#define EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE      0x04
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+       uint8_t         features[8];
+} __attribute__ ((packed)) evt_le_read_remote_used_features_complete;
+#define EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE_SIZE 11
+
+#define EVT_LE_LTK_REQUEST     0x05
+typedef struct {
+       uint16_t        handle;
+       uint64_t        random;
+       uint16_t        diversifier;
+} __attribute__ ((packed)) evt_le_long_term_key_request;
+#define EVT_LE_LTK_REQUEST_SIZE 12
+
+#define EVT_PHYSICAL_LINK_COMPLETE             0x40
+typedef struct {
+       uint8_t         status;
+       uint8_t         handle;
+} __attribute__ ((packed)) evt_physical_link_complete;
+#define EVT_PHYSICAL_LINK_COMPLETE_SIZE 2
+
+#define EVT_CHANNEL_SELECTED           0x41
+
+#define EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE  0x42
+typedef struct {
+       uint8_t         status;
+       uint8_t         handle;
+       uint8_t         reason;
+} __attribute__ ((packed)) evt_disconn_physical_link_complete;
+#define EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE_SIZE 3
+
+#define EVT_PHYSICAL_LINK_LOSS_EARLY_WARNING   0x43
+typedef struct {
+       uint8_t         handle;
+       uint8_t         reason;
+} __attribute__ ((packed)) evt_physical_link_loss_warning;
+#define EVT_PHYSICAL_LINK_LOSS_WARNING_SIZE 2
+
+#define EVT_PHYSICAL_LINK_RECOVERY             0x44
+typedef struct {
+       uint8_t         handle;
+} __attribute__ ((packed)) evt_physical_link_recovery;
+#define EVT_PHYSICAL_LINK_RECOVERY_SIZE 1
+
+#define EVT_LOGICAL_LINK_COMPLETE              0x45
+typedef struct {
+       uint8_t         status;
+       uint16_t        log_handle;
+       uint8_t         handle;
+       uint8_t         tx_flow_id;
+} __attribute__ ((packed)) evt_logical_link_complete;
+#define EVT_LOGICAL_LINK_COMPLETE_SIZE 5
+
+#define EVT_DISCONNECT_LOGICAL_LINK_COMPLETE   0x46
+
+#define EVT_FLOW_SPEC_MODIFY_COMPLETE          0x47
+typedef struct {
+       uint8_t         status;
+       uint16_t        handle;
+} __attribute__ ((packed)) evt_flow_spec_modify_complete;
+#define EVT_FLOW_SPEC_MODIFY_COMPLETE_SIZE 3
+
+#define EVT_NUMBER_COMPLETED_BLOCKS            0x48
+
+#define EVT_AMP_STATUS_CHANGE                  0x4D
+typedef struct {
+       uint8_t         status;
+       uint8_t         amp_status;
+} __attribute__ ((packed)) evt_amp_status_change;
+#define EVT_AMP_STATUS_CHANGE_SIZE 2
+
+#define EVT_TESTING                    0xFE
+
+#define EVT_VENDOR                     0xFF
+
+/* Internal events generated by BlueZ stack */
+#define EVT_STACK_INTERNAL             0xFD
+typedef struct {
+       uint16_t        type;
+       uint8_t         data[0];
+} __attribute__ ((packed)) evt_stack_internal;
+#define EVT_STACK_INTERNAL_SIZE 2
+
+#define EVT_SI_DEVICE  0x01
+typedef struct {
+       uint16_t        event;
+       uint16_t        dev_id;
+} __attribute__ ((packed)) evt_si_device;
+#define EVT_SI_DEVICE_SIZE 4
+
+/* --------  HCI Packet structures  -------- */
+#define HCI_TYPE_LEN   1
+
+typedef struct {
+       uint16_t        opcode;         /* OCF & OGF */
+       uint8_t         plen;
+} __attribute__ ((packed))     hci_command_hdr;
+#define HCI_COMMAND_HDR_SIZE   3
+
+typedef struct {
+       uint8_t         evt;
+       uint8_t         plen;
+} __attribute__ ((packed))     hci_event_hdr;
+#define HCI_EVENT_HDR_SIZE     2
+
+typedef struct {
+       uint16_t        handle;         /* Handle & Flags(PB, BC) */
+       uint16_t        dlen;
+} __attribute__ ((packed))     hci_acl_hdr;
+#define HCI_ACL_HDR_SIZE       4
+
+typedef struct {
+       uint16_t        handle;
+       uint8_t         dlen;
+} __attribute__ ((packed))     hci_sco_hdr;
+#define HCI_SCO_HDR_SIZE       3
+
+typedef struct {
+       uint16_t        device;
+       uint16_t        type;
+       uint16_t        plen;
+} __attribute__ ((packed))     hci_msg_hdr;
+#define HCI_MSG_HDR_SIZE       6
+
+/* Command opcode pack/unpack */
+#define cmd_opcode_pack(ogf, ocf)      (uint16_t)((ocf & 0x03ff)|(ogf << 10))
+#define cmd_opcode_ogf(op)             (op >> 10)
+#define cmd_opcode_ocf(op)             (op & 0x03ff)
+
+/* ACL handle and flags pack/unpack */
+#define acl_handle_pack(h, f)  (uint16_t)((h & 0x0fff)|(f << 12))
+#define acl_handle(h)          (h & 0x0fff)
+#define acl_flags(h)           (h >> 12)
+
+#endif /* _NO_HCI_DEFS */
+
+/* HCI Socket options */
+#define HCI_DATA_DIR   1
+#define HCI_FILTER     2
+#define HCI_TIME_STAMP 3
+
+/* HCI CMSG flags */
+#define HCI_CMSG_DIR   0x0001
+#define HCI_CMSG_TSTAMP        0x0002
+
+struct sockaddr_hci {
+       sa_family_t     hci_family;
+       unsigned short  hci_dev;
+       unsigned short  hci_channel;
+};
+#define HCI_DEV_NONE   0xffff
+
+#define HCI_CHANNEL_RAW                0
+#define HCI_CHANNEL_MONITOR    2
+#define HCI_CHANNEL_CONTROL    3
+
+struct hci_filter {
+       uint32_t type_mask;
+       uint32_t event_mask[2];
+       uint16_t opcode;
+};
+
+#define HCI_FLT_TYPE_BITS      31
+#define HCI_FLT_EVENT_BITS     63
+#define HCI_FLT_OGF_BITS       63
+#define HCI_FLT_OCF_BITS       127
+
+/* Ioctl requests structures */
+struct hci_dev_stats {
+       uint32_t err_rx;
+       uint32_t err_tx;
+       uint32_t cmd_tx;
+       uint32_t evt_rx;
+       uint32_t acl_tx;
+       uint32_t acl_rx;
+       uint32_t sco_tx;
+       uint32_t sco_rx;
+       uint32_t byte_rx;
+       uint32_t byte_tx;
+};
+
+struct hci_dev_info {
+       uint16_t dev_id;
+       char     name[8];
+
+       bdaddr_t bdaddr;
+
+       uint32_t flags;
+       uint8_t  type;
+
+       uint8_t  features[8];
+
+       uint32_t pkt_type;
+       uint32_t link_policy;
+       uint32_t link_mode;
+
+       uint16_t acl_mtu;
+       uint16_t acl_pkts;
+       uint16_t sco_mtu;
+       uint16_t sco_pkts;
+
+       struct   hci_dev_stats stat;
+};
+
+struct hci_conn_info {
+       uint16_t handle;
+       bdaddr_t bdaddr;
+       uint8_t  type;
+       uint8_t  out;
+       uint16_t state;
+       uint32_t link_mode;
+};
+
+struct hci_dev_req {
+       uint16_t dev_id;
+       uint32_t dev_opt;
+};
+
+struct hci_dev_list_req {
+       uint16_t dev_num;
+       struct hci_dev_req dev_req[0];  /* hci_dev_req structures */
+};
+
+struct hci_conn_list_req {
+       uint16_t dev_id;
+       uint16_t conn_num;
+       struct hci_conn_info conn_info[0];
+};
+
+struct hci_conn_info_req {
+       bdaddr_t bdaddr;
+       uint8_t  type;
+       struct hci_conn_info conn_info[0];
+};
+
+struct hci_auth_info_req {
+       bdaddr_t bdaddr;
+       uint8_t  type;
+};
+
+struct hci_inquiry_req {
+       uint16_t dev_id;
+       uint16_t flags;
+       uint8_t  lap[3];
+       uint8_t  length;
+       uint8_t  num_rsp;
+};
+#define IREQ_CACHE_FLUSH 0x0001
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HCI_H */
diff --git a/lib/hci_lib.h b/lib/hci_lib.h
new file mode 100644 (file)
index 0000000..cf4a0ff
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __HCI_LIB_H
+#define __HCI_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct hci_request {
+       uint16_t ogf;
+       uint16_t ocf;
+       int      event;
+       void     *cparam;
+       int      clen;
+       void     *rparam;
+       int      rlen;
+};
+
+struct hci_version {
+       uint16_t manufacturer;
+       uint8_t  hci_ver;
+       uint16_t hci_rev;
+       uint8_t  lmp_ver;
+       uint16_t lmp_subver;
+};
+
+int hci_open_dev(int dev_id);
+int hci_close_dev(int dd);
+int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param);
+int hci_send_req(int dd, struct hci_request *req, int timeout);
+
+int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to);
+int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to);
+
+int hci_inquiry(int dev_id, int len, int num_rsp, const uint8_t *lap, inquiry_info **ii, long flags);
+int hci_devinfo(int dev_id, struct hci_dev_info *di);
+int hci_devba(int dev_id, bdaddr_t *bdaddr);
+int hci_devid(const char *str);
+
+int hci_read_local_name(int dd, int len, char *name, int to);
+int hci_write_local_name(int dd, const char *name, int to);
+int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to);
+int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr, uint8_t pscan_rep_mode, uint16_t clkoffset, int len, char *name, int to);
+int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to);
+int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver, int to);
+int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to);
+int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page, uint8_t *max_page, uint8_t *features, int to);
+int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to);
+int hci_read_local_version(int dd, struct hci_version *ver, int to);
+int hci_read_local_commands(int dd, uint8_t *commands, int to);
+int hci_read_local_features(int dd, uint8_t *features, int to);
+int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page, uint8_t *features, int to);
+int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to);
+int hci_read_class_of_dev(int dd, uint8_t *cls, int to);
+int hci_write_class_of_dev(int dd, uint32_t cls, int to);
+int hci_read_voice_setting(int dd, uint16_t *vs, int to);
+int hci_write_voice_setting(int dd, uint16_t vs, int to);
+int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to);
+int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to);
+int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to);
+int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to);
+int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to);
+int hci_authenticate_link(int dd, uint16_t handle, int to);
+int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to);
+int hci_change_link_key(int dd, uint16_t handle, int to);
+int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to);
+int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval, uint16_t min_interval, int to);
+int hci_exit_park_mode(int dd, uint16_t handle, int to);
+int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to);
+int hci_write_inquiry_scan_type(int dd, uint8_t type, int to);
+int hci_read_inquiry_mode(int dd, uint8_t *mode, int to);
+int hci_write_inquiry_mode(int dd, uint8_t mode, int to);
+int hci_read_afh_mode(int dd, uint8_t *mode, int to);
+int hci_write_afh_mode(int dd, uint8_t mode, int to);
+int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to);
+int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to);
+int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to);
+int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to);
+int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to);
+int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to);
+int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to);
+int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to);
+int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type, int8_t *level, int to);
+int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to);
+int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to);
+int hci_read_link_supervision_timeout(int dd, uint16_t handle, uint16_t *timeout, int to);
+int hci_write_link_supervision_timeout(int dd, uint16_t handle, uint16_t timeout, int to);
+int hci_set_afh_classification(int dd, uint8_t *map, int to);
+int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality, int to);
+int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to);
+int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map, int to);
+int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock, uint16_t *accuracy, int to);
+
+int hci_le_set_scan_enable(int dev_id, uint8_t enable, uint8_t filter_dup, int to);
+int hci_le_set_scan_parameters(int dev_id, uint8_t type, uint16_t interval,
+                                       uint16_t window, uint8_t own_type,
+                                       uint8_t filter, int to);
+int hci_le_set_advertise_enable(int dev_id, uint8_t enable, int to);
+int hci_le_create_conn(int dd, uint16_t interval, uint16_t window,
+               uint8_t initiator_filter, uint8_t peer_bdaddr_type,
+               bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type,
+               uint16_t min_interval, uint16_t max_interval,
+               uint16_t latency, uint16_t supervision_timeout,
+               uint16_t min_ce_length, uint16_t max_ce_length,
+               uint16_t *handle, int to);
+
+int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval,
+                       uint16_t max_interval, uint16_t latency,
+                       uint16_t supervision_timeout, int to);
+int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to);
+int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to);
+int hci_le_read_white_list_size(int dd, uint8_t *size, int to);
+int hci_le_clear_white_list(int dd, int to);
+int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg);
+int hci_get_route(bdaddr_t *bdaddr);
+
+char *hci_bustostr(int bus);
+char *hci_typetostr(int type);
+char *hci_dtypetostr(int type);
+char *hci_dflagstostr(uint32_t flags);
+char *hci_ptypetostr(unsigned int ptype);
+int hci_strtoptype(char *str, unsigned int *val);
+char *hci_scoptypetostr(unsigned int ptype);
+int hci_strtoscoptype(char *str, unsigned int *val);
+char *hci_lptostr(unsigned int ptype);
+int hci_strtolp(char *str, unsigned int *val);
+char *hci_lmtostr(unsigned int ptype);
+int hci_strtolm(char *str, unsigned int *val);
+
+char *hci_cmdtostr(unsigned int cmd);
+char *hci_commandstostr(uint8_t *commands, char *pref, int width);
+
+char *hci_vertostr(unsigned int ver);
+int hci_strtover(char *str, unsigned int *ver);
+char *lmp_vertostr(unsigned int ver);
+int lmp_strtover(char *str, unsigned int *ver);
+
+char *lmp_featurestostr(uint8_t *features, char *pref, int width);
+
+static inline void hci_set_bit(int nr, void *addr)
+{
+       *((uint32_t *) addr + (nr >> 5)) |= (1 << (nr & 31));
+}
+
+static inline void hci_clear_bit(int nr, void *addr)
+{
+       *((uint32_t *) addr + (nr >> 5)) &= ~(1 << (nr & 31));
+}
+
+static inline int hci_test_bit(int nr, void *addr)
+{
+       return *((uint32_t *) addr + (nr >> 5)) & (1 << (nr & 31));
+}
+
+/* HCI filter tools */
+static inline void hci_filter_clear(struct hci_filter *f)
+{
+       memset(f, 0, sizeof(*f));
+}
+static inline void hci_filter_set_ptype(int t, struct hci_filter *f)
+{
+       hci_set_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
+}
+static inline void hci_filter_clear_ptype(int t, struct hci_filter *f)
+{
+       hci_clear_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
+}
+static inline int hci_filter_test_ptype(int t, struct hci_filter *f)
+{
+       return hci_test_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask);
+}
+static inline void hci_filter_all_ptypes(struct hci_filter *f)
+{
+       memset((void *) &f->type_mask, 0xff, sizeof(f->type_mask));
+}
+static inline void hci_filter_set_event(int e, struct hci_filter *f)
+{
+       hci_set_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
+}
+static inline void hci_filter_clear_event(int e, struct hci_filter *f)
+{
+       hci_clear_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
+}
+static inline int hci_filter_test_event(int e, struct hci_filter *f)
+{
+       return hci_test_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask);
+}
+static inline void hci_filter_all_events(struct hci_filter *f)
+{
+       memset((void *) f->event_mask, 0xff, sizeof(f->event_mask));
+}
+static inline void hci_filter_set_opcode(int opcode, struct hci_filter *f)
+{
+       f->opcode = opcode;
+}
+static inline void hci_filter_clear_opcode(struct hci_filter *f)
+{
+       f->opcode = 0;
+}
+static inline int hci_filter_test_opcode(int opcode, struct hci_filter *f)
+{
+       return (f->opcode == opcode);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HCI_LIB_H */
diff --git a/lib/hidp.h b/lib/hidp.h
new file mode 100644 (file)
index 0000000..c5e6a78
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __HIDP_H
+#define __HIDP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* HIDP defaults */
+#define HIDP_MINIMUM_MTU 48
+#define HIDP_DEFAULT_MTU 48
+
+/* HIDP ioctl defines */
+#define HIDPCONNADD    _IOW('H', 200, int)
+#define HIDPCONNDEL    _IOW('H', 201, int)
+#define HIDPGETCONNLIST        _IOR('H', 210, int)
+#define HIDPGETCONNINFO        _IOR('H', 211, int)
+
+#define HIDP_VIRTUAL_CABLE_UNPLUG      0
+#define HIDP_BOOT_PROTOCOL_MODE                1
+#define HIDP_BLUETOOTH_VENDOR_ID       9
+
+struct hidp_connadd_req {
+       int ctrl_sock;          /* Connected control socket */
+       int intr_sock;          /* Connected interrupt socket */
+       uint16_t parser;        /* Parser version */
+       uint16_t rd_size;       /* Report descriptor size */
+       uint8_t *rd_data;       /* Report descriptor data */
+       uint8_t  country;
+       uint8_t  subclass;
+       uint16_t vendor;
+       uint16_t product;
+       uint16_t version;
+       uint32_t flags;
+       uint32_t idle_to;
+       char name[128];         /* Device name */
+};
+
+struct hidp_conndel_req {
+       bdaddr_t bdaddr;
+       uint32_t flags;
+};
+
+struct hidp_conninfo {
+       bdaddr_t bdaddr;
+       uint32_t flags;
+       uint16_t state;
+       uint16_t vendor;
+       uint16_t product;
+       uint16_t version;
+       char name[128];
+};
+
+struct hidp_connlist_req {
+       uint32_t cnum;
+       struct hidp_conninfo *ci;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __HIDP_H */
diff --git a/lib/l2cap.h b/lib/l2cap.h
new file mode 100644 (file)
index 0000000..5ce94c4
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (c) 2012       Code Aurora Forum. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __L2CAP_H
+#define __L2CAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+/* L2CAP defaults */
+#define L2CAP_DEFAULT_MTU      672
+#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF
+
+/* L2CAP socket address */
+struct sockaddr_l2 {
+       sa_family_t     l2_family;
+       unsigned short  l2_psm;
+       bdaddr_t        l2_bdaddr;
+       unsigned short  l2_cid;
+       uint8_t         l2_bdaddr_type;
+};
+
+/* L2CAP socket options */
+#define L2CAP_OPTIONS  0x01
+struct l2cap_options {
+       uint16_t        omtu;
+       uint16_t        imtu;
+       uint16_t        flush_to;
+       uint8_t         mode;
+       uint8_t         fcs;
+       uint8_t         max_tx;
+       uint16_t        txwin_size;
+};
+
+#define L2CAP_CONNINFO 0x02
+struct l2cap_conninfo {
+       uint16_t        hci_handle;
+       uint8_t         dev_class[3];
+};
+
+#define L2CAP_LM       0x03
+#define L2CAP_LM_MASTER                0x0001
+#define L2CAP_LM_AUTH          0x0002
+#define L2CAP_LM_ENCRYPT       0x0004
+#define L2CAP_LM_TRUSTED       0x0008
+#define L2CAP_LM_RELIABLE      0x0010
+#define L2CAP_LM_SECURE                0x0020
+
+/* L2CAP command codes */
+#define L2CAP_COMMAND_REJ      0x01
+#define L2CAP_CONN_REQ         0x02
+#define L2CAP_CONN_RSP         0x03
+#define L2CAP_CONF_REQ         0x04
+#define L2CAP_CONF_RSP         0x05
+#define L2CAP_DISCONN_REQ      0x06
+#define L2CAP_DISCONN_RSP      0x07
+#define L2CAP_ECHO_REQ         0x08
+#define L2CAP_ECHO_RSP         0x09
+#define L2CAP_INFO_REQ         0x0a
+#define L2CAP_INFO_RSP         0x0b
+#define L2CAP_CREATE_REQ       0x0c
+#define L2CAP_CREATE_RSP       0x0d
+#define L2CAP_MOVE_REQ         0x0e
+#define L2CAP_MOVE_RSP         0x0f
+#define L2CAP_MOVE_CFM         0x10
+#define L2CAP_MOVE_CFM_RSP     0x11
+
+/* L2CAP extended feature mask */
+#define L2CAP_FEAT_FLOWCTL     0x00000001
+#define L2CAP_FEAT_RETRANS     0x00000002
+#define L2CAP_FEAT_BIDIR_QOS   0x00000004
+#define L2CAP_FEAT_ERTM                0x00000008
+#define L2CAP_FEAT_STREAMING   0x00000010
+#define L2CAP_FEAT_FCS         0x00000020
+#define L2CAP_FEAT_EXT_FLOW    0x00000040
+#define L2CAP_FEAT_FIXED_CHAN  0x00000080
+#define L2CAP_FEAT_EXT_WINDOW  0x00000100
+#define L2CAP_FEAT_UCD         0x00000200
+
+/* L2CAP fixed channels */
+#define L2CAP_FC_L2CAP         0x02
+#define L2CAP_FC_CONNLESS      0x04
+#define L2CAP_FC_A2MP          0x08
+
+/* L2CAP structures */
+typedef struct {
+       uint16_t        len;
+       uint16_t        cid;
+} __attribute__ ((packed)) l2cap_hdr;
+#define L2CAP_HDR_SIZE 4
+
+typedef struct {
+       uint8_t         code;
+       uint8_t         ident;
+       uint16_t        len;
+} __attribute__ ((packed)) l2cap_cmd_hdr;
+#define L2CAP_CMD_HDR_SIZE 4
+
+typedef struct {
+       uint16_t        reason;
+} __attribute__ ((packed)) l2cap_cmd_rej;
+#define L2CAP_CMD_REJ_SIZE 2
+
+typedef struct {
+       uint16_t        psm;
+       uint16_t        scid;
+} __attribute__ ((packed)) l2cap_conn_req;
+#define L2CAP_CONN_REQ_SIZE 4
+
+typedef struct {
+       uint16_t        dcid;
+       uint16_t        scid;
+       uint16_t        result;
+       uint16_t        status;
+} __attribute__ ((packed)) l2cap_conn_rsp;
+#define L2CAP_CONN_RSP_SIZE 8
+
+/* connect result */
+#define L2CAP_CR_SUCCESS       0x0000
+#define L2CAP_CR_PEND          0x0001
+#define L2CAP_CR_BAD_PSM       0x0002
+#define L2CAP_CR_SEC_BLOCK     0x0003
+#define L2CAP_CR_NO_MEM                0x0004
+
+/* connect status */
+#define L2CAP_CS_NO_INFO       0x0000
+#define L2CAP_CS_AUTHEN_PEND   0x0001
+#define L2CAP_CS_AUTHOR_PEND   0x0002
+
+typedef struct {
+       uint16_t        dcid;
+       uint16_t        flags;
+       uint8_t         data[0];
+} __attribute__ ((packed)) l2cap_conf_req;
+#define L2CAP_CONF_REQ_SIZE 4
+
+typedef struct {
+       uint16_t        scid;
+       uint16_t        flags;
+       uint16_t        result;
+       uint8_t         data[0];
+} __attribute__ ((packed)) l2cap_conf_rsp;
+#define L2CAP_CONF_RSP_SIZE 6
+
+#define L2CAP_CONF_SUCCESS     0x0000
+#define L2CAP_CONF_UNACCEPT    0x0001
+#define L2CAP_CONF_REJECT      0x0002
+#define L2CAP_CONF_UNKNOWN     0x0003
+#define L2CAP_CONF_PENDING     0x0004
+#define L2CAP_CONF_EFS_REJECT  0x0005
+
+typedef struct {
+       uint8_t         type;
+       uint8_t         len;
+       uint8_t         val[0];
+} __attribute__ ((packed)) l2cap_conf_opt;
+#define L2CAP_CONF_OPT_SIZE 2
+
+#define L2CAP_CONF_MTU         0x01
+#define L2CAP_CONF_FLUSH_TO    0x02
+#define L2CAP_CONF_QOS         0x03
+#define L2CAP_CONF_RFC         0x04
+#define L2CAP_CONF_FCS         0x05
+#define L2CAP_CONF_EFS         0x06
+#define L2CAP_CONF_EWS         0x07
+
+#define L2CAP_CONF_MAX_SIZE    22
+
+#define L2CAP_MODE_BASIC       0x00
+#define L2CAP_MODE_RETRANS     0x01
+#define L2CAP_MODE_FLOWCTL     0x02
+#define L2CAP_MODE_ERTM                0x03
+#define L2CAP_MODE_STREAMING   0x04
+
+#define L2CAP_SERVTYPE_NOTRAFFIC       0x00
+#define L2CAP_SERVTYPE_BESTEFFORT      0x01
+#define L2CAP_SERVTYPE_GUARANTEED      0x02
+
+typedef struct {
+       uint16_t        dcid;
+       uint16_t        scid;
+} __attribute__ ((packed)) l2cap_disconn_req;
+#define L2CAP_DISCONN_REQ_SIZE 4
+
+typedef struct {
+       uint16_t        dcid;
+       uint16_t        scid;
+} __attribute__ ((packed)) l2cap_disconn_rsp;
+#define L2CAP_DISCONN_RSP_SIZE 4
+
+typedef struct {
+       uint16_t        type;
+} __attribute__ ((packed)) l2cap_info_req;
+#define L2CAP_INFO_REQ_SIZE 2
+
+typedef struct {
+       uint16_t        type;
+       uint16_t        result;
+       uint8_t         data[0];
+} __attribute__ ((packed)) l2cap_info_rsp;
+#define L2CAP_INFO_RSP_SIZE 4
+
+/* info type */
+#define L2CAP_IT_CL_MTU                0x0001
+#define L2CAP_IT_FEAT_MASK     0x0002
+
+/* info result */
+#define L2CAP_IR_SUCCESS       0x0000
+#define L2CAP_IR_NOTSUPP       0x0001
+
+typedef struct {
+       uint16_t        psm;
+       uint16_t        scid;
+       uint8_t         id;
+} __attribute__ ((packed)) l2cap_create_req;
+#define L2CAP_CREATE_REQ_SIZE 5
+
+typedef struct {
+       uint16_t        dcid;
+       uint16_t        scid;
+       uint16_t        result;
+       uint16_t        status;
+} __attribute__ ((packed)) l2cap_create_rsp;
+#define L2CAP_CREATE_RSP_SIZE 8
+
+typedef struct {
+       uint16_t        icid;
+       uint8_t         id;
+} __attribute__ ((packed)) l2cap_move_req;
+#define L2CAP_MOVE_REQ_SIZE 3
+
+typedef struct {
+       uint16_t        icid;
+       uint16_t        result;
+} __attribute__ ((packed)) l2cap_move_rsp;
+#define L2CAP_MOVE_RSP_SIZE 4
+
+typedef struct {
+       uint16_t        icid;
+       uint16_t        result;
+} __attribute__ ((packed)) l2cap_move_cfm;
+#define L2CAP_MOVE_CFM_SIZE 4
+
+typedef struct {
+       uint16_t        icid;
+} __attribute__ ((packed)) l2cap_move_cfm_rsp;
+#define L2CAP_MOVE_CFM_RSP_SIZE 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __L2CAP_H */
diff --git a/lib/mgmt.h b/lib/mgmt.h
new file mode 100644 (file)
index 0000000..a58915b
--- /dev/null
@@ -0,0 +1,554 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#define MGMT_INDEX_NONE                        0xFFFF
+
+#define MGMT_STATUS_SUCCESS            0x00
+#define MGMT_STATUS_UNKNOWN_COMMAND    0x01
+#define MGMT_STATUS_NOT_CONNECTED      0x02
+#define MGMT_STATUS_FAILED             0x03
+#define MGMT_STATUS_CONNECT_FAILED     0x04
+#define MGMT_STATUS_AUTH_FAILED                0x05
+#define MGMT_STATUS_NOT_PAIRED         0x06
+#define MGMT_STATUS_NO_RESOURCES       0x07
+#define MGMT_STATUS_TIMEOUT            0x08
+#define MGMT_STATUS_ALREADY_CONNECTED  0x09
+#define MGMT_STATUS_BUSY               0x0a
+#define MGMT_STATUS_REJECTED           0x0b
+#define MGMT_STATUS_NOT_SUPPORTED      0x0c
+#define MGMT_STATUS_INVALID_PARAMS     0x0d
+#define MGMT_STATUS_DISCONNECTED       0x0e
+#define MGMT_STATUS_NOT_POWERED                0x0f
+#define MGMT_STATUS_CANCELLED          0x10
+#define MGMT_STATUS_INVALID_INDEX      0x11
+
+struct mgmt_hdr {
+       uint16_t opcode;
+       uint16_t index;
+       uint16_t len;
+} __packed;
+#define MGMT_HDR_SIZE  6
+
+struct mgmt_addr_info {
+       bdaddr_t bdaddr;
+       uint8_t type;
+} __packed;
+
+#define MGMT_OP_READ_VERSION           0x0001
+struct mgmt_rp_read_version {
+       uint8_t version;
+       uint16_t revision;
+} __packed;
+
+#define MGMT_OP_READ_COMMANDS          0x0002
+struct mgmt_rp_read_commands {
+       uint16_t num_commands;
+       uint16_t num_events;
+       uint16_t opcodes[0];
+} __packed;
+
+#define MGMT_OP_READ_INDEX_LIST                0x0003
+struct mgmt_rp_read_index_list {
+       uint16_t num_controllers;
+       uint16_t index[0];
+} __packed;
+
+/* Reserve one extra byte for names in management messages so that they
+ * are always guaranteed to be nul-terminated */
+#define MGMT_MAX_NAME_LENGTH           (HCI_MAX_NAME_LENGTH + 1)
+#define MGMT_MAX_SHORT_NAME_LENGTH     (10 + 1)
+
+#define MGMT_SETTING_POWERED           0x00000001
+#define MGMT_SETTING_CONNECTABLE       0x00000002
+#define MGMT_SETTING_FAST_CONNECTABLE  0x00000004
+#define MGMT_SETTING_DISCOVERABLE      0x00000008
+#define MGMT_SETTING_PAIRABLE          0x00000010
+#define MGMT_SETTING_LINK_SECURITY     0x00000020
+#define MGMT_SETTING_SSP               0x00000040
+#define MGMT_SETTING_BREDR             0x00000080
+#define MGMT_SETTING_HS                        0x00000100
+#define MGMT_SETTING_LE                        0x00000200
+
+#define MGMT_OP_READ_INFO              0x0004
+struct mgmt_rp_read_info {
+       bdaddr_t bdaddr;
+       uint8_t version;
+       uint16_t manufacturer;
+       uint32_t supported_settings;
+       uint32_t current_settings;
+       uint8_t dev_class[3];
+       uint8_t name[MGMT_MAX_NAME_LENGTH];
+       uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
+} __packed;
+
+struct mgmt_mode {
+       uint8_t val;
+} __packed;
+
+#define MGMT_OP_SET_POWERED            0x0005
+
+#define MGMT_OP_SET_DISCOVERABLE       0x0006
+struct mgmt_cp_set_discoverable {
+       uint8_t val;
+       uint16_t timeout;
+} __packed;
+
+#define MGMT_OP_SET_CONNECTABLE                0x0007
+
+#define MGMT_OP_SET_FAST_CONNECTABLE   0x0008
+
+#define MGMT_OP_SET_PAIRABLE           0x0009
+
+#define MGMT_OP_SET_LINK_SECURITY      0x000A
+
+#define MGMT_OP_SET_SSP                        0x000B
+
+#define MGMT_OP_SET_HS                 0x000C
+
+#define MGMT_OP_SET_LE                 0x000D
+
+#define MGMT_OP_SET_DEV_CLASS          0x000E
+struct mgmt_cp_set_dev_class {
+       uint8_t major;
+       uint8_t minor;
+} __packed;
+
+#define MGMT_OP_SET_LOCAL_NAME         0x000F
+struct mgmt_cp_set_local_name {
+       uint8_t name[MGMT_MAX_NAME_LENGTH];
+       uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
+} __packed;
+
+#define MGMT_OP_ADD_UUID               0x0010
+struct mgmt_cp_add_uuid {
+       uint8_t uuid[16];
+       uint8_t svc_hint;
+} __packed;
+
+#define MGMT_OP_REMOVE_UUID            0x0011
+struct mgmt_cp_remove_uuid {
+       uint8_t uuid[16];
+} __packed;
+
+struct mgmt_link_key_info {
+       struct mgmt_addr_info addr;
+       uint8_t type;
+       uint8_t val[16];
+       uint8_t pin_len;
+} __packed;
+
+#define MGMT_OP_LOAD_LINK_KEYS         0x0012
+struct mgmt_cp_load_link_keys {
+       uint8_t debug_keys;
+       uint16_t key_count;
+       struct mgmt_link_key_info keys[0];
+} __packed;
+
+struct mgmt_ltk_info {
+       struct mgmt_addr_info addr;
+       uint8_t authenticated;
+       uint8_t master;
+       uint8_t enc_size;
+       uint16_t ediv;
+       uint8_t rand[8];
+       uint8_t val[16];
+} __packed;
+
+#define MGMT_OP_LOAD_LONG_TERM_KEYS    0x0013
+struct mgmt_cp_load_long_term_keys {
+       uint16_t key_count;
+       struct mgmt_ltk_info keys[0];
+} __packed;
+
+#define MGMT_OP_DISCONNECT             0x0014
+struct mgmt_cp_disconnect {
+       struct mgmt_addr_info addr;
+} __packed;
+struct mgmt_rp_disconnect {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_GET_CONNECTIONS                0x0015
+struct mgmt_rp_get_connections {
+       uint16_t conn_count;
+       struct mgmt_addr_info addr[0];
+} __packed;
+
+#define MGMT_OP_PIN_CODE_REPLY         0x0016
+struct mgmt_cp_pin_code_reply {
+       struct mgmt_addr_info addr;
+       uint8_t pin_len;
+       uint8_t pin_code[16];
+} __packed;
+
+#define MGMT_OP_PIN_CODE_NEG_REPLY     0x0017
+struct mgmt_cp_pin_code_neg_reply {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_SET_IO_CAPABILITY      0x0018
+struct mgmt_cp_set_io_capability {
+       uint8_t io_capability;
+} __packed;
+
+#define MGMT_OP_PAIR_DEVICE            0x0019
+struct mgmt_cp_pair_device {
+       struct mgmt_addr_info addr;
+       uint8_t io_cap;
+} __packed;
+struct mgmt_rp_pair_device {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_CANCEL_PAIR_DEVICE     0x001A
+
+#define MGMT_OP_UNPAIR_DEVICE          0x001B
+struct mgmt_cp_unpair_device {
+       struct mgmt_addr_info addr;
+       uint8_t disconnect;
+} __packed;
+struct mgmt_rp_unpair_device {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_USER_CONFIRM_REPLY     0x001C
+struct mgmt_cp_user_confirm_reply {
+       struct mgmt_addr_info addr;
+} __packed;
+struct mgmt_rp_user_confirm_reply {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001D
+
+#define MGMT_OP_USER_PASSKEY_REPLY     0x001E
+struct mgmt_cp_user_passkey_reply {
+       struct mgmt_addr_info addr;
+       uint32_t passkey;
+} __packed;
+struct mgmt_rp_user_passkey_reply {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001F
+struct mgmt_cp_user_passkey_neg_reply {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_READ_LOCAL_OOB_DATA    0x0020
+struct mgmt_rp_read_local_oob_data {
+       uint8_t hash[16];
+       uint8_t randomizer[16];
+} __packed;
+
+#define MGMT_OP_ADD_REMOTE_OOB_DATA    0x0021
+struct mgmt_cp_add_remote_oob_data {
+       struct mgmt_addr_info addr;
+       uint8_t hash[16];
+       uint8_t randomizer[16];
+} __packed;
+
+#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0022
+struct mgmt_cp_remove_remote_oob_data {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_START_DISCOVERY                0x0023
+struct mgmt_cp_start_discovery {
+       uint8_t type;
+} __packed;
+
+#define MGMT_OP_STOP_DISCOVERY         0x0024
+struct mgmt_cp_stop_discovery {
+       uint8_t type;
+} __packed;
+
+#define MGMT_OP_CONFIRM_NAME           0x0025
+struct mgmt_cp_confirm_name {
+       struct mgmt_addr_info addr;
+       uint8_t name_known;
+} __packed;
+struct mgmt_rp_confirm_name {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_BLOCK_DEVICE           0x0026
+struct mgmt_cp_block_device {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_UNBLOCK_DEVICE         0x0027
+struct mgmt_cp_unblock_device {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_SET_DEVICE_ID          0x0028
+struct mgmt_cp_set_device_id {
+       uint16_t source;
+       uint16_t vendor;
+       uint16_t product;
+       uint16_t version;
+} __packed;
+
+#define MGMT_EV_CMD_COMPLETE           0x0001
+struct mgmt_ev_cmd_complete {
+       uint16_t opcode;
+       uint8_t status;
+       uint8_t data[0];
+} __packed;
+
+#define MGMT_EV_CMD_STATUS             0x0002
+struct mgmt_ev_cmd_status {
+       uint16_t opcode;
+       uint8_t status;
+} __packed;
+
+#define MGMT_EV_CONTROLLER_ERROR       0x0003
+struct mgmt_ev_controller_error {
+       uint8_t error_code;
+} __packed;
+
+#define MGMT_EV_INDEX_ADDED            0x0004
+
+#define MGMT_EV_INDEX_REMOVED          0x0005
+
+#define MGMT_EV_NEW_SETTINGS           0x0006
+
+#define MGMT_EV_CLASS_OF_DEV_CHANGED   0x0007
+struct mgmt_ev_class_of_dev_changed {
+       uint8_t class_of_dev[3];
+} __packed;
+
+#define MGMT_EV_LOCAL_NAME_CHANGED     0x0008
+struct mgmt_ev_local_name_changed {
+       uint8_t name[MGMT_MAX_NAME_LENGTH];
+       uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
+} __packed;
+
+#define MGMT_EV_NEW_LINK_KEY           0x0009
+struct mgmt_ev_new_link_key {
+       uint8_t store_hint;
+       struct mgmt_link_key_info key;
+} __packed;
+
+#define MGMT_EV_NEW_LONG_TERM_KEY      0x000A
+struct mgmt_ev_new_long_term_key {
+       uint8_t store_hint;
+       struct mgmt_ltk_info key;
+} __packed;
+
+#define MGMT_EV_DEVICE_CONNECTED       0x000B
+struct mgmt_ev_device_connected {
+       struct mgmt_addr_info addr;
+       uint32_t flags;
+       uint16_t eir_len;
+       uint8_t eir[0];
+} __packed;
+
+#define MGMT_EV_DEVICE_DISCONNECTED    0x000C
+struct mgmt_ev_device_disconnected {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_EV_CONNECT_FAILED         0x000D
+struct mgmt_ev_connect_failed {
+       struct mgmt_addr_info addr;
+       uint8_t status;
+} __packed;
+
+#define MGMT_EV_PIN_CODE_REQUEST       0x000E
+struct mgmt_ev_pin_code_request {
+       struct mgmt_addr_info addr;
+       uint8_t secure;
+} __packed;
+
+#define MGMT_EV_USER_CONFIRM_REQUEST   0x000F
+struct mgmt_ev_user_confirm_request {
+       struct mgmt_addr_info addr;
+       uint8_t confirm_hint;
+       uint32_t value;
+} __packed;
+
+#define MGMT_EV_USER_PASSKEY_REQUEST   0x0010
+struct mgmt_ev_user_passkey_request {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_EV_AUTH_FAILED            0x0011
+struct mgmt_ev_auth_failed {
+       struct mgmt_addr_info addr;
+       uint8_t status;
+} __packed;
+
+#define MGMT_DEV_FOUND_CONFIRM_NAME    0x01
+#define MGMT_DEV_FOUND_LEGACY_PAIRING  0x02
+
+#define MGMT_EV_DEVICE_FOUND           0x0012
+struct mgmt_ev_device_found {
+       struct mgmt_addr_info addr;
+       int8_t rssi;
+       uint32_t flags;
+       uint16_t eir_len;
+       uint8_t eir[0];
+} __packed;
+
+#define MGMT_EV_DISCOVERING            0x0013
+struct mgmt_ev_discovering {
+       uint8_t type;
+       uint8_t discovering;
+} __packed;
+
+#define MGMT_EV_DEVICE_BLOCKED         0x0014
+struct mgmt_ev_device_blocked {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_EV_DEVICE_UNBLOCKED       0x0015
+struct mgmt_ev_device_unblocked {
+       struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_EV_DEVICE_UNPAIRED                0x0016
+struct mgmt_ev_device_unpaired {
+       struct mgmt_addr_info addr;
+} __packed;
+
+static const char *mgmt_op[] = {
+       "<0x0000>",
+       "Read Version",
+       "Read Commands",
+       "Read Index List",
+       "Read Controller Info",
+       "Set Powered",
+       "Set Discoverable",
+       "Set Connectable",
+       "Set Fast Connectable",         /* 0x0008 */
+       "Set Pairable",
+       "Set Link Security",
+       "Set Secure Simple Pairing",
+       "Set High Speed",
+       "Set Low Energy",
+       "Set Dev Class",
+       "Set Local Name",
+       "Add UUID",                     /* 0x0010 */
+       "Remove UUID",
+       "Load Link Keys",
+       "Load Long Term Keys",
+       "Disconnect",
+       "Get Connections",
+       "PIN Code Reply",
+       "PIN Code Neg Reply",
+       "Set IO Capability",            /* 0x0018 */
+       "Pair Device",
+       "Cancel Pair Device",
+       "Unpair Device",
+       "User Confirm Reply",
+       "User Confirm Neg Reply",
+       "User Passkey Reply",
+       "User Passkey Neg Reply",
+       "Read Local OOB Data",          /* 0x0020 */
+       "Add Remote OOB Data",
+       "Remove Remove OOB Data",
+       "Start Discovery",
+       "Stop Discovery",
+       "Confirm Name",
+       "Block Device",
+       "Unblock Device",
+       "Set Device ID",
+};
+
+static const char *mgmt_ev[] = {
+       "<0x0000>",
+       "Command Complete",
+       "Command Status",
+       "Controller Error",
+       "Index Added",
+       "Index Removed",
+       "New Settings",
+       "Class of Device Changed",
+       "Local Name Changed",           /* 0x0008 */
+       "New Link Key",
+       "New Long Term Key",
+       "Device Connected",
+       "Device Disconnected",
+       "Connect Failed",
+       "PIN Code Request",
+       "User Confirm Request",
+       "User Passkey Request",         /* 0x0010 */
+       "Authentication Failed",
+       "Device Found",
+       "Discovering",
+       "Device Blocked",
+       "Device Unblocked",
+       "Device Unpaired",
+};
+
+static const char *mgmt_status[] = {
+       "Success",
+       "Unknown Command",
+       "Not Connected",
+       "Failed",
+       "Connect Failed",
+       "Authentication Failed",
+       "Not Paired",
+       "No Resources",
+       "Timeout",
+       "Already Connected",
+       "Busy",
+       "Rejected",
+       "Not Supported",
+       "Invalid Parameters",
+       "Disconnected",
+       "Not Powered",
+       "Cancelled",
+       "Invalid Index",
+};
+
+#ifndef NELEM
+#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+static inline const char *mgmt_opstr(uint16_t op)
+{
+       if (op >= NELEM(mgmt_op))
+               return "<unknown opcode>";
+       return mgmt_op[op];
+}
+
+static inline const char *mgmt_evstr(uint16_t ev)
+{
+       if (ev >= NELEM(mgmt_ev))
+               return "<unknown event>";
+       return mgmt_ev[ev];
+}
+
+static inline const char *mgmt_errstr(uint8_t status)
+{
+       if (status >= NELEM(mgmt_status))
+               return "<unknown status>";
+       return mgmt_status[status];
+}
diff --git a/lib/rfcomm.h b/lib/rfcomm.h
new file mode 100644 (file)
index 0000000..ad6c0e1
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __RFCOMM_H
+#define __RFCOMM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+
+/* RFCOMM defaults */
+#define RFCOMM_DEFAULT_MTU     127
+
+#define RFCOMM_PSM 3
+
+/* RFCOMM socket address */
+struct sockaddr_rc {
+       sa_family_t     rc_family;
+       bdaddr_t        rc_bdaddr;
+       uint8_t         rc_channel;
+};
+
+/* RFCOMM socket options */
+#define RFCOMM_CONNINFO        0x02
+struct rfcomm_conninfo {
+       uint16_t        hci_handle;
+       uint8_t         dev_class[3];
+};
+
+#define RFCOMM_LM      0x03
+#define RFCOMM_LM_MASTER       0x0001
+#define RFCOMM_LM_AUTH         0x0002
+#define RFCOMM_LM_ENCRYPT      0x0004
+#define RFCOMM_LM_TRUSTED      0x0008
+#define RFCOMM_LM_RELIABLE     0x0010
+#define RFCOMM_LM_SECURE       0x0020
+
+/* RFCOMM TTY support */
+#define RFCOMM_MAX_DEV 256
+
+#define RFCOMMCREATEDEV                _IOW('R', 200, int)
+#define RFCOMMRELEASEDEV       _IOW('R', 201, int)
+#define RFCOMMGETDEVLIST       _IOR('R', 210, int)
+#define RFCOMMGETDEVINFO       _IOR('R', 211, int)
+
+struct rfcomm_dev_req {
+       int16_t         dev_id;
+       uint32_t        flags;
+       bdaddr_t        src;
+       bdaddr_t        dst;
+       uint8_t channel;
+};
+#define RFCOMM_REUSE_DLC       0
+#define RFCOMM_RELEASE_ONHUP   1
+#define RFCOMM_HANGUP_NOW      2
+#define RFCOMM_TTY_ATTACHED    3
+
+struct rfcomm_dev_info {
+       int16_t         id;
+       uint32_t        flags;
+       uint16_t        state;
+       bdaddr_t        src;
+       bdaddr_t        dst;
+       uint8_t         channel;
+};
+
+struct rfcomm_dev_list_req {
+       uint16_t        dev_num;
+       struct rfcomm_dev_info dev_info[0];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RFCOMM_H */
diff --git a/lib/sco.h b/lib/sco.h
new file mode 100644 (file)
index 0000000..75336a5
--- /dev/null
+++ b/lib/sco.h
@@ -0,0 +1,62 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __SCO_H
+#define __SCO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* SCO defaults */
+#define SCO_DEFAULT_MTU                500
+#define SCO_DEFAULT_FLUSH_TO   0xFFFF
+
+#define SCO_CONN_TIMEOUT       (HZ * 40)
+#define SCO_DISCONN_TIMEOUT    (HZ * 2)
+#define SCO_CONN_IDLE_TIMEOUT  (HZ * 60)
+
+/* SCO socket address */
+struct sockaddr_sco {
+       sa_family_t     sco_family;
+       bdaddr_t        sco_bdaddr;
+};
+
+/* set/get sockopt defines */
+#define SCO_OPTIONS    0x01
+struct sco_options {
+       uint16_t        mtu;
+};
+
+#define SCO_CONNINFO   0x02
+struct sco_conninfo {
+       uint16_t        hci_handle;
+       uint8_t         dev_class[3];
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SCO_H */
diff --git a/lib/sdp.c b/lib/sdp.c
new file mode 100644 (file)
index 0000000..7cb930f
--- /dev/null
+++ b/lib/sdp.c
@@ -0,0 +1,4805 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include "bluetooth.h"
+#include "hci.h"
+#include "hci_lib.h"
+#include "l2cap.h"
+#include "sdp.h"
+#include "sdp_lib.h"
+
+#define SDPINF(fmt, arg...) syslog(LOG_INFO, fmt "\n", ## arg)
+#define SDPERR(fmt, arg...) syslog(LOG_ERR, "%s: " fmt "\n", __func__ , ## arg)
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#ifdef SDP_DEBUG
+#define SDPDBG(fmt, arg...) syslog(LOG_DEBUG, "%s: " fmt "\n", __func__ , ## arg)
+#else
+#define SDPDBG(fmt...)
+#endif
+
+static uint128_t bluetooth_base_uuid = {
+       .data = {       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }
+};
+
+#define SDP_MAX_ATTR_LEN 65535
+
+static sdp_data_t *sdp_copy_seq(sdp_data_t *data);
+static int sdp_attr_add_new_with_length(sdp_record_t *rec,
+       uint16_t attr, uint8_t dtd, const void *value, uint32_t len);
+static int sdp_gen_buffer(sdp_buf_t *buf, sdp_data_t *d);
+
+/* Message structure. */
+struct tupla {
+       int index;
+       char *str;
+};
+
+static struct tupla Protocol[] = {
+       { SDP_UUID,             "SDP"           },
+       { UDP_UUID,             "UDP"           },
+       { RFCOMM_UUID,          "RFCOMM"        },
+       { TCP_UUID,             "TCP"           },
+       { TCS_BIN_UUID,         "TCS-BIN"       },
+       { TCS_AT_UUID,          "TCS-AT"        },
+       { OBEX_UUID,            "OBEX"          },
+       { IP_UUID,              "IP"            },
+       { FTP_UUID,             "FTP"           },
+       { HTTP_UUID,            "HTTP"          },
+       { WSP_UUID,             "WSP"           },
+       { BNEP_UUID,            "BNEP"          },
+       { UPNP_UUID,            "UPNP"          },
+       { HIDP_UUID,            "HIDP"          },
+       { HCRP_CTRL_UUID,       "HCRP-Ctrl"     },
+       { HCRP_DATA_UUID,       "HCRP-Data"     },
+       { HCRP_NOTE_UUID,       "HCRP-Notify"   },
+       { AVCTP_UUID,           "AVCTP"         },
+       { AVDTP_UUID,           "AVDTP"         },
+       { CMTP_UUID,            "CMTP"          },
+       { UDI_UUID,             "UDI"           },
+       { MCAP_CTRL_UUID,       "MCAP-Ctrl"     },
+       { MCAP_DATA_UUID,       "MCAP-Data"     },
+       { L2CAP_UUID,           "L2CAP"         },
+       { ATT_UUID,             "ATT"           },
+       { 0 }
+};
+
+static struct tupla ServiceClass[] = {
+       { SDP_SERVER_SVCLASS_ID,                "SDP Server"                    },
+       { BROWSE_GRP_DESC_SVCLASS_ID,           "Browse Group Descriptor"       },
+       { PUBLIC_BROWSE_GROUP,                  "Public Browse Group"           },
+       { SERIAL_PORT_SVCLASS_ID,               "Serial Port"                   },
+       { LAN_ACCESS_SVCLASS_ID,                "LAN Access Using PPP"          },
+       { DIALUP_NET_SVCLASS_ID,                "Dialup Networking"             },
+       { IRMC_SYNC_SVCLASS_ID,                 "IrMC Sync"                     },
+       { OBEX_OBJPUSH_SVCLASS_ID,              "OBEX Object Push"              },
+       { OBEX_FILETRANS_SVCLASS_ID,            "OBEX File Transfer"            },
+       { IRMC_SYNC_CMD_SVCLASS_ID,             "IrMC Sync Command"             },
+       { HEADSET_SVCLASS_ID,                   "Headset"                       },
+       { CORDLESS_TELEPHONY_SVCLASS_ID,        "Cordless Telephony"            },
+       { AUDIO_SOURCE_SVCLASS_ID,              "Audio Source"                  },
+       { AUDIO_SINK_SVCLASS_ID,                "Audio Sink"                    },
+       { AV_REMOTE_TARGET_SVCLASS_ID,          "AV Remote Target"              },
+       { ADVANCED_AUDIO_SVCLASS_ID,            "Advanced Audio"                },
+       { AV_REMOTE_SVCLASS_ID,                 "AV Remote"                     },
+       { VIDEO_CONF_SVCLASS_ID,                "Video Conferencing"            },
+       { INTERCOM_SVCLASS_ID,                  "Intercom"                      },
+       { FAX_SVCLASS_ID,                       "Fax"                           },
+       { HEADSET_AGW_SVCLASS_ID,               "Headset Audio Gateway"         },
+       { WAP_SVCLASS_ID,                       "WAP"                           },
+       { WAP_CLIENT_SVCLASS_ID,                "WAP Client"                    },
+       { PANU_SVCLASS_ID,                      "PAN User"                      },
+       { NAP_SVCLASS_ID,                       "Network Access Point"          },
+       { GN_SVCLASS_ID,                        "PAN Group Network"             },
+       { DIRECT_PRINTING_SVCLASS_ID,           "Direct Printing"               },
+       { REFERENCE_PRINTING_SVCLASS_ID,        "Reference Printing"            },
+       { IMAGING_SVCLASS_ID,                   "Imaging"                       },
+       { IMAGING_RESPONDER_SVCLASS_ID,         "Imaging Responder"             },
+       { IMAGING_ARCHIVE_SVCLASS_ID,           "Imaging Automatic Archive"     },
+       { IMAGING_REFOBJS_SVCLASS_ID,           "Imaging Referenced Objects"    },
+       { HANDSFREE_SVCLASS_ID,                 "Handsfree"                     },
+       { HANDSFREE_AGW_SVCLASS_ID,             "Handsfree Audio Gateway"       },
+       { DIRECT_PRT_REFOBJS_SVCLASS_ID,        "Direct Printing Ref. Objects"  },
+       { REFLECTED_UI_SVCLASS_ID,              "Reflected UI"                  },
+       { BASIC_PRINTING_SVCLASS_ID,            "Basic Printing"                },
+       { PRINTING_STATUS_SVCLASS_ID,           "Printing Status"               },
+       { HID_SVCLASS_ID,                       "Human Interface Device"        },
+       { HCR_SVCLASS_ID,                       "Hardcopy Cable Replacement"    },
+       { HCR_PRINT_SVCLASS_ID,                 "HCR Print"                     },
+       { HCR_SCAN_SVCLASS_ID,                  "HCR Scan"                      },
+       { CIP_SVCLASS_ID,                       "Common ISDN Access"            },
+       { VIDEO_CONF_GW_SVCLASS_ID,             "Video Conferencing Gateway"    },
+       { UDI_MT_SVCLASS_ID,                    "UDI MT"                        },
+       { UDI_TA_SVCLASS_ID,                    "UDI TA"                        },
+       { AV_SVCLASS_ID,                        "Audio/Video"                   },
+       { SAP_SVCLASS_ID,                       "SIM Access"                    },
+       { PBAP_PCE_SVCLASS_ID,                  "Phonebook Access - PCE"        },
+       { PBAP_PSE_SVCLASS_ID,                  "Phonebook Access - PSE"        },
+       { PBAP_SVCLASS_ID,                      "Phonebook Access"              },
+       { PNP_INFO_SVCLASS_ID,                  "PnP Information"               },
+       { GENERIC_NETWORKING_SVCLASS_ID,        "Generic Networking"            },
+       { GENERIC_FILETRANS_SVCLASS_ID,         "Generic File Transfer"         },
+       { GENERIC_AUDIO_SVCLASS_ID,             "Generic Audio"                 },
+       { GENERIC_TELEPHONY_SVCLASS_ID,         "Generic Telephony"             },
+       { UPNP_SVCLASS_ID,                      "UPnP"                          },
+       { UPNP_IP_SVCLASS_ID,                   "UPnP IP"                       },
+       { UPNP_PAN_SVCLASS_ID,                  "UPnP PAN"                      },
+       { UPNP_LAP_SVCLASS_ID,                  "UPnP LAP"                      },
+       { UPNP_L2CAP_SVCLASS_ID,                "UPnP L2CAP"                    },
+       { VIDEO_SOURCE_SVCLASS_ID,              "Video Source"                  },
+       { VIDEO_SINK_SVCLASS_ID,                "Video Sink"                    },
+       { VIDEO_DISTRIBUTION_SVCLASS_ID,        "Video Distribution"            },
+       { HDP_SVCLASS_ID,                       "HDP"                           },
+       { HDP_SOURCE_SVCLASS_ID,                "HDP Source"                    },
+       { HDP_SINK_SVCLASS_ID,                  "HDP Sink"                      },
+       { APPLE_AGENT_SVCLASS_ID,               "Apple Agent"                   },
+       { GENERIC_ATTRIB_SVCLASS_ID,            "Generic Attribute"             },
+       { 0 }
+};
+
+#define Profile ServiceClass
+
+static char *string_lookup(struct tupla *pt0, int index)
+{
+       struct tupla *pt;
+
+       for (pt = pt0; pt->index; pt++)
+               if (pt->index == index)
+                       return pt->str;
+
+       return "";
+}
+
+static char *string_lookup_uuid(struct tupla *pt0, const uuid_t* uuid)
+{
+       uuid_t tmp_uuid;
+
+       memcpy(&tmp_uuid, uuid, sizeof(tmp_uuid));
+
+       if (sdp_uuid128_to_uuid(&tmp_uuid)) {
+               switch (tmp_uuid.type) {
+               case SDP_UUID16:
+                       return string_lookup(pt0, tmp_uuid.value.uuid16);
+               case SDP_UUID32:
+                       return string_lookup(pt0, tmp_uuid.value.uuid32);
+               }
+       }
+
+       return "";
+}
+
+/*
+ * Prints into a string the Protocol UUID
+ * coping a maximum of n characters.
+ */
+static int uuid2str(struct tupla *message, const uuid_t *uuid, char *str, size_t n)
+{
+       char *str2;
+
+       if (!uuid) {
+               snprintf(str, n, "NULL");
+               return -2;
+       }
+
+       switch (uuid->type) {
+       case SDP_UUID16:
+               str2 = string_lookup(message, uuid->value.uuid16);
+               snprintf(str, n, "%s", str2);
+               break;
+       case SDP_UUID32:
+               str2 = string_lookup(message, uuid->value.uuid32);
+               snprintf(str, n, "%s", str2);
+               break;
+       case SDP_UUID128:
+               str2 = string_lookup_uuid(message, uuid);
+               snprintf(str, n, "%s", str2);
+               break;
+       default:
+               snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
+               return -1;
+       }
+
+       return 0;
+}
+
+int sdp_proto_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+       return uuid2str(Protocol, uuid, str, n);
+}
+
+int sdp_svclass_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+       return uuid2str(ServiceClass, uuid, str, n);
+}
+
+int sdp_profile_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+       return uuid2str(Profile, uuid, str, n);
+}
+
+/*
+ * convert the UUID to string, copying a maximum of n characters.
+ */
+int sdp_uuid2strn(const uuid_t *uuid, char *str, size_t n)
+{
+       if (!uuid) {
+               snprintf(str, n, "NULL");
+               return -2;
+       }
+       switch (uuid->type) {
+       case SDP_UUID16:
+               snprintf(str, n, "%.4x", uuid->value.uuid16);
+               break;
+       case SDP_UUID32:
+               snprintf(str, n, "%.8x", uuid->value.uuid32);
+               break;
+       case SDP_UUID128:{
+               unsigned int   data0;
+               unsigned short data1;
+               unsigned short data2;
+               unsigned short data3;
+               unsigned int   data4;
+               unsigned short data5;
+
+               memcpy(&data0, &uuid->value.uuid128.data[0], 4);
+               memcpy(&data1, &uuid->value.uuid128.data[4], 2);
+               memcpy(&data2, &uuid->value.uuid128.data[6], 2);
+               memcpy(&data3, &uuid->value.uuid128.data[8], 2);
+               memcpy(&data4, &uuid->value.uuid128.data[10], 4);
+               memcpy(&data5, &uuid->value.uuid128.data[14], 2);
+
+               snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+                               ntohl(data0), ntohs(data1),
+                               ntohs(data2), ntohs(data3),
+                               ntohl(data4), ntohs(data5));
+               }
+               break;
+       default:
+               snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
+               return -1;      /* Enum type of UUID not set */
+       }
+       return 0;
+}
+
+#ifdef SDP_DEBUG
+/*
+ * Function prints the UUID in hex as per defined syntax -
+ *
+ * 4bytes-2bytes-2bytes-2bytes-6bytes
+ *
+ * There is some ugly code, including hardcoding, but
+ * that is just the way it is converting 16 and 32 bit
+ * UUIDs to 128 bit as defined in the SDP doc
+ */
+void sdp_uuid_print(const uuid_t *uuid)
+{
+       if (uuid == NULL) {
+               SDPERR("Null passed to print UUID\n");
+               return;
+       }
+       if (uuid->type == SDP_UUID16) {
+               SDPDBG("  uint16_t : 0x%.4x\n", uuid->value.uuid16);
+       } else if (uuid->type == SDP_UUID32) {
+               SDPDBG("  uint32_t : 0x%.8x\n", uuid->value.uuid32);
+       } else if (uuid->type == SDP_UUID128) {
+               unsigned int data0;
+               unsigned short data1;
+               unsigned short data2;
+               unsigned short data3;
+               unsigned int data4;
+               unsigned short data5;
+
+               memcpy(&data0, &uuid->value.uuid128.data[0], 4);
+               memcpy(&data1, &uuid->value.uuid128.data[4], 2);
+               memcpy(&data2, &uuid->value.uuid128.data[6], 2);
+               memcpy(&data3, &uuid->value.uuid128.data[8], 2);
+               memcpy(&data4, &uuid->value.uuid128.data[10], 4);
+               memcpy(&data5, &uuid->value.uuid128.data[14], 2);
+
+               SDPDBG("  uint128_t : 0x%.8x-", ntohl(data0));
+               SDPDBG("%.4x-", ntohs(data1));
+               SDPDBG("%.4x-", ntohs(data2));
+               SDPDBG("%.4x-", ntohs(data3));
+               SDPDBG("%.8x", ntohl(data4));
+               SDPDBG("%.4x\n", ntohs(data5));
+       } else
+               SDPERR("Enum type of UUID not set\n");
+}
+#endif
+
+sdp_data_t *sdp_data_alloc_with_length(uint8_t dtd, const void *value,
+                                                       uint32_t length)
+{
+       sdp_data_t *seq;
+       sdp_data_t *d = malloc(sizeof(sdp_data_t));
+
+       if (!d)
+               return NULL;
+
+       memset(d, 0, sizeof(sdp_data_t));
+       d->dtd = dtd;
+       d->unitSize = sizeof(uint8_t);
+
+       switch (dtd) {
+       case SDP_DATA_NIL:
+               break;
+       case SDP_UINT8:
+               d->val.uint8 = *(uint8_t *) value;
+               d->unitSize += sizeof(uint8_t);
+               break;
+       case SDP_INT8:
+       case SDP_BOOL:
+               d->val.int8 = *(int8_t *) value;
+               d->unitSize += sizeof(int8_t);
+               break;
+       case SDP_UINT16:
+               d->val.uint16 = bt_get_unaligned((uint16_t *) value);
+               d->unitSize += sizeof(uint16_t);
+               break;
+       case SDP_INT16:
+               d->val.int16 = bt_get_unaligned((int16_t *) value);
+               d->unitSize += sizeof(int16_t);
+               break;
+       case SDP_UINT32:
+               d->val.uint32 = bt_get_unaligned((uint32_t *) value);
+               d->unitSize += sizeof(uint32_t);
+               break;
+       case SDP_INT32:
+               d->val.int32 = bt_get_unaligned((int32_t *) value);
+               d->unitSize += sizeof(int32_t);
+               break;
+       case SDP_INT64:
+               d->val.int64 = bt_get_unaligned((int64_t *) value);
+               d->unitSize += sizeof(int64_t);
+               break;
+       case SDP_UINT64:
+               d->val.uint64 = bt_get_unaligned((uint64_t *) value);
+               d->unitSize += sizeof(uint64_t);
+               break;
+       case SDP_UINT128:
+               memcpy(&d->val.uint128.data, value, sizeof(uint128_t));
+               d->unitSize += sizeof(uint128_t);
+               break;
+       case SDP_INT128:
+               memcpy(&d->val.int128.data, value, sizeof(uint128_t));
+               d->unitSize += sizeof(uint128_t);
+               break;
+       case SDP_UUID16:
+               sdp_uuid16_create(&d->val.uuid, bt_get_unaligned((uint16_t *) value));
+               d->unitSize += sizeof(uint16_t);
+               break;
+       case SDP_UUID32:
+               sdp_uuid32_create(&d->val.uuid, bt_get_unaligned((uint32_t *) value));
+               d->unitSize += sizeof(uint32_t);
+               break;
+       case SDP_UUID128:
+               sdp_uuid128_create(&d->val.uuid, value);
+               d->unitSize += sizeof(uint128_t);
+               break;
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+               if (!value) {
+                       free(d);
+                       return NULL;
+               }
+
+               d->unitSize += length;
+               if (length <= USHRT_MAX) {
+                       d->val.str = malloc(length);
+                       if (!d->val.str) {
+                               free(d);
+                               return NULL;
+                       }
+
+                       memcpy(d->val.str, value, length);
+               } else {
+                       SDPERR("Strings of size > USHRT_MAX not supported\n");
+                       free(d);
+                       d = NULL;
+               }
+               break;
+       case SDP_URL_STR32:
+       case SDP_TEXT_STR32:
+               SDPERR("Strings of size > USHRT_MAX not supported\n");
+               break;
+       case SDP_ALT8:
+       case SDP_ALT16:
+       case SDP_ALT32:
+       case SDP_SEQ8:
+       case SDP_SEQ16:
+       case SDP_SEQ32:
+               if (dtd == SDP_ALT8 || dtd == SDP_SEQ8)
+                       d->unitSize += sizeof(uint8_t);
+               else if (dtd == SDP_ALT16 || dtd == SDP_SEQ16)
+                       d->unitSize += sizeof(uint16_t);
+               else if (dtd == SDP_ALT32 || dtd == SDP_SEQ32)
+                       d->unitSize += sizeof(uint32_t);
+               seq = (sdp_data_t *)value;
+               d->val.dataseq = seq;
+               for (; seq; seq = seq->next)
+                       d->unitSize += seq->unitSize;
+               break;
+       default:
+               free(d);
+               d = NULL;
+       }
+
+       return d;
+}
+
+sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value)
+{
+       uint32_t length;
+
+       switch (dtd) {
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+               if (!value)
+                       return NULL;
+
+               length = strlen((char *) value);
+               break;
+       default:
+               length = 0;
+               break;
+       }
+
+       return sdp_data_alloc_with_length(dtd, value, length);
+}
+
+sdp_data_t *sdp_seq_append(sdp_data_t *seq, sdp_data_t *d)
+{
+       if (seq) {
+               sdp_data_t *p;
+               for (p = seq; p->next; p = p->next);
+               p->next = d;
+       } else
+               seq = d;
+       d->next = NULL;
+       return seq;
+}
+
+sdp_data_t *sdp_seq_alloc_with_length(void **dtds, void **values, int *length,
+                                                               int len)
+{
+       sdp_data_t *curr = NULL, *seq = NULL;
+       int i;
+
+       for (i = 0; i < len; i++) {
+               sdp_data_t *data;
+               int8_t dtd = *(uint8_t *) dtds[i];
+
+               if (dtd >= SDP_SEQ8 && dtd <= SDP_ALT32)
+                       data = (sdp_data_t *) values[i];
+               else
+                       data = sdp_data_alloc_with_length(dtd, values[i], length[i]);
+
+               if (!data)
+                       return NULL;
+
+               if (curr)
+                       curr->next = data;
+               else
+                       seq = data;
+
+               curr = data;
+       }
+
+       return sdp_data_alloc_with_length(SDP_SEQ8, seq, length[i]);
+}
+
+sdp_data_t *sdp_seq_alloc(void **dtds, void **values, int len)
+{
+       sdp_data_t *curr = NULL, *seq = NULL;
+       int i;
+
+       for (i = 0; i < len; i++) {
+               sdp_data_t *data;
+               uint8_t dtd = *(uint8_t *) dtds[i];
+
+               if (dtd >= SDP_SEQ8 && dtd <= SDP_ALT32)
+                       data = (sdp_data_t *) values[i];
+               else
+                       data = sdp_data_alloc(dtd, values[i]);
+
+               if (!data)
+                       return NULL;
+
+               if (curr)
+                       curr->next = data;
+               else
+                       seq = data;
+
+               curr = data;
+       }
+
+       return sdp_data_alloc(SDP_SEQ8, seq);
+}
+
+static void extract_svclass_uuid(sdp_data_t *data, uuid_t *uuid)
+{
+       sdp_data_t *d;
+
+       if (!data || data->dtd < SDP_SEQ8 || data->dtd > SDP_SEQ32)
+               return;
+
+       d = data->val.dataseq;
+       if (!d)
+               return;
+
+       if (d->dtd < SDP_UUID16 || d->dtd > SDP_UUID128)
+               return;
+
+       *uuid = d->val.uuid;
+}
+
+int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *d)
+{
+       sdp_data_t *p = sdp_data_get(rec, attr);
+
+       if (p)
+               return -1;
+
+       d->attrId = attr;
+       rec->attrlist = sdp_list_insert_sorted(rec->attrlist, d, sdp_attrid_comp_func);
+
+       if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+               extract_svclass_uuid(d, &rec->svclass);
+
+       return 0;
+}
+
+void sdp_attr_remove(sdp_record_t *rec, uint16_t attr)
+{
+       sdp_data_t *d = sdp_data_get(rec, attr);
+
+       if (d)
+               rec->attrlist = sdp_list_remove(rec->attrlist, d);
+
+       if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+               memset(&rec->svclass, 0, sizeof(rec->svclass));
+}
+
+void sdp_set_seq_len(uint8_t *ptr, uint32_t length)
+{
+       uint8_t dtd = *ptr++;
+
+       switch (dtd) {
+       case SDP_SEQ8:
+       case SDP_ALT8:
+       case SDP_TEXT_STR8:
+       case SDP_URL_STR8:
+               *ptr = (uint8_t) length;
+               break;
+       case SDP_SEQ16:
+       case SDP_ALT16:
+       case SDP_TEXT_STR16:
+       case SDP_URL_STR16:
+               bt_put_unaligned(htons(length), (uint16_t *) ptr);
+               break;
+       case SDP_SEQ32:
+       case SDP_ALT32:
+       case SDP_TEXT_STR32:
+       case SDP_URL_STR32:
+               bt_put_unaligned(htonl(length), (uint32_t *) ptr);
+               break;
+       }
+}
+
+static int sdp_get_data_type(sdp_buf_t *buf, uint8_t dtd)
+{
+       int data_type = 0;
+
+       data_type += sizeof(uint8_t);
+
+       switch (dtd) {
+       case SDP_SEQ8:
+       case SDP_TEXT_STR8:
+       case SDP_URL_STR8:
+       case SDP_ALT8:
+               data_type += sizeof(uint8_t);
+               break;
+       case SDP_SEQ16:
+       case SDP_TEXT_STR16:
+       case SDP_URL_STR16:
+       case SDP_ALT16:
+               data_type += sizeof(uint16_t);
+               break;
+       case SDP_SEQ32:
+       case SDP_TEXT_STR32:
+       case SDP_URL_STR32:
+       case SDP_ALT32:
+               data_type += sizeof(uint32_t);
+               break;
+       }
+
+       if (!buf->data)
+               buf->buf_size += data_type;
+
+       return data_type;
+}
+
+static int sdp_set_data_type(sdp_buf_t *buf, uint8_t dtd)
+{
+       int data_type = 0;
+       uint8_t *p = buf->data + buf->data_size;
+
+       *p = dtd;
+       data_type = sdp_get_data_type(buf, dtd);
+       buf->data_size += data_type;
+
+       return data_type;
+}
+
+void sdp_set_attrid(sdp_buf_t *buf, uint16_t attr)
+{
+       uint8_t *p = buf->data;
+
+       /* data type for attr */
+       *p++ = SDP_UINT16;
+       buf->data_size = sizeof(uint8_t);
+       bt_put_unaligned(htons(attr), (uint16_t *) p);
+       buf->data_size += sizeof(uint16_t);
+}
+
+static int get_data_size(sdp_buf_t *buf, sdp_data_t *sdpdata)
+{
+       sdp_data_t *d;
+       int n = 0;
+
+       for (d = sdpdata->val.dataseq; d; d = d->next) {
+               if (buf->data)
+                       n += sdp_gen_pdu(buf, d);
+               else
+                       n += sdp_gen_buffer(buf, d);
+       }
+
+       return n;
+}
+
+static int sdp_get_data_size(sdp_buf_t *buf, sdp_data_t *d)
+{
+       uint32_t data_size = 0;
+       uint8_t dtd = d->dtd;
+
+       switch (dtd) {
+       case SDP_DATA_NIL:
+               break;
+       case SDP_UINT8:
+               data_size = sizeof(uint8_t);
+               break;
+       case SDP_UINT16:
+               data_size = sizeof(uint16_t);
+               break;
+       case SDP_UINT32:
+               data_size = sizeof(uint32_t);
+               break;
+       case SDP_UINT64:
+               data_size = sizeof(uint64_t);
+               break;
+       case SDP_UINT128:
+               data_size = sizeof(uint128_t);
+               break;
+       case SDP_INT8:
+       case SDP_BOOL:
+               data_size = sizeof(int8_t);
+               break;
+       case SDP_INT16:
+               data_size = sizeof(int16_t);
+               break;
+       case SDP_INT32:
+               data_size = sizeof(int32_t);
+               break;
+       case SDP_INT64:
+               data_size = sizeof(int64_t);
+               break;
+       case SDP_INT128:
+               data_size = sizeof(uint128_t);
+               break;
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+       case SDP_TEXT_STR32:
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_URL_STR32:
+               data_size = d->unitSize - sizeof(uint8_t);
+               break;
+       case SDP_SEQ8:
+       case SDP_SEQ16:
+       case SDP_SEQ32:
+               data_size = get_data_size(buf, d);
+               break;
+       case SDP_ALT8:
+       case SDP_ALT16:
+       case SDP_ALT32:
+               data_size = get_data_size(buf, d);
+               break;
+       case SDP_UUID16:
+               data_size = sizeof(uint16_t);
+               break;
+       case SDP_UUID32:
+               data_size = sizeof(uint32_t);
+               break;
+       case SDP_UUID128:
+               data_size = sizeof(uint128_t);
+               break;
+       default:
+               break;
+       }
+
+       if (!buf->data)
+               buf->buf_size += data_size;
+
+       return data_size;
+}
+
+static int sdp_gen_buffer(sdp_buf_t *buf, sdp_data_t *d)
+{
+       int orig = buf->buf_size;
+
+       if (buf->buf_size == 0 && d->dtd == 0) {
+               /* create initial sequence */
+               buf->buf_size += sizeof(uint8_t);
+
+               /* reserve space for sequence size */
+               buf->buf_size += sizeof(uint8_t);
+       }
+
+       /* attribute length */
+       buf->buf_size += sizeof(uint8_t) + sizeof(uint16_t);
+
+       sdp_get_data_type(buf, d->dtd);
+       sdp_get_data_size(buf, d);
+
+       if (buf->buf_size > UCHAR_MAX && d->dtd == SDP_SEQ8)
+               buf->buf_size += sizeof(uint8_t);
+
+       return buf->buf_size - orig;
+}
+
+int sdp_gen_pdu(sdp_buf_t *buf, sdp_data_t *d)
+{
+       uint32_t pdu_size = 0, data_size = 0;
+       unsigned char *src = NULL, is_seq = 0, is_alt = 0;
+       uint8_t dtd = d->dtd;
+       uint16_t u16;
+       uint32_t u32;
+       uint64_t u64;
+       uint128_t u128;
+       uint8_t *seqp = buf->data + buf->data_size;
+
+       pdu_size = sdp_set_data_type(buf, dtd);
+       data_size = sdp_get_data_size(buf, d);
+
+       switch (dtd) {
+       case SDP_DATA_NIL:
+               break;
+       case SDP_UINT8:
+               src = &d->val.uint8;
+               break;
+       case SDP_UINT16:
+               u16 = htons(d->val.uint16);
+               src = (unsigned char *) &u16;
+               break;
+       case SDP_UINT32:
+               u32 = htonl(d->val.uint32);
+               src = (unsigned char *) &u32;
+               break;
+       case SDP_UINT64:
+               u64 = hton64(d->val.uint64);
+               src = (unsigned char *) &u64;
+               break;
+       case SDP_UINT128:
+               hton128(&d->val.uint128, &u128);
+               src = (unsigned char *) &u128;
+               break;
+       case SDP_INT8:
+       case SDP_BOOL:
+               src = (unsigned char *) &d->val.int8;
+               break;
+       case SDP_INT16:
+               u16 = htons(d->val.int16);
+               src = (unsigned char *) &u16;
+               break;
+       case SDP_INT32:
+               u32 = htonl(d->val.int32);
+               src = (unsigned char *) &u32;
+               break;
+       case SDP_INT64:
+               u64 = hton64(d->val.int64);
+               src = (unsigned char *) &u64;
+               break;
+       case SDP_INT128:
+               hton128(&d->val.int128, &u128);
+               src = (unsigned char *) &u128;
+               break;
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+       case SDP_TEXT_STR32:
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_URL_STR32:
+               src = (unsigned char *) d->val.str;
+               sdp_set_seq_len(seqp, data_size);
+               break;
+       case SDP_SEQ8:
+       case SDP_SEQ16:
+       case SDP_SEQ32:
+               is_seq = 1;
+               sdp_set_seq_len(seqp, data_size);
+               break;
+       case SDP_ALT8:
+       case SDP_ALT16:
+       case SDP_ALT32:
+               is_alt = 1;
+               sdp_set_seq_len(seqp, data_size);
+               break;
+       case SDP_UUID16:
+               u16 = htons(d->val.uuid.value.uuid16);
+               src = (unsigned char *) &u16;
+               break;
+       case SDP_UUID32:
+               u32 = htonl(d->val.uuid.value.uuid32);
+               src = (unsigned char *) &u32;
+               break;
+       case SDP_UUID128:
+               src = (unsigned char *) &d->val.uuid.value.uuid128;
+               break;
+       default:
+               break;
+       }
+
+       if (!is_seq && !is_alt) {
+               if (src && buf->buf_size >= buf->data_size + data_size) {
+                       memcpy(buf->data + buf->data_size, src, data_size);
+                       buf->data_size += data_size;
+               } else if (dtd != SDP_DATA_NIL) {
+                       SDPDBG("Gen PDU : Can't copy from invalid source or dest\n");
+               }
+       }
+
+       pdu_size += data_size;
+
+       return pdu_size;
+}
+
+static void sdp_attr_pdu(void *value, void *udata)
+{
+       sdp_append_to_pdu((sdp_buf_t *)udata, (sdp_data_t *)value);
+}
+
+static void sdp_attr_size(void *value, void *udata)
+{
+       sdp_gen_buffer((sdp_buf_t *)udata, (sdp_data_t *)value);
+}
+
+int sdp_gen_record_pdu(const sdp_record_t *rec, sdp_buf_t *buf)
+{
+       memset(buf, 0, sizeof(sdp_buf_t));
+       sdp_list_foreach(rec->attrlist, sdp_attr_size, buf);
+
+       buf->data = malloc(buf->buf_size);
+       if (!buf->data)
+               return -ENOMEM;
+       buf->data_size = 0;
+       memset(buf->data, 0, buf->buf_size);
+
+       sdp_list_foreach(rec->attrlist, sdp_attr_pdu, buf);
+
+       return 0;
+}
+
+void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *d)
+{
+       sdp_data_t *p = sdp_data_get(rec, attr);
+
+       if (p) {
+               rec->attrlist = sdp_list_remove(rec->attrlist, p);
+               sdp_data_free(p);
+       }
+
+       d->attrId = attr;
+       rec->attrlist = sdp_list_insert_sorted(rec->attrlist, d, sdp_attrid_comp_func);
+
+       if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+               extract_svclass_uuid(d, &rec->svclass);
+}
+
+int sdp_attrid_comp_func(const void *key1, const void *key2)
+{
+       const sdp_data_t *d1 = (const sdp_data_t *)key1;
+       const sdp_data_t *d2 = (const sdp_data_t *)key2;
+
+       if (d1 && d2)
+               return d1->attrId - d2->attrId;
+       return 0;
+}
+
+static void data_seq_free(sdp_data_t *seq)
+{
+       sdp_data_t *d = seq->val.dataseq;
+
+       while (d) {
+               sdp_data_t *next = d->next;
+               sdp_data_free(d);
+               d = next;
+       }
+}
+
+void sdp_data_free(sdp_data_t *d)
+{
+       switch (d->dtd) {
+       case SDP_SEQ8:
+       case SDP_SEQ16:
+       case SDP_SEQ32:
+               data_seq_free(d);
+               break;
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_URL_STR32:
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+       case SDP_TEXT_STR32:
+               free(d->val.str);
+               break;
+       }
+       free(d);
+}
+
+int sdp_uuid_extract(const uint8_t *p, int bufsize, uuid_t *uuid, int *scanned)
+{
+       uint8_t type;
+
+       if (bufsize < (int) sizeof(uint8_t)) {
+               SDPERR("Unexpected end of packet");
+               return -1;
+       }
+
+       type = *(const uint8_t *) p;
+
+       if (!SDP_IS_UUID(type)) {
+               SDPERR("Unknown data type : %d expecting a svc UUID\n", type);
+               return -1;
+       }
+       p += sizeof(uint8_t);
+       *scanned += sizeof(uint8_t);
+       bufsize -= sizeof(uint8_t);
+       if (type == SDP_UUID16) {
+               if (bufsize < (int) sizeof(uint16_t)) {
+                       SDPERR("Not enough room for 16-bit UUID");
+                       return -1;
+               }
+               sdp_uuid16_create(uuid, ntohs(bt_get_unaligned((uint16_t *) p)));
+               *scanned += sizeof(uint16_t);
+       } else if (type == SDP_UUID32) {
+               if (bufsize < (int) sizeof(uint32_t)) {
+                       SDPERR("Not enough room for 32-bit UUID");
+                       return -1;
+               }
+               sdp_uuid32_create(uuid, ntohl(bt_get_unaligned((uint32_t *) p)));
+               *scanned += sizeof(uint32_t);
+       } else {
+               if (bufsize < (int) sizeof(uint128_t)) {
+                       SDPERR("Not enough room for 128-bit UUID");
+                       return -1;
+               }
+               sdp_uuid128_create(uuid, p);
+               *scanned += sizeof(uint128_t);
+       }
+       return 0;
+}
+
+static sdp_data_t *extract_int(const void *p, int bufsize, int *len)
+{
+       sdp_data_t *d;
+
+       if (bufsize < (int) sizeof(uint8_t)) {
+               SDPERR("Unexpected end of packet");
+               return NULL;
+       }
+
+       d = malloc(sizeof(sdp_data_t));
+       if (!d)
+               return NULL;
+
+       SDPDBG("Extracting integer\n");
+       memset(d, 0, sizeof(sdp_data_t));
+       d->dtd = *(uint8_t *) p;
+       p += sizeof(uint8_t);
+       *len += sizeof(uint8_t);
+       bufsize -= sizeof(uint8_t);
+
+       switch (d->dtd) {
+       case SDP_DATA_NIL:
+               break;
+       case SDP_BOOL:
+       case SDP_INT8:
+       case SDP_UINT8:
+               if (bufsize < (int) sizeof(uint8_t)) {
+                       SDPERR("Unexpected end of packet");
+                       free(d);
+                       return NULL;
+               }
+               *len += sizeof(uint8_t);
+               d->val.uint8 = *(uint8_t *) p;
+               break;
+       case SDP_INT16:
+       case SDP_UINT16:
+               if (bufsize < (int) sizeof(uint16_t)) {
+                       SDPERR("Unexpected end of packet");
+                       free(d);
+                       return NULL;
+               }
+               *len += sizeof(uint16_t);
+               d->val.uint16 = ntohs(bt_get_unaligned((uint16_t *) p));
+               break;
+       case SDP_INT32:
+       case SDP_UINT32:
+               if (bufsize < (int) sizeof(uint32_t)) {
+                       SDPERR("Unexpected end of packet");
+                       free(d);
+                       return NULL;
+               }
+               *len += sizeof(uint32_t);
+               d->val.uint32 = ntohl(bt_get_unaligned((uint32_t *) p));
+               break;
+       case SDP_INT64:
+       case SDP_UINT64:
+               if (bufsize < (int) sizeof(uint64_t)) {
+                       SDPERR("Unexpected end of packet");
+                       free(d);
+                       return NULL;
+               }
+               *len += sizeof(uint64_t);
+               d->val.uint64 = ntoh64(bt_get_unaligned((uint64_t *) p));
+               break;
+       case SDP_INT128:
+       case SDP_UINT128:
+               if (bufsize < (int) sizeof(uint128_t)) {
+                       SDPERR("Unexpected end of packet");
+                       free(d);
+                       return NULL;
+               }
+               *len += sizeof(uint128_t);
+               ntoh128((uint128_t *) p, &d->val.uint128);
+               break;
+       default:
+               free(d);
+               d = NULL;
+       }
+       return d;
+}
+
+static sdp_data_t *extract_uuid(const uint8_t *p, int bufsize, int *len,
+                                                       sdp_record_t *rec)
+{
+       sdp_data_t *d = malloc(sizeof(sdp_data_t));
+
+       if (!d)
+               return NULL;
+
+       SDPDBG("Extracting UUID");
+       memset(d, 0, sizeof(sdp_data_t));
+       if (sdp_uuid_extract(p, bufsize, &d->val.uuid, len) < 0) {
+               free(d);
+               return NULL;
+       }
+       d->dtd = *p;
+       if (rec)
+               sdp_pattern_add_uuid(rec, &d->val.uuid);
+       return d;
+}
+
+/*
+ * Extract strings from the PDU (could be service description and similar info)
+ */
+static sdp_data_t *extract_str(const void *p, int bufsize, int *len)
+{
+       char *s;
+       int n;
+       sdp_data_t *d;
+
+       if (bufsize < (int) sizeof(uint8_t)) {
+               SDPERR("Unexpected end of packet");
+               return NULL;
+       }
+
+       d = malloc(sizeof(sdp_data_t));
+       if (!d)
+               return NULL;
+
+       memset(d, 0, sizeof(sdp_data_t));
+       d->dtd = *(uint8_t *) p;
+       p += sizeof(uint8_t);
+       *len += sizeof(uint8_t);
+       bufsize -= sizeof(uint8_t);
+
+       switch (d->dtd) {
+       case SDP_TEXT_STR8:
+       case SDP_URL_STR8:
+               if (bufsize < (int) sizeof(uint8_t)) {
+                       SDPERR("Unexpected end of packet");
+                       free(d);
+                       return NULL;
+               }
+               n = *(uint8_t *) p;
+               p += sizeof(uint8_t);
+               *len += sizeof(uint8_t);
+               bufsize -= sizeof(uint8_t);
+               break;
+       case SDP_TEXT_STR16:
+       case SDP_URL_STR16:
+               if (bufsize < (int) sizeof(uint16_t)) {
+                       SDPERR("Unexpected end of packet");
+                       free(d);
+                       return NULL;
+               }
+               n = ntohs(bt_get_unaligned((uint16_t *) p));
+               p += sizeof(uint16_t);
+               *len += sizeof(uint16_t) + n;
+               bufsize -= sizeof(uint16_t);
+               break;
+       default:
+               SDPERR("Sizeof text string > UINT16_MAX\n");
+               free(d);
+               return NULL;
+       }
+
+       if (bufsize < n) {
+               SDPERR("String too long to fit in packet");
+               free(d);
+               return NULL;
+       }
+
+       s = malloc(n + 1);
+       if (!s) {
+               SDPERR("Not enough memory for incoming string");
+               free(d);
+               return NULL;
+       }
+       memset(s, 0, n + 1);
+       memcpy(s, p, n);
+
+       *len += n;
+
+       SDPDBG("Len : %d\n", n);
+       SDPDBG("Str : %s\n", s);
+
+       d->val.str = s;
+       d->unitSize = n + sizeof(uint8_t);
+       return d;
+}
+
+/*
+ * Extract the sequence type and its length, and return offset into buf
+ * or 0 on failure.
+ */
+int sdp_extract_seqtype(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size)
+{
+       uint8_t dtd;
+       int scanned = sizeof(uint8_t);
+
+       if (bufsize < (int) sizeof(uint8_t)) {
+               SDPERR("Unexpected end of packet");
+               return 0;
+       }
+
+       dtd = *(uint8_t *) buf;
+       buf += sizeof(uint8_t);
+       bufsize -= sizeof(uint8_t);
+       *dtdp = dtd;
+       switch (dtd) {
+       case SDP_SEQ8:
+       case SDP_ALT8:
+               if (bufsize < (int) sizeof(uint8_t)) {
+                       SDPERR("Unexpected end of packet");
+                       return 0;
+               }
+               *size = *(uint8_t *) buf;
+               scanned += sizeof(uint8_t);
+               break;
+       case SDP_SEQ16:
+       case SDP_ALT16:
+               if (bufsize < (int) sizeof(uint16_t)) {
+                       SDPERR("Unexpected end of packet");
+                       return 0;
+               }
+               *size = ntohs(bt_get_unaligned((uint16_t *) buf));
+               scanned += sizeof(uint16_t);
+               break;
+       case SDP_SEQ32:
+       case SDP_ALT32:
+               if (bufsize < (int) sizeof(uint32_t)) {
+                       SDPERR("Unexpected end of packet");
+                       return 0;
+               }
+               *size = ntohl(bt_get_unaligned((uint32_t *) buf));
+               scanned += sizeof(uint32_t);
+               break;
+       default:
+               SDPERR("Unknown sequence type, aborting\n");
+               return 0;
+       }
+       return scanned;
+}
+
+static sdp_data_t *extract_seq(const void *p, int bufsize, int *len,
+                                                       sdp_record_t *rec)
+{
+       int seqlen, n = 0;
+       sdp_data_t *curr, *prev;
+       sdp_data_t *d = malloc(sizeof(sdp_data_t));
+
+       if (!d)
+               return NULL;
+
+       SDPDBG("Extracting SEQ");
+       memset(d, 0, sizeof(sdp_data_t));
+       *len = sdp_extract_seqtype(p, bufsize, &d->dtd, &seqlen);
+       SDPDBG("Sequence Type : 0x%x length : 0x%x\n", d->dtd, seqlen);
+
+       if (*len == 0)
+               return d;
+
+       if (*len > bufsize) {
+               SDPERR("Packet not big enough to hold sequence.");
+               free(d);
+               return NULL;
+       }
+
+       p += *len;
+       bufsize -= *len;
+       prev = NULL;
+       while (n < seqlen) {
+               int attrlen = 0;
+               curr = sdp_extract_attr(p, bufsize, &attrlen, rec);
+               if (curr == NULL)
+                       break;
+
+               if (prev)
+                       prev->next = curr;
+               else
+                       d->val.dataseq = curr;
+               prev = curr;
+               p += attrlen;
+               n += attrlen;
+               bufsize -= attrlen;
+
+               SDPDBG("Extracted: %d SequenceLength: %d", n, seqlen);
+       }
+
+       *len += n;
+       return d;
+}
+
+sdp_data_t *sdp_extract_attr(const uint8_t *p, int bufsize, int *size,
+                                                       sdp_record_t *rec)
+{
+       sdp_data_t *elem;
+       int n = 0;
+       uint8_t dtd;
+
+       if (bufsize < (int) sizeof(uint8_t)) {
+               SDPERR("Unexpected end of packet");
+               return NULL;
+       }
+
+       dtd = *(const uint8_t *)p;
+
+       SDPDBG("extract_attr: dtd=0x%x", dtd);
+       switch (dtd) {
+       case SDP_DATA_NIL:
+       case SDP_BOOL:
+       case SDP_UINT8:
+       case SDP_UINT16:
+       case SDP_UINT32:
+       case SDP_UINT64:
+       case SDP_UINT128:
+       case SDP_INT8:
+       case SDP_INT16:
+       case SDP_INT32:
+       case SDP_INT64:
+       case SDP_INT128:
+               elem = extract_int(p, bufsize, &n);
+               break;
+       case SDP_UUID16:
+       case SDP_UUID32:
+       case SDP_UUID128:
+               elem = extract_uuid(p, bufsize, &n, rec);
+               break;
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+       case SDP_TEXT_STR32:
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_URL_STR32:
+               elem = extract_str(p, bufsize, &n);
+               break;
+       case SDP_SEQ8:
+       case SDP_SEQ16:
+       case SDP_SEQ32:
+       case SDP_ALT8:
+       case SDP_ALT16:
+       case SDP_ALT32:
+               elem = extract_seq(p, bufsize, &n, rec);
+               break;
+       default:
+               SDPERR("Unknown data descriptor : 0x%x terminating\n", dtd);
+               return NULL;
+       }
+       *size += n;
+       return elem;
+}
+
+#ifdef SDP_DEBUG
+static void attr_print_func(void *value, void *userData)
+{
+       sdp_data_t *d = (sdp_data_t *)value;
+
+       SDPDBG("=====================================\n");
+       SDPDBG("ATTRIBUTE IDENTIFIER : 0x%x\n",  d->attrId);
+       SDPDBG("ATTRIBUTE VALUE PTR : %p\n", value);
+       if (d)
+               sdp_data_print(d);
+       else
+               SDPDBG("NULL value\n");
+       SDPDBG("=====================================\n");
+}
+
+void sdp_print_service_attr(sdp_list_t *svcAttrList)
+{
+       SDPDBG("Printing service attr list %p\n", svcAttrList);
+       sdp_list_foreach(svcAttrList, attr_print_func, NULL);
+       SDPDBG("Printed service attr list %p\n", svcAttrList);
+}
+#endif
+
+sdp_record_t *sdp_extract_pdu(const uint8_t *buf, int bufsize, int *scanned)
+{
+       int extracted = 0, seqlen = 0;
+       uint8_t dtd;
+       uint16_t attr;
+       sdp_record_t *rec = sdp_record_alloc();
+       const uint8_t *p = buf;
+
+       *scanned = sdp_extract_seqtype(buf, bufsize, &dtd, &seqlen);
+       p += *scanned;
+       bufsize -= *scanned;
+       rec->attrlist = NULL;
+
+       while (extracted < seqlen && bufsize > 0) {
+               int n = sizeof(uint8_t), attrlen = 0;
+               sdp_data_t *data = NULL;
+
+               SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d",
+                                                       seqlen, extracted);
+
+               if (bufsize < n + (int) sizeof(uint16_t)) {
+                       SDPERR("Unexpected end of packet");
+                       break;
+               }
+
+               dtd = *(uint8_t *) p;
+               attr = ntohs(bt_get_unaligned((uint16_t *) (p + n)));
+               n += sizeof(uint16_t);
+
+               SDPDBG("DTD of attrId : %d Attr id : 0x%x \n", dtd, attr);
+
+               data = sdp_extract_attr(p + n, bufsize - n, &attrlen, rec);
+
+               SDPDBG("Attr id : 0x%x attrValueLength : %d\n", attr, attrlen);
+
+               n += attrlen;
+               if (data == NULL) {
+                       SDPDBG("Terminating extraction of attributes");
+                       break;
+               }
+
+               if (attr == SDP_ATTR_RECORD_HANDLE)
+                       rec->handle = data->val.uint32;
+
+               if (attr == SDP_ATTR_SVCLASS_ID_LIST)
+                       extract_svclass_uuid(data, &rec->svclass);
+
+               extracted += n;
+               p += n;
+               bufsize -= n;
+               sdp_attr_replace(rec, attr, data);
+
+               SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d",
+                                                       seqlen, extracted);
+       }
+#ifdef SDP_DEBUG
+       SDPDBG("Successful extracting of Svc Rec attributes\n");
+       sdp_print_service_attr(rec->attrlist);
+#endif
+       *scanned += seqlen;
+       return rec;
+}
+
+static void sdp_copy_pattern(void *value, void *udata)
+{
+       uuid_t *uuid = value;
+       sdp_record_t *rec = udata;
+
+       sdp_pattern_add_uuid(rec, uuid);
+}
+
+static void *sdp_data_value(sdp_data_t *data, uint32_t *len)
+{
+       void *val = NULL;
+
+       switch (data->dtd) {
+       case SDP_DATA_NIL:
+               break;
+       case SDP_UINT8:
+               val = &data->val.uint8;
+               break;
+       case SDP_INT8:
+       case SDP_BOOL:
+               val = &data->val.int8;
+               break;
+       case SDP_UINT16:
+               val = &data->val.uint16;
+               break;
+       case SDP_INT16:
+               val = &data->val.int16;
+               break;
+       case SDP_UINT32:
+               val = &data->val.uint32;
+               break;
+       case SDP_INT32:
+               val = &data->val.int32;
+               break;
+       case SDP_INT64:
+               val = &data->val.int64;
+               break;
+       case SDP_UINT64:
+               val = &data->val.uint64;
+               break;
+       case SDP_UINT128:
+               val = &data->val.uint128;
+               break;
+       case SDP_INT128:
+               val = &data->val.int128;
+               break;
+       case SDP_UUID16:
+               val = &data->val.uuid.value.uuid16;
+               break;
+       case SDP_UUID32:
+               val = &data->val.uuid.value.uuid32;
+               break;
+       case SDP_UUID128:
+               val = &data->val.uuid.value.uuid128;
+               break;
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+       case SDP_URL_STR32:
+       case SDP_TEXT_STR32:
+               val = data->val.str;
+               if (len)
+                       *len = data->unitSize - 1;
+               break;
+       case SDP_ALT8:
+       case SDP_ALT16:
+       case SDP_ALT32:
+       case SDP_SEQ8:
+       case SDP_SEQ16:
+       case SDP_SEQ32:
+               val = sdp_copy_seq(data->val.dataseq);
+               break;
+       }
+
+       return val;
+}
+
+static sdp_data_t *sdp_copy_seq(sdp_data_t *data)
+{
+       sdp_data_t *tmp, *seq = NULL, *cur = NULL;
+
+       for (tmp = data; tmp; tmp = tmp->next) {
+               sdp_data_t *datatmp;
+               void *value;
+
+               value = sdp_data_value(tmp, NULL);
+               datatmp = sdp_data_alloc_with_length(tmp->dtd, value,
+                                                               tmp->unitSize);
+
+               if (cur)
+                       cur->next = datatmp;
+               else
+                       seq = datatmp;
+
+               cur = datatmp;
+       }
+
+       return seq;
+}
+
+static void sdp_copy_attrlist(void *value, void *udata)
+{
+       sdp_data_t *data = value;
+       sdp_record_t *rec = udata;
+       void *val;
+       uint32_t len = 0;
+
+       val = sdp_data_value(data, &len);
+
+       if (!len)
+               sdp_attr_add_new(rec, data->attrId, data->dtd, val);
+       else
+               sdp_attr_add_new_with_length(rec, data->attrId,
+                                                       data->dtd, val, len);
+}
+
+sdp_record_t *sdp_copy_record(sdp_record_t *rec)
+{
+       sdp_record_t *cpy;
+
+       cpy = sdp_record_alloc();
+
+       cpy->handle = rec->handle;
+
+       sdp_list_foreach(rec->pattern, sdp_copy_pattern, cpy);
+       sdp_list_foreach(rec->attrlist, sdp_copy_attrlist, cpy);
+
+       cpy->svclass = rec->svclass;
+
+       return cpy;
+}
+
+#ifdef SDP_DEBUG
+static void print_dataseq(sdp_data_t *p)
+{
+       sdp_data_t *d;
+
+       for (d = p; d; d = d->next)
+               sdp_data_print(d);
+}
+#endif
+
+void sdp_record_print(const sdp_record_t *rec)
+{
+       sdp_data_t *d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
+       if (d)
+               printf("Service Name: %.*s\n", d->unitSize, d->val.str);
+       d = sdp_data_get(rec, SDP_ATTR_SVCDESC_PRIMARY);
+       if (d)
+               printf("Service Description: %.*s\n", d->unitSize, d->val.str);
+       d = sdp_data_get(rec, SDP_ATTR_PROVNAME_PRIMARY);
+       if (d)
+               printf("Service Provider: %.*s\n", d->unitSize, d->val.str);
+}
+
+#ifdef SDP_DEBUG
+void sdp_data_print(sdp_data_t *d)
+{
+       switch (d->dtd) {
+       case SDP_DATA_NIL:
+               SDPDBG("NIL\n");
+               break;
+       case SDP_BOOL:
+       case SDP_UINT8:
+       case SDP_UINT16:
+       case SDP_UINT32:
+       case SDP_UINT64:
+       case SDP_UINT128:
+       case SDP_INT8:
+       case SDP_INT16:
+       case SDP_INT32:
+       case SDP_INT64:
+       case SDP_INT128:
+               SDPDBG("Integer : 0x%x\n", d->val.uint32);
+               break;
+       case SDP_UUID16:
+       case SDP_UUID32:
+       case SDP_UUID128:
+               SDPDBG("UUID\n");
+               sdp_uuid_print(&d->val.uuid);
+               break;
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+       case SDP_TEXT_STR32:
+               SDPDBG("Text : %s\n", d->val.str);
+               break;
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_URL_STR32:
+               SDPDBG("URL : %s\n", d->val.str);
+               break;
+       case SDP_SEQ8:
+       case SDP_SEQ16:
+       case SDP_SEQ32:
+               print_dataseq(d->val.dataseq);
+               break;
+       case SDP_ALT8:
+       case SDP_ALT16:
+       case SDP_ALT32:
+               SDPDBG("Data Sequence Alternates\n");
+               print_dataseq(d->val.dataseq);
+               break;
+       }
+}
+#endif
+
+sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attrId)
+{
+       if (rec->attrlist) {
+               sdp_data_t sdpTemplate;
+               sdp_list_t *p;
+
+               sdpTemplate.attrId = attrId;
+               p = sdp_list_find(rec->attrlist, &sdpTemplate, sdp_attrid_comp_func);
+               if (p)
+                       return p->data;
+       }
+       return NULL;
+}
+
+static int sdp_send_req(sdp_session_t *session, uint8_t *buf, uint32_t size)
+{
+       uint32_t sent = 0;
+
+       while (sent < size) {
+               int n = send(session->sock, buf + sent, size - sent, 0);
+               if (n < 0)
+                       return -1;
+               sent += n;
+       }
+       return 0;
+}
+
+static int sdp_read_rsp(sdp_session_t *session, uint8_t *buf, uint32_t size)
+{
+       fd_set readFds;
+       struct timeval timeout = { SDP_RESPONSE_TIMEOUT, 0 };
+
+       FD_ZERO(&readFds);
+       FD_SET(session->sock, &readFds);
+       SDPDBG("Waiting for response\n");
+       if (select(session->sock + 1, &readFds, NULL, NULL, &timeout) == 0) {
+               SDPERR("Client timed out\n");
+               errno = ETIMEDOUT;
+               return -1;
+       }
+       return recv(session->sock, buf, size, 0);
+}
+
+/*
+ * generic send request, wait for response method.
+ */
+int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *reqbuf,
+                       uint8_t *rspbuf, uint32_t reqsize, uint32_t *rspsize)
+{
+       int n;
+       sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+       sdp_pdu_hdr_t *rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+
+       SDPDBG("");
+       if (0 > sdp_send_req(session, reqbuf, reqsize)) {
+               SDPERR("Error sending data:%s", strerror(errno));
+               return -1;
+       }
+       n = sdp_read_rsp(session, rspbuf, SDP_RSP_BUFFER_SIZE);
+       if (0 > n)
+               return -1;
+       SDPDBG("Read : %d\n", n);
+       if (n == 0 || reqhdr->tid != rsphdr->tid) {
+               errno = EPROTO;
+               return -1;
+       }
+       *rspsize = n;
+       return 0;
+}
+
+/*
+ * singly-linked lists (after openobex implementation)
+ */
+sdp_list_t *sdp_list_append(sdp_list_t *p, void *d)
+{
+       sdp_list_t *q, *n = malloc(sizeof(sdp_list_t));
+
+       if (!n)
+               return NULL;
+
+       n->data = d;
+       n->next = 0;
+
+       if (!p)
+               return n;
+
+       for (q = p; q->next; q = q->next);
+       q->next = n;
+
+       return p;
+}
+
+sdp_list_t *sdp_list_remove(sdp_list_t *list, void *d)
+{
+       sdp_list_t *p, *q;
+
+       for (q = 0, p = list; p; q = p, p = p->next)
+               if (p->data == d) {
+                       if (q)
+                               q->next = p->next;
+                       else
+                               list = p->next;
+                       free(p);
+                       break;
+               }
+
+       return list;
+}
+
+sdp_list_t *sdp_list_insert_sorted(sdp_list_t *list, void *d,
+                                                       sdp_comp_func_t f)
+{
+       sdp_list_t *q, *p, *n;
+
+       n = malloc(sizeof(sdp_list_t));
+       if (!n)
+               return NULL;
+       n->data = d;
+       for (q = 0, p = list; p; q = p, p = p->next)
+               if (f(p->data, d) >= 0)
+                       break;
+       /* insert between q and p; if !q insert at head */
+       if (q)
+               q->next = n;
+       else
+               list = n;
+       n->next = p;
+       return list;
+}
+
+/*
+ * Every element of the list points to things which need
+ * to be free()'d. This method frees the list's contents
+ */
+void sdp_list_free(sdp_list_t *list, sdp_free_func_t f)
+{
+       sdp_list_t *next;
+       while (list) {
+               next = list->next;
+               if (f)
+                       f(list->data);
+               free(list);
+               list = next;
+       }
+}
+
+static inline int __find_port(sdp_data_t *seq, int proto)
+{
+       if (!seq || !seq->next)
+               return 0;
+
+       if (SDP_IS_UUID(seq->dtd) && sdp_uuid_to_proto(&seq->val.uuid) == proto) {
+               seq = seq->next;
+               switch (seq->dtd) {
+               case SDP_UINT8:
+                       return seq->val.uint8;
+               case SDP_UINT16:
+                       return seq->val.uint16;
+               }
+       }
+       return 0;
+}
+
+int sdp_get_proto_port(const sdp_list_t *list, int proto)
+{
+       if (proto != L2CAP_UUID && proto != RFCOMM_UUID) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       for (; list; list = list->next) {
+               sdp_list_t *p;
+               for (p = list->data; p; p = p->next) {
+                       sdp_data_t *seq = p->data;
+                       int port = __find_port(seq, proto);
+                       if (port)
+                               return port;
+               }
+       }
+       return 0;
+}
+
+sdp_data_t *sdp_get_proto_desc(sdp_list_t *list, int proto)
+{
+       for (; list; list = list->next) {
+               sdp_list_t *p;
+               for (p = list->data; p; p = p->next) {
+                       sdp_data_t *seq = p->data;
+                       if (SDP_IS_UUID(seq->dtd) &&
+                                       sdp_uuid_to_proto(&seq->val.uuid) == proto)
+                               return seq->next;
+               }
+       }
+       return NULL;
+}
+
+int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **pap)
+{
+       sdp_data_t *pdlist, *curr;
+       sdp_list_t *ap = 0;
+
+       pdlist = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
+       if (pdlist == NULL) {
+               errno = ENODATA;
+               return -1;
+       }
+       SDPDBG("AP type : 0%x\n", pdlist->dtd);
+
+       for (; pdlist; pdlist = pdlist->next) {
+               sdp_list_t *pds = 0;
+               for (curr = pdlist->val.dataseq; curr; curr = curr->next)
+                       pds = sdp_list_append(pds, curr->val.dataseq);
+               ap = sdp_list_append(ap, pds);
+       }
+       *pap = ap;
+       return 0;
+}
+
+int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **pap)
+{
+       sdp_data_t *pdlist, *curr;
+       sdp_list_t *ap = 0;
+
+       pdlist = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
+       if (pdlist == NULL) {
+               errno = ENODATA;
+               return -1;
+       }
+       SDPDBG("AP type : 0%x\n", pdlist->dtd);
+
+       pdlist = pdlist->val.dataseq;
+
+       for (; pdlist; pdlist = pdlist->next) {
+               sdp_list_t *pds = 0;
+               for (curr = pdlist->val.dataseq; curr; curr = curr->next)
+                       pds = sdp_list_append(pds, curr->val.dataseq);
+               ap = sdp_list_append(ap, pds);
+       }
+       *pap = ap;
+       return 0;
+}
+
+int sdp_get_uuidseq_attr(const sdp_record_t *rec, uint16_t attr,
+                                                       sdp_list_t **seqp)
+{
+       sdp_data_t *sdpdata = sdp_data_get(rec, attr);
+
+       *seqp = NULL;
+       if (sdpdata && sdpdata->dtd >= SDP_SEQ8 && sdpdata->dtd <= SDP_SEQ32) {
+               sdp_data_t *d;
+               for (d = sdpdata->val.dataseq; d; d = d->next) {
+                       uuid_t *u;
+                       if (d->dtd < SDP_UUID16 || d->dtd > SDP_UUID128) {
+                               errno = EINVAL;
+                               goto fail;
+                       }
+
+                       u = malloc(sizeof(uuid_t));
+                       if (!u)
+                               goto fail;
+
+                       *u = d->val.uuid;
+                       *seqp = sdp_list_append(*seqp, u);
+               }
+               return 0;
+       }
+fail:
+       sdp_list_free(*seqp, free);
+       *seqp = NULL;
+       return -1;
+}
+
+int sdp_set_uuidseq_attr(sdp_record_t *rec, uint16_t aid, sdp_list_t *seq)
+{
+       int status = 0, i, len;
+       void **dtds, **values;
+       uint8_t uuid16 = SDP_UUID16;
+       uint8_t uuid32 = SDP_UUID32;
+       uint8_t uuid128 = SDP_UUID128;
+       sdp_list_t *p;
+
+       len = sdp_list_len(seq);
+       if (!seq || len == 0)
+               return -1;
+       dtds = malloc(len * sizeof(void *));
+       if (!dtds)
+               return -1;
+
+       values = malloc(len * sizeof(void *));
+       if (!values) {
+               free(dtds);
+               return -1;
+       }
+
+       for (p = seq, i = 0; i < len; i++, p = p->next) {
+               uuid_t *uuid = p->data;
+               if (uuid)
+                       switch (uuid->type) {
+                       case SDP_UUID16:
+                               dtds[i] = &uuid16;
+                               values[i] = &uuid->value.uuid16;
+                               break;
+                       case SDP_UUID32:
+                               dtds[i] = &uuid32;
+                               values[i] = &uuid->value.uuid32;
+                               break;
+                       case SDP_UUID128:
+                               dtds[i] = &uuid128;
+                               values[i] = &uuid->value.uuid128;
+                               break;
+                       default:
+                               status = -1;
+                               break;
+                       }
+               else {
+                       status = -1;
+                       break;
+               }
+       }
+       if (status == 0) {
+               sdp_data_t *data = sdp_seq_alloc(dtds, values, len);
+               sdp_attr_replace(rec, aid, data);
+               sdp_pattern_add_uuidseq(rec, seq);
+       }
+       free(dtds);
+       free(values);
+       return status;
+}
+
+int sdp_get_lang_attr(const sdp_record_t *rec, sdp_list_t **langSeq)
+{
+       sdp_lang_attr_t *lang;
+       sdp_data_t *sdpdata, *curr_data;
+
+       *langSeq = NULL;
+       sdpdata = sdp_data_get(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST);
+       if (sdpdata == NULL) {
+               errno = ENODATA;
+               return -1;
+       }
+       curr_data = sdpdata->val.dataseq;
+       while (curr_data) {
+               sdp_data_t *pCode = curr_data;
+               sdp_data_t *pEncoding;
+               sdp_data_t *pOffset;
+
+               pEncoding = pCode->next;
+               if (!pEncoding)
+                       break;
+
+               pOffset = pEncoding->next;
+               if (!pOffset)
+                       break;
+
+               lang = malloc(sizeof(sdp_lang_attr_t));
+               if (!lang) {
+                       sdp_list_free(*langSeq, free);
+                       *langSeq = NULL;
+                       return -1;
+               }
+               lang->code_ISO639 = pCode->val.uint16;
+               lang->encoding = pEncoding->val.uint16;
+               lang->base_offset = pOffset->val.uint16;
+               SDPDBG("code_ISO639 :  0x%02x\n", lang->code_ISO639);
+               SDPDBG("encoding :     0x%02x\n", lang->encoding);
+               SDPDBG("base_offfset : 0x%02x\n", lang->base_offset);
+               *langSeq = sdp_list_append(*langSeq, lang);
+
+               curr_data = pOffset->next;
+       }
+
+       return 0;
+}
+
+int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDescSeq)
+{
+       sdp_profile_desc_t *profDesc;
+       sdp_data_t *sdpdata, *seq;
+
+       *profDescSeq = NULL;
+       sdpdata = sdp_data_get(rec, SDP_ATTR_PFILE_DESC_LIST);
+       if (!sdpdata || !sdpdata->val.dataseq) {
+               errno = ENODATA;
+               return -1;
+       }
+       for (seq = sdpdata->val.dataseq; seq && seq->val.dataseq; seq = seq->next) {
+               uuid_t *uuid = NULL;
+               uint16_t version = 0x100;
+
+               if (SDP_IS_UUID(seq->dtd)) {
+                       sdp_data_t *next = seq->next;
+                       uuid = &seq->val.uuid;
+                       if (next && next->dtd == SDP_UINT16) {
+                               version = next->val.uint16;
+                               seq = next;
+                       }
+               } else if (SDP_IS_SEQ(seq->dtd)) {
+                       sdp_data_t *puuid = seq->val.dataseq;
+                       sdp_data_t *pVnum = seq->val.dataseq->next;
+                       if (puuid && pVnum) {
+                               uuid = &puuid->val.uuid;
+                               version = pVnum->val.uint16;
+                       }
+               }
+
+               if (uuid != NULL) {
+                       profDesc = malloc(sizeof(sdp_profile_desc_t));
+                       if (!profDesc) {
+                               sdp_list_free(*profDescSeq, free);
+                               *profDescSeq = NULL;
+                               return -1;
+                       }
+                       profDesc->uuid = *uuid;
+                       profDesc->version = version;
+#ifdef SDP_DEBUG
+                       sdp_uuid_print(&profDesc->uuid);
+                       SDPDBG("Vnum : 0x%04x\n", profDesc->version);
+#endif
+                       *profDescSeq = sdp_list_append(*profDescSeq, profDesc);
+               }
+       }
+       return 0;
+}
+
+int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **u16)
+{
+       sdp_data_t *d, *curr;
+
+       *u16 = NULL;
+       d = sdp_data_get(rec, SDP_ATTR_VERSION_NUM_LIST);
+       if (d == NULL) {
+               errno = ENODATA;
+               return -1;
+       }
+       for (curr = d->val.dataseq; curr; curr = curr->next)
+               *u16 = sdp_list_append(*u16, &curr->val.uint16);
+       return 0;
+}
+
+/* flexible extraction of basic attributes - Jean II */
+/* How do we expect caller to extract predefined data sequences? */
+int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attrid, int *value)
+{
+       sdp_data_t *sdpdata = sdp_data_get(rec, attrid);
+
+       if (sdpdata)
+               /* Verify that it is what the caller expects */
+               if (sdpdata->dtd == SDP_BOOL || sdpdata->dtd == SDP_UINT8 ||
+               sdpdata->dtd == SDP_UINT16 || sdpdata->dtd == SDP_UINT32 ||
+               sdpdata->dtd == SDP_INT8 || sdpdata->dtd == SDP_INT16 ||
+               sdpdata->dtd == SDP_INT32) {
+                       *value = sdpdata->val.uint32;
+                       return 0;
+               }
+       errno = EINVAL;
+       return -1;
+}
+
+int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attrid, char *value,
+                                                               int valuelen)
+{
+       sdp_data_t *sdpdata = sdp_data_get(rec, attrid);
+       if (sdpdata)
+               /* Verify that it is what the caller expects */
+               if (sdpdata->dtd == SDP_TEXT_STR8 ||
+                               sdpdata->dtd == SDP_TEXT_STR16 ||
+                               sdpdata->dtd == SDP_TEXT_STR32)
+                       if ((int) strlen(sdpdata->val.str) < valuelen) {
+                               strcpy(value, sdpdata->val.str);
+                               return 0;
+                       }
+       errno = EINVAL;
+       return -1;
+}
+
+#define get_basic_attr(attrID, pAttrValue, fieldName)          \
+       sdp_data_t *data = sdp_data_get(rec, attrID);           \
+       if (data) {                                             \
+               *pAttrValue = data->val.fieldName;              \
+               return 0;                                       \
+       }                                                       \
+       errno = EINVAL;                                         \
+       return -1;
+
+int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid)
+{
+       get_basic_attr(SDP_ATTR_SERVICE_ID, uuid, uuid);
+}
+
+int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid)
+{
+       get_basic_attr(SDP_ATTR_GROUP_ID, uuid, uuid);
+}
+
+int sdp_get_record_state(const sdp_record_t *rec, uint32_t *svcRecState)
+{
+       get_basic_attr(SDP_ATTR_RECORD_STATE, svcRecState, uint32);
+}
+
+int sdp_get_service_avail(const sdp_record_t *rec, uint8_t *svcAvail)
+{
+       get_basic_attr(SDP_ATTR_SERVICE_AVAILABILITY, svcAvail, uint8);
+}
+
+int sdp_get_service_ttl(const sdp_record_t *rec, uint32_t *svcTTLInfo)
+{
+       get_basic_attr(SDP_ATTR_SVCINFO_TTL, svcTTLInfo, uint32);
+}
+
+int sdp_get_database_state(const sdp_record_t *rec, uint32_t *svcDBState)
+{
+       get_basic_attr(SDP_ATTR_SVCDB_STATE, svcDBState, uint32);
+}
+
+/*
+ * NOTE that none of the setXXX() functions below will
+ * actually update the SDP server, unless the
+ * {register, update}sdp_record_t() function is invoked.
+ */
+
+int sdp_attr_add_new(sdp_record_t *rec, uint16_t attr, uint8_t dtd,
+                                                       const void *value)
+{
+       sdp_data_t *d = sdp_data_alloc(dtd, value);
+       if (d) {
+               sdp_attr_replace(rec, attr, d);
+               return 0;
+       }
+       return -1;
+}
+
+static int sdp_attr_add_new_with_length(sdp_record_t *rec,
+       uint16_t attr, uint8_t dtd, const void *value, uint32_t len)
+{
+       sdp_data_t *d;
+
+       d = sdp_data_alloc_with_length(dtd, value, len);
+       if (!d)
+               return -1;
+
+       sdp_attr_replace(rec, attr, d);
+
+       return 0;
+}
+
+/*
+ * Set the information attributes of the service
+ * pointed to by rec. The attributes are
+ * service name, description and provider name
+ */
+void sdp_set_info_attr(sdp_record_t *rec, const char *name, const char *prov,
+                                                       const char *desc)
+{
+       if (name)
+               sdp_attr_add_new(rec, SDP_ATTR_SVCNAME_PRIMARY,
+                                                       SDP_TEXT_STR8, name);
+       if (prov)
+               sdp_attr_add_new(rec, SDP_ATTR_PROVNAME_PRIMARY,
+                                                       SDP_TEXT_STR8, prov);
+       if (desc)
+               sdp_attr_add_new(rec, SDP_ATTR_SVCDESC_PRIMARY,
+                                                       SDP_TEXT_STR8, desc);
+}
+
+static sdp_data_t *access_proto_to_dataseq(sdp_record_t *rec, sdp_list_t *proto)
+{
+       sdp_data_t *seq = NULL;
+       void *dtds[10], *values[10];
+       void **seqDTDs, **seqs;
+       int i, seqlen;
+       sdp_list_t *p;
+
+       seqlen = sdp_list_len(proto);
+       seqDTDs = malloc(seqlen * sizeof(void *));
+       if (!seqDTDs)
+               return NULL;
+
+       seqs = malloc(seqlen * sizeof(void *));
+       if (!seqs) {
+               free(seqDTDs);
+               return NULL;
+       }
+
+       for (i = 0, p = proto; p; p = p->next, i++) {
+               sdp_list_t *elt = p->data;
+               sdp_data_t *s;
+               uuid_t *uuid = NULL;
+               unsigned int pslen = 0;
+               for (; elt && pslen < ARRAY_SIZE(dtds); elt = elt->next, pslen++) {
+                       sdp_data_t *d = elt->data;
+                       dtds[pslen] = &d->dtd;
+                       switch (d->dtd) {
+                       case SDP_UUID16:
+                               uuid = (uuid_t *) d;
+                               values[pslen] = &uuid->value.uuid16;
+                               break;
+                       case SDP_UUID32:
+                               uuid = (uuid_t *) d;
+                               values[pslen] = &uuid->value.uuid32;
+                               break;
+                       case SDP_UUID128:
+                               uuid = (uuid_t *) d;
+                               values[pslen] = &uuid->value.uuid128;
+                               break;
+                       case SDP_UINT8:
+                               values[pslen] = &d->val.uint8;
+                               break;
+                       case SDP_UINT16:
+                               values[pslen] = &d->val.uint16;
+                               break;
+                       case SDP_SEQ8:
+                       case SDP_SEQ16:
+                       case SDP_SEQ32:
+                               values[pslen] = d;
+                               break;
+                       /* FIXME: more */
+                       }
+               }
+               s = sdp_seq_alloc(dtds, values, pslen);
+               if (s) {
+                       seqDTDs[i] = &s->dtd;
+                       seqs[i] = s;
+                       if (uuid)
+                               sdp_pattern_add_uuid(rec, uuid);
+               }
+       }
+       seq = sdp_seq_alloc(seqDTDs, seqs, seqlen);
+       free(seqDTDs);
+       free(seqs);
+       return seq;
+}
+
+/*
+ * sets the access protocols of the service specified
+ * to the value specified in "access_proto"
+ *
+ * Note that if there are alternate mechanisms by
+ * which the service is accessed, then they should
+ * be specified as sequences
+ *
+ * Using a value of NULL for accessProtocols has
+ * effect of removing this attribute (if previously set)
+ *
+ * This function replaces the existing sdp_access_proto_t
+ * structure (if any) with the new one specified.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+int sdp_set_access_protos(sdp_record_t *rec, const sdp_list_t *ap)
+{
+       const sdp_list_t *p;
+       sdp_data_t *protos = NULL;
+
+       for (p = ap; p; p = p->next) {
+               sdp_data_t *seq = access_proto_to_dataseq(rec, p->data);
+               protos = sdp_seq_append(protos, seq);
+       }
+
+       sdp_attr_add(rec, SDP_ATTR_PROTO_DESC_LIST, protos);
+
+       return 0;
+}
+
+int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *ap)
+{
+       const sdp_list_t *p;
+       sdp_data_t *protos = NULL;
+
+       for (p = ap; p; p = p->next) {
+               sdp_data_t *seq = access_proto_to_dataseq(rec, p->data);
+               protos = sdp_seq_append(protos, seq);
+       }
+
+       sdp_attr_add(rec, SDP_ATTR_ADD_PROTO_DESC_LIST,
+                       protos ? sdp_data_alloc(SDP_SEQ8, protos) : NULL);
+
+       return 0;
+}
+
+/*
+ * set the "LanguageBase" attributes of the service record
+ * record to the value specified in "langAttrList".
+ *
+ * "langAttrList" is a linked list of "sdp_lang_attr_t"
+ * objects, one for each language in which user visible
+ * attributes are present in the service record.
+ *
+ * Using a value of NULL for langAttrList has
+ * effect of removing this attribute (if previously set)
+ *
+ * This function replaces the exisiting sdp_lang_attr_t
+ * structure (if any) with the new one specified.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+int sdp_set_lang_attr(sdp_record_t *rec, const sdp_list_t *seq)
+{
+       uint8_t uint16 = SDP_UINT16;
+       int status = 0, i = 0, seqlen = sdp_list_len(seq);
+       void **dtds, **values;
+       const sdp_list_t *p;
+
+       dtds = malloc(3 * seqlen * sizeof(void *));
+       if (!dtds)
+               return -1;
+
+       values = malloc(3 * seqlen * sizeof(void *));
+       if (!values) {
+               free(dtds);
+               return -1;
+       }
+
+       for (p = seq; p; p = p->next) {
+               sdp_lang_attr_t *lang = p->data;
+               if (!lang) {
+                       status = -1;
+                       break;
+               }
+               dtds[i] = &uint16;
+               values[i] = &lang->code_ISO639;
+               i++;
+               dtds[i] = &uint16;
+               values[i] = &lang->encoding;
+               i++;
+               dtds[i] = &uint16;
+               values[i] = &lang->base_offset;
+               i++;
+       }
+       if (status == 0) {
+               sdp_data_t *seq = sdp_seq_alloc(dtds, values, 3 * seqlen);
+               sdp_attr_add(rec, SDP_ATTR_LANG_BASE_ATTR_ID_LIST, seq);
+       }
+       free(dtds);
+       free(values);
+       return status;
+}
+
+/*
+ * set the "ServiceID" attribute of the service.
+ *
+ * This is the UUID of the service.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+void sdp_set_service_id(sdp_record_t *rec, uuid_t uuid)
+{
+       switch (uuid.type) {
+       case SDP_UUID16:
+               sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID16,
+                                                       &uuid.value.uuid16);
+               break;
+       case SDP_UUID32:
+               sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID32,
+                                                       &uuid.value.uuid32);
+               break;
+       case SDP_UUID128:
+               sdp_attr_add_new(rec, SDP_ATTR_SERVICE_ID, SDP_UUID128,
+                                                       &uuid.value.uuid128);
+               break;
+       }
+       sdp_pattern_add_uuid(rec, &uuid);
+}
+
+/*
+ * set the GroupID attribute of the service record defining a group.
+ *
+ * This is the UUID of the group.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+void sdp_set_group_id(sdp_record_t *rec, uuid_t uuid)
+{
+       switch (uuid.type) {
+       case SDP_UUID16:
+               sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID16,
+                                                       &uuid.value.uuid16);
+               break;
+       case SDP_UUID32:
+               sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID32,
+                                                       &uuid.value.uuid32);
+               break;
+       case SDP_UUID128:
+               sdp_attr_add_new(rec, SDP_ATTR_GROUP_ID, SDP_UUID128,
+                                                       &uuid.value.uuid128);
+               break;
+       }
+       sdp_pattern_add_uuid(rec, &uuid);
+}
+
+/*
+ * set the ProfileDescriptorList attribute of the service record
+ * pointed to by record to the value specified in "profileDesc".
+ *
+ * Each element in the list is an object of type
+ * sdp_profile_desc_t which is a definition of the
+ * Bluetooth profile that this service conforms to.
+ *
+ * Using a value of NULL for profileDesc has
+ * effect of removing this attribute (if previously set)
+ *
+ * This function replaces the exisiting ProfileDescriptorList
+ * structure (if any) with the new one specified.
+ *
+ * returns 0 if successful or -1 if there is a failure.
+ */
+int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *profiles)
+{
+       int status = 0;
+       uint8_t uuid16 = SDP_UUID16;
+       uint8_t uuid32 = SDP_UUID32;
+       uint8_t uuid128 = SDP_UUID128;
+       uint8_t uint16 = SDP_UINT16;
+       int i = 0, seqlen = sdp_list_len(profiles);
+       void **seqDTDs, **seqs;
+       const sdp_list_t *p;
+
+       seqDTDs = malloc(seqlen * sizeof(void *));
+       if (!seqDTDs)
+               return -1;
+
+       seqs = malloc(seqlen * sizeof(void *));
+       if (!seqs) {
+               free(seqDTDs);
+               return -1;
+       }
+
+       for (p = profiles; p; p = p->next) {
+               sdp_data_t *seq;
+               void *dtds[2], *values[2];
+               sdp_profile_desc_t *profile = p->data;
+               if (!profile) {
+                       status = -1;
+                       break;
+               }
+               switch (profile->uuid.type) {
+               case SDP_UUID16:
+                       dtds[0] = &uuid16;
+                       values[0] = &profile->uuid.value.uuid16;
+                       break;
+               case SDP_UUID32:
+                       dtds[0] = &uuid32;
+                       values[0] = &profile->uuid.value.uuid32;
+                       break;
+               case SDP_UUID128:
+                       dtds[0] = &uuid128;
+                       values[0] = &profile->uuid.value.uuid128;
+                       break;
+               default:
+                       status = -1;
+                       break;
+               }
+               dtds[1] = &uint16;
+               values[1] = &profile->version;
+               seq = sdp_seq_alloc(dtds, values, 2);
+               if (seq) {
+                       seqDTDs[i] = &seq->dtd;
+                       seqs[i] = seq;
+                       sdp_pattern_add_uuid(rec, &profile->uuid);
+               }
+               i++;
+       }
+       if (status == 0) {
+               sdp_data_t *pAPSeq = sdp_seq_alloc(seqDTDs, seqs, seqlen);
+               sdp_attr_add(rec, SDP_ATTR_PFILE_DESC_LIST, pAPSeq);
+       }
+       free(seqDTDs);
+       free(seqs);
+       return status;
+}
+
+/*
+ * sets various URL attributes of the service
+ * pointed to by record. The URL include
+ *
+ * client: a URL to the client's
+ *   platform specific (WinCE, PalmOS) executable
+ *   code that can be used to access this service.
+ *
+ * doc: a URL pointing to service documentation
+ *
+ * icon: a URL to an icon that can be used to represent
+ *   this service.
+ *
+ * Note that you need to pass NULL for any URLs
+ * that you don't want to set or remove
+ */
+void sdp_set_url_attr(sdp_record_t *rec, const char *client, const char *doc,
+                                                       const char *icon)
+{
+       sdp_attr_add_new(rec, SDP_ATTR_CLNT_EXEC_URL, SDP_URL_STR8, client);
+       sdp_attr_add_new(rec, SDP_ATTR_DOC_URL, SDP_URL_STR8, doc);
+       sdp_attr_add_new(rec, SDP_ATTR_ICON_URL, SDP_URL_STR8, icon);
+}
+
+uuid_t *sdp_uuid16_create(uuid_t *u, uint16_t val)
+{
+       memset(u, 0, sizeof(uuid_t));
+       u->type = SDP_UUID16;
+       u->value.uuid16 = val;
+       return u;
+}
+
+uuid_t *sdp_uuid32_create(uuid_t *u, uint32_t val)
+{
+       memset(u, 0, sizeof(uuid_t));
+       u->type = SDP_UUID32;
+       u->value.uuid32 = val;
+       return u;
+}
+
+uuid_t *sdp_uuid128_create(uuid_t *u, const void *val)
+{
+       memset(u, 0, sizeof(uuid_t));
+       u->type = SDP_UUID128;
+       memcpy(&u->value.uuid128, val, sizeof(uint128_t));
+       return u;
+}
+
+/*
+ * UUID comparison function
+ * returns 0 if uuidValue1 == uuidValue2 else -1
+ */
+int sdp_uuid_cmp(const void *p1, const void *p2)
+{
+       uuid_t *u1 = sdp_uuid_to_uuid128(p1);
+       uuid_t *u2 = sdp_uuid_to_uuid128(p2);
+       int ret;
+
+       ret = sdp_uuid128_cmp(u1, u2);
+
+       bt_free(u1);
+       bt_free(u2);
+
+       return ret;
+}
+
+/*
+ * UUID comparison function
+ * returns 0 if uuidValue1 == uuidValue2 else -1
+ */
+int sdp_uuid16_cmp(const void *p1, const void *p2)
+{
+       const uuid_t *u1 = p1;
+       const uuid_t *u2 = p2;
+       return memcmp(&u1->value.uuid16, &u2->value.uuid16, sizeof(uint16_t));
+}
+
+/*
+ * UUID comparison function
+ * returns 0 if uuidValue1 == uuidValue2 else -1
+ */
+int sdp_uuid128_cmp(const void *p1, const void *p2)
+{
+       const uuid_t *u1 = p1;
+       const uuid_t *u2 = p2;
+       return memcmp(&u1->value.uuid128, &u2->value.uuid128, sizeof(uint128_t));
+}
+
+/*
+ * 128 to 16 bit and 32 to 16 bit UUID conversion functions
+ * yet to be implemented. Note that the input is in NBO in
+ * both 32 and 128 bit UUIDs and conversion is needed
+ */
+void sdp_uuid16_to_uuid128(uuid_t *uuid128, const uuid_t *uuid16)
+{
+       /*
+        * We have a 16 bit value, which needs to be added to
+        * bytes 3 and 4 (at indices 2 and 3) of the Bluetooth base
+        */
+       unsigned short data1;
+
+       /* allocate a 128bit UUID and init to the Bluetooth base UUID */
+       uuid128->value.uuid128 = bluetooth_base_uuid;
+       uuid128->type = SDP_UUID128;
+
+       /* extract bytes 2 and 3 of 128bit BT base UUID */
+       memcpy(&data1, &bluetooth_base_uuid.data[2], 2);
+
+       /* add the given UUID (16 bits) */
+       data1 += htons(uuid16->value.uuid16);
+
+       /* set bytes 2 and 3 of the 128 bit value */
+       memcpy(&uuid128->value.uuid128.data[2], &data1, 2);
+}
+
+void sdp_uuid32_to_uuid128(uuid_t *uuid128, const uuid_t *uuid32)
+{
+       /*
+        * We have a 32 bit value, which needs to be added to
+        * bytes 1->4 (at indices 0 thru 3) of the Bluetooth base
+        */
+       unsigned int data0;
+
+       /* allocate a 128bit UUID and init to the Bluetooth base UUID */
+       uuid128->value.uuid128 = bluetooth_base_uuid;
+       uuid128->type = SDP_UUID128;
+
+       /* extract first 4 bytes */
+       memcpy(&data0, &bluetooth_base_uuid.data[0], 4);
+
+       /* add the given UUID (32bits) */
+       data0 += htonl(uuid32->value.uuid32);
+
+       /* set the 4 bytes of the 128 bit value */
+       memcpy(&uuid128->value.uuid128.data[0], &data0, 4);
+}
+
+uuid_t *sdp_uuid_to_uuid128(const uuid_t *uuid)
+{
+       uuid_t *uuid128 = bt_malloc(sizeof(uuid_t));
+
+       if (!uuid128)
+               return NULL;
+
+       memset(uuid128, 0, sizeof(uuid_t));
+       switch (uuid->type) {
+       case SDP_UUID128:
+               *uuid128 = *uuid;
+               break;
+       case SDP_UUID32:
+               sdp_uuid32_to_uuid128(uuid128, uuid);
+               break;
+       case SDP_UUID16:
+               sdp_uuid16_to_uuid128(uuid128, uuid);
+               break;
+       }
+       return uuid128;
+}
+
+/*
+ * converts a 128-bit uuid to a 16/32-bit one if possible
+ * returns true if uuid contains a 16/32-bit UUID at exit
+ */
+int sdp_uuid128_to_uuid(uuid_t *uuid)
+{
+       uint128_t *b = &bluetooth_base_uuid;
+       uint128_t *u = &uuid->value.uuid128;
+       uint32_t data;
+       unsigned int i;
+
+       if (uuid->type != SDP_UUID128)
+               return 1;
+
+       for (i = 4; i < sizeof(b->data); i++)
+               if (b->data[i] != u->data[i])
+                       return 0;
+
+       memcpy(&data, u->data, 4);
+       data = htonl(data);
+       if (data <= 0xffff) {
+               uuid->type = SDP_UUID16;
+               uuid->value.uuid16 = (uint16_t) data;
+       } else {
+               uuid->type = SDP_UUID32;
+               uuid->value.uuid32 = data;
+       }
+       return 1;
+}
+
+/*
+ * convert a UUID to the 16-bit short-form
+ */
+int sdp_uuid_to_proto(uuid_t *uuid)
+{
+       uuid_t u = *uuid;
+       if (sdp_uuid128_to_uuid(&u)) {
+               switch (u.type) {
+               case SDP_UUID16:
+                       return u.value.uuid16;
+               case SDP_UUID32:
+                       return u.value.uuid32;
+               }
+       }
+       return 0;
+}
+
+/*
+ * This function appends data to the PDU buffer "dst" from source "src".
+ * The data length is also computed and set.
+ * Should the PDU length exceed 2^8, then sequence type is
+ * set accordingly and the data is memmove()'d.
+ */
+void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len)
+{
+       uint8_t *p = dst->data;
+       uint8_t dtd = *p;
+
+       SDPDBG("Append src size: %d\n", len);
+       SDPDBG("Append dst size: %d\n", dst->data_size);
+       SDPDBG("Dst buffer size: %d\n", dst->buf_size);
+       if (dst->data_size == 0 && dtd == 0) {
+               /* create initial sequence */
+               *p = SDP_SEQ8;
+               dst->data_size += sizeof(uint8_t);
+               /* reserve space for sequence size */
+               dst->data_size += sizeof(uint8_t);
+       }
+
+       memcpy(dst->data + dst->data_size, data, len);
+       dst->data_size += len;
+
+       dtd = *(uint8_t *) dst->data;
+       if (dst->data_size > UCHAR_MAX && dtd == SDP_SEQ8) {
+               short offset = sizeof(uint8_t) + sizeof(uint8_t);
+               memmove(dst->data + offset + 1, dst->data + offset,
+                                               dst->data_size - offset);
+               *p = SDP_SEQ16;
+               dst->data_size += 1;
+       }
+       dtd = *(uint8_t *) p;
+       p += sizeof(uint8_t);
+       switch (dtd) {
+       case SDP_SEQ8:
+               *(uint8_t *) p = dst->data_size - sizeof(uint8_t) - sizeof(uint8_t);
+               break;
+       case SDP_SEQ16:
+               bt_put_unaligned(htons(dst->data_size - sizeof(uint8_t) - sizeof(uint16_t)), (uint16_t *) p);
+               break;
+       case SDP_SEQ32:
+               bt_put_unaligned(htonl(dst->data_size - sizeof(uint8_t) - sizeof(uint32_t)), (uint32_t *) p);
+               break;
+       }
+}
+
+void sdp_append_to_pdu(sdp_buf_t *pdu, sdp_data_t *d)
+{
+       sdp_buf_t append;
+
+       memset(&append, 0, sizeof(sdp_buf_t));
+       sdp_gen_buffer(&append, d);
+       append.data = malloc(append.buf_size);
+       if (!append.data)
+               return;
+
+       sdp_set_attrid(&append, d->attrId);
+       sdp_gen_pdu(&append, d);
+       sdp_append_to_buf(pdu, append.data, append.data_size);
+       free(append.data);
+}
+
+/*
+ * Registers an sdp record.
+ *
+ * It is incorrect to call this method on a record that
+ * has been already registered with the server.
+ *
+ * Returns zero on success, otherwise -1 (and sets errno).
+ */
+int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle)
+{
+       uint8_t *req, *rsp, *p;
+       uint32_t reqsize, rspsize;
+       sdp_pdu_hdr_t *reqhdr, *rsphdr;
+       int status;
+
+       SDPDBG("");
+
+       if (!session->local) {
+               errno = EREMOTE;
+               return -1;
+       }
+       req = malloc(SDP_REQ_BUFFER_SIZE);
+       rsp = malloc(SDP_RSP_BUFFER_SIZE);
+       if (req == NULL || rsp == NULL) {
+               status = -1;
+               errno = ENOMEM;
+               goto end;
+       }
+
+       reqhdr = (sdp_pdu_hdr_t *)req;
+       reqhdr->pdu_id = SDP_SVC_REGISTER_REQ;
+       reqhdr->tid    = htons(sdp_gen_tid(session));
+       reqsize = sizeof(sdp_pdu_hdr_t) + 1;
+       p = req + sizeof(sdp_pdu_hdr_t);
+
+       if (bacmp(device, BDADDR_ANY)) {
+               *p++ = flags | SDP_DEVICE_RECORD;
+               bacpy((bdaddr_t *) p, device);
+               p += sizeof(bdaddr_t);
+               reqsize += sizeof(bdaddr_t);
+       } else
+               *p++ = flags;
+
+       memcpy(p, data, size);
+       reqsize += size;
+       reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+       status = sdp_send_req_w4_rsp(session, req, rsp, reqsize, &rspsize);
+       if (status < 0)
+               goto end;
+
+       if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+               SDPERR("Unexpected end of packet");
+               errno = EPROTO;
+               status = -1;
+               goto end;
+       }
+
+       rsphdr = (sdp_pdu_hdr_t *) rsp;
+       p = rsp + sizeof(sdp_pdu_hdr_t);
+
+       if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+               /* Invalid service record */
+               errno = EINVAL;
+               status = -1;
+       } else if (rsphdr->pdu_id != SDP_SVC_REGISTER_RSP) {
+               errno = EPROTO;
+               status = -1;
+       } else {
+               if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint32_t)) {
+                       SDPERR("Unexpected end of packet");
+                       errno = EPROTO;
+                       status = -1;
+                       goto end;
+               }
+               if (handle)
+                       *handle  = ntohl(bt_get_unaligned((uint32_t *) p));
+       }
+
+end:
+       free(req);
+       free(rsp);
+
+       return status;
+}
+
+int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags)
+{
+       sdp_buf_t pdu;
+       uint32_t handle;
+       int err;
+
+       SDPDBG("");
+
+       if (rec->handle && rec->handle != 0xffffffff) {
+               uint32_t handle = rec->handle;
+               sdp_data_t *data = sdp_data_alloc(SDP_UINT32, &handle);
+               sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
+       }
+
+       if (sdp_gen_record_pdu(rec, &pdu) < 0) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       err = sdp_device_record_register_binary(session, device,
+                               pdu.data, pdu.data_size, flags, &handle);
+
+       free(pdu.data);
+
+       if (err == 0) {
+               sdp_data_t *data = sdp_data_alloc(SDP_UINT32, &handle);
+               rec->handle = handle;
+               sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
+       }
+
+       return err;
+}
+
+int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags)
+{
+       return sdp_device_record_register(session, BDADDR_ANY, rec, flags);
+}
+
+/*
+ * unregister a service record
+ */
+int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle)
+{
+       uint8_t *reqbuf, *rspbuf, *p;
+       uint32_t reqsize = 0, rspsize = 0;
+       sdp_pdu_hdr_t *reqhdr, *rsphdr;
+       int status;
+
+       SDPDBG("");
+
+       if (handle == SDP_SERVER_RECORD_HANDLE) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (!session->local) {
+               errno = EREMOTE;
+               return -1;
+       }
+
+       reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+       rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+       if (!reqbuf || !rspbuf) {
+               errno = ENOMEM;
+               status = -1;
+               goto end;
+       }
+       reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+       reqhdr->pdu_id = SDP_SVC_REMOVE_REQ;
+       reqhdr->tid    = htons(sdp_gen_tid(session));
+
+       p = reqbuf + sizeof(sdp_pdu_hdr_t);
+       reqsize = sizeof(sdp_pdu_hdr_t);
+       bt_put_unaligned(htonl(handle), (uint32_t *) p);
+       reqsize += sizeof(uint32_t);
+
+       reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+       status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+       if (status < 0)
+               goto end;
+
+       if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint16_t)) {
+               SDPERR("Unexpected end of packet");
+               errno = EPROTO;
+               status = -1;
+               goto end;
+       }
+
+       rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+       p = rspbuf + sizeof(sdp_pdu_hdr_t);
+       status = bt_get_unaligned((uint16_t *) p);
+
+       if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+               /* For this case the status always is invalid record handle */
+               errno = EINVAL;
+               status = -1;
+       } else if (rsphdr->pdu_id != SDP_SVC_REMOVE_RSP) {
+               errno = EPROTO;
+               status = -1;
+       }
+end:
+       free(reqbuf);
+       free(rspbuf);
+
+       return status;
+}
+
+int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec)
+{
+       int err;
+
+       err = sdp_device_record_unregister_binary(session, device, rec->handle);
+       if (err == 0)
+               sdp_record_free(rec);
+
+       return err;
+}
+
+int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec)
+{
+       return sdp_device_record_unregister(session, BDADDR_ANY, rec);
+}
+
+/*
+ * modify an existing service record
+ */
+int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size)
+{
+       return -1;
+}
+
+int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec)
+{
+       uint8_t *reqbuf, *rspbuf, *p;
+       uint32_t reqsize, rspsize;
+       sdp_pdu_hdr_t *reqhdr, *rsphdr;
+       uint32_t handle;
+       sdp_buf_t pdu;
+       int status;
+
+       SDPDBG("");
+
+       handle = rec->handle;
+
+       if (handle == SDP_SERVER_RECORD_HANDLE) {
+               errno = EINVAL;
+               return -1;
+       }
+       if (!session->local) {
+               errno = EREMOTE;
+               return -1;
+       }
+       reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+       rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+       if (!reqbuf || !rspbuf) {
+               errno = ENOMEM;
+               status = -1;
+               goto end;
+       }
+       reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+       reqhdr->pdu_id = SDP_SVC_UPDATE_REQ;
+       reqhdr->tid    = htons(sdp_gen_tid(session));
+
+       p = reqbuf + sizeof(sdp_pdu_hdr_t);
+       reqsize = sizeof(sdp_pdu_hdr_t);
+
+       bt_put_unaligned(htonl(handle), (uint32_t *) p);
+       reqsize += sizeof(uint32_t);
+       p += sizeof(uint32_t);
+
+       if (sdp_gen_record_pdu(rec, &pdu) < 0) {
+               errno = ENOMEM;
+               status = -1;
+               goto end;
+       }
+       memcpy(p, pdu.data, pdu.data_size);
+       reqsize += pdu.data_size;
+       free(pdu.data);
+
+       reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+       status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+       if (status < 0)
+               goto end;
+
+       if (rspsize < sizeof(sdp_pdu_hdr_t) + sizeof(uint16_t)) {
+               SDPERR("Unexpected end of packet");
+               errno = EPROTO;
+               status = -1;
+               goto end;
+       }
+
+       SDPDBG("Send req status : %d\n", status);
+
+       rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+       p = rspbuf + sizeof(sdp_pdu_hdr_t);
+       status = bt_get_unaligned((uint16_t *) p);
+
+       if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+               /* The status can be invalid sintax or invalid record handle */
+               errno = EINVAL;
+               status = -1;
+       } else if (rsphdr->pdu_id != SDP_SVC_UPDATE_RSP) {
+               errno = EPROTO;
+               status = -1;
+       }
+end:
+       free(reqbuf);
+       free(rspbuf);
+       return status;
+}
+
+int sdp_record_update(sdp_session_t *session, const sdp_record_t *rec)
+{
+       return sdp_device_record_update(session, BDADDR_ANY, rec);
+}
+
+sdp_record_t *sdp_record_alloc(void)
+{
+       sdp_record_t *rec = malloc(sizeof(sdp_record_t));
+
+       if (!rec)
+               return NULL;
+
+       memset(rec, 0, sizeof(sdp_record_t));
+       rec->handle = 0xffffffff;
+       return rec;
+}
+
+/*
+ * Free the contents of a service record
+ */
+void sdp_record_free(sdp_record_t *rec)
+{
+       sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free);
+       sdp_list_free(rec->pattern, free);
+       free(rec);
+}
+
+void sdp_pattern_add_uuid(sdp_record_t *rec, uuid_t *uuid)
+{
+       uuid_t *uuid128 = sdp_uuid_to_uuid128(uuid);
+
+       SDPDBG("Elements in target pattern : %d\n", sdp_list_len(rec->pattern));
+       SDPDBG("Trying to add : 0x%lx\n", (unsigned long) uuid128);
+
+       if (sdp_list_find(rec->pattern, uuid128, sdp_uuid128_cmp) == NULL)
+               rec->pattern = sdp_list_insert_sorted(rec->pattern, uuid128, sdp_uuid128_cmp);
+       else
+               bt_free(uuid128);
+
+       SDPDBG("Elements in target pattern : %d\n", sdp_list_len(rec->pattern));
+}
+
+void sdp_pattern_add_uuidseq(sdp_record_t *rec, sdp_list_t *seq)
+{
+       for (; seq; seq = seq->next) {
+               uuid_t *uuid = (uuid_t *)seq->data;
+               sdp_pattern_add_uuid(rec, uuid);
+       }
+}
+
+/*
+ * Extract a sequence of service record handles from a PDU buffer
+ * and add the entries to a sdp_list_t. Note that the service record
+ * handles are not in "data element sequence" form, but just like
+ * an array of service handles
+ */
+static void extract_record_handle_seq(uint8_t *pdu, int bufsize, sdp_list_t **seq, int count, unsigned int *scanned)
+{
+       sdp_list_t *pSeq = *seq;
+       uint8_t *pdata = pdu;
+       int n;
+
+       for (n = 0; n < count; n++) {
+               uint32_t *pSvcRec;
+               if (bufsize < (int) sizeof(uint32_t)) {
+                       SDPERR("Unexpected end of packet");
+                       break;
+               }
+               pSvcRec = malloc(sizeof(uint32_t));
+               if (!pSvcRec)
+                       break;
+               *pSvcRec = ntohl(bt_get_unaligned((uint32_t *) pdata));
+               pSeq = sdp_list_append(pSeq, pSvcRec);
+               pdata += sizeof(uint32_t);
+               *scanned += sizeof(uint32_t);
+               bufsize -= sizeof(uint32_t);
+       }
+       *seq = pSeq;
+}
+/*
+ * Generate the attribute sequence pdu form
+ * from sdp_list_t elements. Return length of attr seq
+ */
+static int gen_dataseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dtd)
+{
+       sdp_data_t *dataseq;
+       void **types, **values;
+       sdp_buf_t buf;
+       int i, seqlen = sdp_list_len(seq);
+
+       /* Fill up the value and the dtd arrays */
+       SDPDBG("");
+
+       SDPDBG("Seq length : %d\n", seqlen);
+
+       types = malloc(seqlen * sizeof(void *));
+       if (!types)
+               return -ENOMEM;
+
+       values = malloc(seqlen * sizeof(void *));
+       if (!values) {
+               free(types);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < seqlen; i++) {
+               void *data = seq->data;
+               types[i] = &dtd;
+               if (SDP_IS_UUID(dtd))
+                       data = &((uuid_t *)data)->value;
+               values[i] = data;
+               seq = seq->next;
+       }
+
+       dataseq = sdp_seq_alloc(types, values, seqlen);
+       if (!dataseq) {
+               free(types);
+               free(values);
+               return -ENOMEM;
+       }
+
+       memset(&buf, 0, sizeof(sdp_buf_t));
+       sdp_gen_buffer(&buf, dataseq);
+       buf.data = malloc(buf.buf_size);
+
+       if (!buf.data) {
+               sdp_data_free(dataseq);
+               free(types);
+               free(values);
+               return -ENOMEM;
+       }
+
+       SDPDBG("Data Seq : 0x%p\n", seq);
+       seqlen = sdp_gen_pdu(&buf, dataseq);
+       SDPDBG("Copying : %d\n", buf.data_size);
+       memcpy(dst, buf.data, buf.data_size);
+
+       sdp_data_free(dataseq);
+
+       free(types);
+       free(values);
+       free(buf.data);
+       return seqlen;
+}
+
+static int gen_searchseq_pdu(uint8_t *dst, const sdp_list_t *seq)
+{
+       uuid_t *uuid = seq->data;
+       return gen_dataseq_pdu(dst, seq, uuid->type);
+}
+
+static int gen_attridseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dataType)
+{
+       return gen_dataseq_pdu(dst, seq, dataType);
+}
+
+typedef struct {
+       uint8_t length;
+       unsigned char data[16];
+} __attribute__ ((packed)) sdp_cstate_t;
+
+static int copy_cstate(uint8_t *pdata, int pdata_len, const sdp_cstate_t *cstate)
+{
+       if (cstate) {
+               uint8_t len = cstate->length;
+               if (len >= pdata_len) {
+                       SDPERR("Continuation state size exceeds internal buffer");
+                       len = pdata_len - 1;
+               }
+               *pdata++ = len;
+               memcpy(pdata, cstate->data, len);
+               return len + 1;
+       }
+       *pdata = 0;
+       return 1;
+}
+
+/*
+ * This is a service search request.
+ *
+ * INPUT :
+ *
+ *   sdp_list_t *search
+ *     Singly linked list containing elements of the search
+ *     pattern. Each entry in the list is a UUID (DataTypeSDP_UUID16)
+ *     of the service to be searched
+ *
+ *   uint16_t max_rec_num
+ *      A 16 bit integer which tells the service, the maximum
+ *      entries that the client can handle in the response. The
+ *      server is obliged not to return > max_rec_num entries
+ *
+ * OUTPUT :
+ *
+ *   int return value
+ *     0:
+ *       The request completed successfully. This does not
+ *       mean the requested services were found
+ *     -1:
+ *       On any failure and sets errno
+ *
+ *   sdp_list_t **rsp_list
+ *     This variable is set on a successful return if there are
+ *     non-zero service handles. It is a singly linked list of
+ *     service record handles (uint16_t)
+ */
+int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search,
+                       uint16_t max_rec_num, sdp_list_t **rsp)
+{
+       int status = 0;
+       uint32_t reqsize = 0, _reqsize;
+       uint32_t rspsize = 0, rsplen;
+       int seqlen = 0;
+       int rec_count;
+       unsigned scanned, pdata_len;
+       uint8_t *pdata, *_pdata;
+       uint8_t *reqbuf, *rspbuf;
+       sdp_pdu_hdr_t *reqhdr, *rsphdr;
+       sdp_cstate_t *cstate = NULL;
+
+       reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+       rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+       if (!reqbuf || !rspbuf) {
+               errno = ENOMEM;
+               status = -1;
+               goto end;
+       }
+       reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+       reqhdr->pdu_id = SDP_SVC_SEARCH_REQ;
+       pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
+       reqsize = sizeof(sdp_pdu_hdr_t);
+
+       /* add service class IDs for search */
+       seqlen = gen_searchseq_pdu(pdata, search);
+
+       SDPDBG("Data seq added : %d\n", seqlen);
+
+       /* set the length and increment the pointer */
+       reqsize += seqlen;
+       pdata += seqlen;
+
+       /* specify the maximum svc rec count that client expects */
+       bt_put_unaligned(htons(max_rec_num), (uint16_t *) pdata);
+       reqsize += sizeof(uint16_t);
+       pdata += sizeof(uint16_t);
+
+       _reqsize = reqsize;
+       _pdata   = pdata;
+       *rsp = NULL;
+
+       do {
+               /* Add continuation state or NULL (first time) */
+               reqsize = _reqsize + copy_cstate(_pdata,
+                                       SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
+
+               /* Set the request header's param length */
+               reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+               reqhdr->tid  = htons(sdp_gen_tid(session));
+               /*
+                * Send the request, wait for response and if
+                * no error, set the appropriate values and return
+                */
+               status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+               if (status < 0)
+                       goto end;
+
+               if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+                       SDPERR("Unexpected end of packet");
+                       status = -1;
+                       goto end;
+               }
+
+               rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+               rsplen = ntohs(rsphdr->plen);
+
+               if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+                       SDPDBG("Status : 0x%x\n", rsphdr->pdu_id);
+                       status = -1;
+                       goto end;
+               }
+               scanned = 0;
+               pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+               pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
+
+               if (pdata_len < sizeof(uint16_t) + sizeof(uint16_t)) {
+                       SDPERR("Unexpected end of packet");
+                       status = -1;
+                       goto end;
+               }
+
+               /* net service record match count */
+               pdata += sizeof(uint16_t);
+               scanned += sizeof(uint16_t);
+               pdata_len -= sizeof(uint16_t);
+               rec_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
+               pdata += sizeof(uint16_t);
+               scanned += sizeof(uint16_t);
+               pdata_len -= sizeof(uint16_t);
+
+               SDPDBG("Current svc count: %d\n", rec_count);
+               SDPDBG("ResponseLength: %d\n", rsplen);
+
+               if (!rec_count) {
+                       status = -1;
+                       goto end;
+               }
+               extract_record_handle_seq(pdata, pdata_len, rsp, rec_count, &scanned);
+               SDPDBG("BytesScanned : %d\n", scanned);
+
+               if (rsplen > scanned) {
+                       uint8_t cstate_len;
+
+                       if (rspsize < sizeof(sdp_pdu_hdr_t) + scanned + sizeof(uint8_t)) {
+                               SDPERR("Unexpected end of packet: continuation state data missing");
+                               status = -1;
+                               goto end;
+                       }
+
+                       pdata = rspbuf + sizeof(sdp_pdu_hdr_t) + scanned;
+                       cstate_len = *(uint8_t *) pdata;
+                       if (cstate_len > 0) {
+                               cstate = (sdp_cstate_t *)pdata;
+                               SDPDBG("Cont state length: %d\n", cstate_len);
+                       } else
+                               cstate = NULL;
+               }
+       } while (cstate);
+
+end:
+       free(reqbuf);
+       free(rspbuf);
+
+       return status;
+}
+
+/*
+ * This is a service attribute request.
+ *
+ * INPUT :
+ *
+ *   uint32_t handle
+ *     The handle of the service for which the attribute(s) are
+ *     requested
+ *
+ *   sdp_attrreq_type_t reqtype
+ *     Attribute identifiers are 16 bit unsigned integers specified
+ *     in one of 2 ways described below :
+ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *        They are the actual attribute identifiers in ascending order
+ *
+ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *        The high-order 16bits is the start of range
+ *        the low-order 16bits are the end of range
+ *        0x0000 to 0xFFFF gets all attributes
+ *
+ *   sdp_list_t *attrid
+ *     Singly linked list containing attribute identifiers desired.
+ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ *   return sdp_record_t *
+ *     0:
+ *       On any error and sets errno
+ *     !0:
+ *      The service record
+ */
+sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle,
+                       sdp_attrreq_type_t reqtype, const sdp_list_t *attrids)
+{
+       uint32_t reqsize = 0, _reqsize;
+       uint32_t rspsize = 0, rsp_count;
+       int attr_list_len = 0;
+       int seqlen = 0;
+       unsigned int pdata_len;
+       uint8_t *pdata, *_pdata;
+       uint8_t *reqbuf, *rspbuf;
+       sdp_pdu_hdr_t *reqhdr, *rsphdr;
+       sdp_cstate_t *cstate = NULL;
+       uint8_t cstate_len = 0;
+       sdp_buf_t rsp_concat_buf;
+       sdp_record_t *rec = 0;
+
+       if (reqtype != SDP_ATTR_REQ_INDIVIDUAL && reqtype != SDP_ATTR_REQ_RANGE) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       memset(&rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+       reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+       rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+       if (!reqbuf || !rspbuf) {
+               errno = ENOMEM;
+               goto end;
+       }
+       reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+       reqhdr->pdu_id = SDP_SVC_ATTR_REQ;
+
+       pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
+       reqsize = sizeof(sdp_pdu_hdr_t);
+
+       /* add the service record handle */
+       bt_put_unaligned(htonl(handle), (uint32_t *) pdata);
+       reqsize += sizeof(uint32_t);
+       pdata += sizeof(uint32_t);
+
+       /* specify the response limit */
+       bt_put_unaligned(htons(65535), (uint16_t *) pdata);
+       reqsize += sizeof(uint16_t);
+       pdata += sizeof(uint16_t);
+
+       /* get attr seq PDU form */
+       seqlen = gen_attridseq_pdu(pdata, attrids,
+               reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32);
+       if (seqlen == -1) {
+               errno = EINVAL;
+               goto end;
+       }
+       pdata += seqlen;
+       reqsize += seqlen;
+       SDPDBG("Attr list length : %d\n", seqlen);
+
+       /* save before Continuation State */
+       _pdata = pdata;
+       _reqsize = reqsize;
+
+       do {
+               int status;
+
+               /* add NULL continuation state */
+               reqsize = _reqsize + copy_cstate(_pdata,
+                                       SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
+
+               /* set the request header's param length */
+               reqhdr->tid  = htons(sdp_gen_tid(session));
+               reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+               status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+               if (status < 0)
+                       goto end;
+
+               if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+                       SDPERR("Unexpected end of packet");
+                       goto end;
+               }
+
+               rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+               if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+                       SDPDBG("PDU ID : 0x%x\n", rsphdr->pdu_id);
+                       goto end;
+               }
+               pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+               pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
+
+               if (pdata_len < sizeof(uint16_t)) {
+                       SDPERR("Unexpected end of packet");
+                       goto end;
+               }
+
+               rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
+               attr_list_len += rsp_count;
+               pdata += sizeof(uint16_t);
+               pdata_len -= sizeof(uint16_t);
+
+               /*
+                * if continuation state set need to re-issue request before
+                * parsing
+                */
+               if (pdata_len < rsp_count + sizeof(uint8_t)) {
+                       SDPERR("Unexpected end of packet: continuation state data missing");
+                       goto end;
+               }
+               cstate_len = *(uint8_t *) (pdata + rsp_count);
+
+               SDPDBG("Response id : %d\n", rsphdr->pdu_id);
+               SDPDBG("Attrlist byte count : %d\n", rsp_count);
+               SDPDBG("sdp_cstate_t length : %d\n", cstate_len);
+
+               /*
+                * a split response: concatenate intermediate responses
+                * and the last one (which has cstate_len == 0)
+                */
+               if (cstate_len > 0 || rsp_concat_buf.data_size != 0) {
+                       uint8_t *targetPtr = NULL;
+
+                       cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0;
+
+                       /* build concatenated response buffer */
+                       rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count);
+                       rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count;
+                       targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size;
+                       memcpy(targetPtr, pdata, rsp_count);
+                       rsp_concat_buf.data_size += rsp_count;
+               }
+       } while (cstate);
+
+       if (attr_list_len > 0) {
+               int scanned = 0;
+               if (rsp_concat_buf.data_size != 0) {
+                       pdata = rsp_concat_buf.data;
+                       pdata_len = rsp_concat_buf.data_size;
+               }
+               rec = sdp_extract_pdu(pdata, pdata_len, &scanned);
+       }
+
+end:
+       free(reqbuf);
+       free(rsp_concat_buf.data);
+       free(rspbuf);
+       return rec;
+}
+
+/*
+ * SDP transaction structure for asynchronous search
+ */
+struct sdp_transaction {
+       sdp_callback_t *cb;     /* called when the transaction finishes */
+       void *udata;            /* client user data */
+       uint8_t *reqbuf;        /* pointer to request PDU */
+       sdp_buf_t rsp_concat_buf;
+       uint32_t reqsize;       /* without cstate */
+       int err;                /* ZERO if success or the errno if failed */
+};
+
+/*
+ * Creates a new sdp session for asynchronous search
+ * INPUT:
+ *  int sk
+ *     non-blocking L2CAP socket
+ *
+ * RETURN:
+ *  sdp_session_t *
+ *  NULL - On memory allocation failure
+ */
+sdp_session_t *sdp_create(int sk, uint32_t flags)
+{
+       sdp_session_t *session;
+       struct sdp_transaction *t;
+
+       session = malloc(sizeof(sdp_session_t));
+       if (!session) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       memset(session, 0, sizeof(*session));
+
+       session->flags = flags;
+       session->sock = sk;
+
+       t = malloc(sizeof(struct sdp_transaction));
+       if (!t) {
+               errno = ENOMEM;
+               free(session);
+               return NULL;
+       }
+       memset(t, 0, sizeof(*t));
+
+       session->priv = t;
+
+       return session;
+}
+
+/*
+ * Sets the callback function/user data used to notify the application
+ * that the asynchronous transaction finished. This function must be
+ * called before request an asynchronous search.
+ *
+ * INPUT:
+ *  sdp_session_t *session
+ *     Current sdp session to be handled
+ *  sdp_callback_t *cb
+ *      callback to be called when the transaction finishes
+ *  void *udata
+ *      user data passed to callback
+ * RETURN:
+ *      0 - Success
+ *     -1 - Failure
+ */
+int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata)
+{
+       struct sdp_transaction *t;
+
+       if (!session || !session->priv)
+               return -1;
+
+       t = session->priv;
+       t->cb = func;
+       t->udata = udata;
+
+       return 0;
+}
+
+/*
+ * This function starts an asynchronous service search request.
+ * The incoming and outgoing data are stored in the transaction structure
+ * buffers. When there is incoming data the sdp_process function must be
+ * called to get the data and handle the continuation state.
+ *
+ * INPUT :
+ *  sdp_session_t *session
+ *     Current sdp session to be handled
+ *
+ *   sdp_list_t *search
+ *     Singly linked list containing elements of the search
+ *     pattern. Each entry in the list is a UUID (DataTypeSDP_UUID16)
+ *     of the service to be searched
+ *
+ *   uint16_t max_rec_num
+ *      A 16 bit integer which tells the service, the maximum
+ *      entries that the client can handle in the response. The
+ *      server is obliged not to return > max_rec_num entries
+ *
+ * OUTPUT :
+ *
+ *   int return value
+ *     0  - if the request has been sent properly
+ *     -1 - On any failure and sets errno
+ */
+
+int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num)
+{
+       struct sdp_transaction *t;
+       sdp_pdu_hdr_t *reqhdr;
+       uint8_t *pdata;
+       int cstate_len, seqlen = 0;
+
+       if (!session || !session->priv)
+               return -1;
+
+       t = session->priv;
+
+       /* clean possible allocated buffer */
+       free(t->rsp_concat_buf.data);
+       memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+       if (!t->reqbuf) {
+               t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+               if (!t->reqbuf) {
+                       t->err = ENOMEM;
+                       goto end;
+               }
+       }
+       memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
+
+       reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
+       reqhdr->tid = htons(sdp_gen_tid(session));
+       reqhdr->pdu_id = SDP_SVC_SEARCH_REQ;
+
+       /* generate PDU */
+       pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
+       t->reqsize = sizeof(sdp_pdu_hdr_t);
+
+       /* add service class IDs for search */
+       seqlen = gen_searchseq_pdu(pdata, search);
+
+       SDPDBG("Data seq added : %d\n", seqlen);
+
+       /* now set the length and increment the pointer */
+       t->reqsize += seqlen;
+       pdata += seqlen;
+
+       bt_put_unaligned(htons(max_rec_num), (uint16_t *) pdata);
+       t->reqsize += sizeof(uint16_t);
+       pdata += sizeof(uint16_t);
+
+       /* set the request header's param length */
+       cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
+       reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
+
+       if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
+               SDPERR("Error sendind data:%s", strerror(errno));
+               t->err = errno;
+               goto end;
+       }
+
+       return 0;
+end:
+
+       free(t->reqbuf);
+       t->reqbuf = NULL;
+
+       return -1;
+}
+
+/*
+ * This function starts an asynchronous service attribute request.
+ * The incoming and outgoing data are stored in the transaction structure
+ * buffers. When there is incoming data the sdp_process function must be
+ * called to get the data and handle the continuation state.
+ *
+ * INPUT :
+ *  sdp_session_t *session
+ *     Current sdp session to be handled
+ *
+ *   uint32_t handle
+ *     The handle of the service for which the attribute(s) are
+ *     requested
+ *
+ *   sdp_attrreq_type_t reqtype
+ *     Attribute identifiers are 16 bit unsigned integers specified
+ *     in one of 2 ways described below :
+ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *        They are the actual attribute identifiers in ascending order
+ *
+ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *        The high-order 16bits is the start of range
+ *        the low-order 16bits are the end of range
+ *        0x0000 to 0xFFFF gets all attributes
+ *
+ *   sdp_list_t *attrid_list
+ *     Singly linked list containing attribute identifiers desired.
+ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ *   int return value
+ *      0 - if the request has been sent properly
+ *     -1 - On any failure and sets errno
+ */
+
+int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list)
+{
+       struct sdp_transaction *t;
+       sdp_pdu_hdr_t *reqhdr;
+       uint8_t *pdata;
+       int cstate_len, seqlen = 0;
+
+       if (!session || !session->priv)
+               return -1;
+
+       t = session->priv;
+
+       /* clean possible allocated buffer */
+       free(t->rsp_concat_buf.data);
+       memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+       if (!t->reqbuf) {
+               t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+               if (!t->reqbuf) {
+                       t->err = ENOMEM;
+                       goto end;
+               }
+       }
+       memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
+
+       reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
+       reqhdr->tid = htons(sdp_gen_tid(session));
+       reqhdr->pdu_id = SDP_SVC_ATTR_REQ;
+
+       /* generate PDU */
+       pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
+       t->reqsize = sizeof(sdp_pdu_hdr_t);
+
+       /* add the service record handle */
+       bt_put_unaligned(htonl(handle), (uint32_t *) pdata);
+       t->reqsize += sizeof(uint32_t);
+       pdata += sizeof(uint32_t);
+
+       /* specify the response limit */
+       bt_put_unaligned(htons(65535), (uint16_t *) pdata);
+       t->reqsize += sizeof(uint16_t);
+       pdata += sizeof(uint16_t);
+
+       /* get attr seq PDU form */
+       seqlen = gen_attridseq_pdu(pdata, attrid_list,
+                       reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32);
+       if (seqlen == -1) {
+               t->err = EINVAL;
+               goto end;
+       }
+
+       /* now set the length and increment the pointer */
+       t->reqsize += seqlen;
+       pdata += seqlen;
+       SDPDBG("Attr list length : %d\n", seqlen);
+
+       /* set the request header's param length */
+       cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
+       reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
+
+       if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
+               SDPERR("Error sendind data:%s", strerror(errno));
+               t->err = errno;
+               goto end;
+       }
+
+       return 0;
+end:
+
+       free(t->reqbuf);
+       t->reqbuf = NULL;
+
+       return -1;
+}
+
+/*
+ * This function starts an asynchronous service search attributes.
+ * It is a service search request combined with attribute request. The incoming
+ * and outgoing data are stored in the transaction structure buffers. When there
+ * is incoming data the sdp_process function must be called to get the data
+ * and handle the continuation state.
+ *
+ * INPUT:
+ *  sdp_session_t *session
+ *     Current sdp session to be handled
+ *
+ *   sdp_list_t *search
+ *     Singly linked list containing elements of the search
+ *     pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
+ *     of the service to be searched
+ *
+ *   AttributeSpecification attrSpec
+ *     Attribute identifiers are 16 bit unsigned integers specified
+ *     in one of 2 ways described below :
+ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *        They are the actual attribute identifiers in ascending order
+ *
+ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *        The high-order 16bits is the start of range
+ *        the low-order 16bits are the end of range
+ *        0x0000 to 0xFFFF gets all attributes
+ *
+ *   sdp_list_t *attrid_list
+ *     Singly linked list containing attribute identifiers desired.
+ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+
+ * RETURN:
+ *      0 - if the request has been sent properly
+ *     -1 - On any failure
+ */
+int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list)
+{
+       struct sdp_transaction *t;
+       sdp_pdu_hdr_t *reqhdr;
+       uint8_t *pdata;
+       int cstate_len, seqlen = 0;
+
+       if (!session || !session->priv)
+               return -1;
+
+       t = session->priv;
+
+       /* clean possible allocated buffer */
+       free(t->rsp_concat_buf.data);
+       memset(&t->rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+       if (!t->reqbuf) {
+               t->reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+               if (!t->reqbuf) {
+                       t->err = ENOMEM;
+                       goto end;
+               }
+       }
+       memset(t->reqbuf, 0, SDP_REQ_BUFFER_SIZE);
+
+       reqhdr = (sdp_pdu_hdr_t *) t->reqbuf;
+       reqhdr->tid = htons(sdp_gen_tid(session));
+       reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;
+
+       /* generate PDU */
+       pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
+       t->reqsize = sizeof(sdp_pdu_hdr_t);
+
+       /* add service class IDs for search */
+       seqlen = gen_searchseq_pdu(pdata, search);
+
+       SDPDBG("Data seq added : %d\n", seqlen);
+
+       /* now set the length and increment the pointer */
+       t->reqsize += seqlen;
+       pdata += seqlen;
+
+       bt_put_unaligned(htons(SDP_MAX_ATTR_LEN), (uint16_t *) pdata);
+       t->reqsize += sizeof(uint16_t);
+       pdata += sizeof(uint16_t);
+
+       SDPDBG("Max attr byte count : %d\n", SDP_MAX_ATTR_LEN);
+
+       /* get attr seq PDU form */
+       seqlen = gen_attridseq_pdu(pdata, attrid_list,
+                       reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32);
+       if (seqlen == -1) {
+               t->err = EINVAL;
+               goto end;
+       }
+
+       pdata += seqlen;
+       SDPDBG("Attr list length : %d\n", seqlen);
+       t->reqsize += seqlen;
+
+       /* set the request header's param length */
+       cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
+       reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
+
+       if (sdp_send_req(session, t->reqbuf, t->reqsize + cstate_len) < 0) {
+               SDPERR("Error sendind data:%s", strerror(errno));
+               t->err = errno;
+               goto end;
+       }
+
+       return 0;
+end:
+
+       free(t->reqbuf);
+       t->reqbuf = NULL;
+
+       return -1;
+}
+
+/*
+ * Function used to get the error reason after sdp_callback_t function has been called
+ * and the status is 0xffff or if sdp_service_{search, attr, search_attr}_async returns -1.
+ * It indicates that an error NOT related to SDP_ErrorResponse happened. Get errno directly
+ * is not safe because multiple transactions can be triggered.
+ * This function must be used with asynchronous sdp functions only.
+ *
+ * INPUT:
+ *  sdp_session_t *session
+ *     Current sdp session to be handled
+ * RETURN:
+ *      0 = No error in the current transaction
+ *     -1 - if the session is invalid
+ *     positive value - the errno value
+ *
+ */
+int sdp_get_error(sdp_session_t *session)
+{
+       struct sdp_transaction *t;
+
+       if (!session || !session->priv) {
+               SDPERR("Invalid session");
+               return -1;
+       }
+
+       t = session->priv;
+
+       return t->err;
+}
+
+/*
+ * Receive the incoming SDP PDU. This function must be called when there is data
+ * available to be read. On continuation state, the original request (with a new
+ * transaction ID) and the continuation state data will be appended in the initial PDU.
+ * If an error happens or the transaction finishes the callback function will be called.
+ *
+ * INPUT:
+ *  sdp_session_t *session
+ *     Current sdp session to be handled
+ * RETURN:
+ *     0  - if the transaction is on continuation state
+ *     -1 - On any failure or the transaction finished
+ */
+int sdp_process(sdp_session_t *session)
+{
+       struct sdp_transaction *t;
+       sdp_pdu_hdr_t *reqhdr, *rsphdr;
+       sdp_cstate_t *pcstate;
+       uint8_t *pdata, *rspbuf, *targetPtr;
+       int rsp_count, err = -1;
+       size_t size = 0;
+       int n, plen;
+       uint16_t status = 0xffff;
+       uint8_t pdu_id = 0x00;
+
+       if (!session || !session->priv) {
+               SDPERR("Invalid session");
+               return -1;
+       }
+
+       rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+       if (!rspbuf) {
+               SDPERR("Response buffer alloc failure:%s (%d)",
+                               strerror(errno), errno);
+               return -1;
+       }
+
+       memset(rspbuf, 0, SDP_RSP_BUFFER_SIZE);
+
+       t = session->priv;
+       reqhdr = (sdp_pdu_hdr_t *)t->reqbuf;
+       rsphdr = (sdp_pdu_hdr_t *)rspbuf;
+
+       pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+
+       n = sdp_read_rsp(session, rspbuf, SDP_RSP_BUFFER_SIZE);
+       if (n < 0) {
+               SDPERR("Read response:%s (%d)", strerror(errno), errno);
+               t->err = errno;
+               goto end;
+       }
+
+       if (n == 0 || reqhdr->tid != rsphdr->tid ||
+               (n != (int) (ntohs(rsphdr->plen) + sizeof(sdp_pdu_hdr_t)))) {
+               t->err = EPROTO;
+               SDPERR("Protocol error.");
+               goto end;
+       }
+
+       pdu_id = rsphdr->pdu_id;
+       switch (rsphdr->pdu_id) {
+       uint8_t *ssr_pdata;
+       uint16_t tsrc, csrc;
+       case SDP_SVC_SEARCH_RSP:
+               /*
+                * TSRC: Total Service Record Count (2 bytes)
+                * CSRC: Current Service Record Count (2 bytes)
+                */
+               ssr_pdata = pdata;
+               tsrc = ntohs(bt_get_unaligned((uint16_t *) ssr_pdata));
+               ssr_pdata += sizeof(uint16_t);
+               csrc = ntohs(bt_get_unaligned((uint16_t *) ssr_pdata));
+
+               /* csrc should never be larger than tsrc */
+               if (csrc > tsrc) {
+                       t->err = EPROTO;
+                       SDPERR("Protocol error: wrong current service record count value.");
+                       goto end;
+               }
+
+               SDPDBG("Total svc count: %d\n", tsrc);
+               SDPDBG("Current svc count: %d\n", csrc);
+
+               /* parameter length without continuation state */
+               plen = sizeof(tsrc) + sizeof(csrc) + csrc * 4;
+
+               if (t->rsp_concat_buf.data_size == 0) {
+                       /* first fragment */
+                       rsp_count = sizeof(tsrc) + sizeof(csrc) + csrc * 4;
+               } else {
+                       /* point to the first csrc */
+                       uint16_t *pcsrc = (uint16_t *) (t->rsp_concat_buf.data + 2);
+
+                       /* FIXME: update the interface later. csrc doesn't need be passed to clients */
+
+                       pdata += sizeof(uint16_t); /* point to csrc */
+
+                       /* the first csrc contains the sum of partial csrc responses */
+                       *pcsrc += bt_get_unaligned((uint16_t *) pdata);
+
+                       pdata += sizeof(uint16_t); /* point to the first handle */
+                       rsp_count = csrc * 4;
+               }
+               status = 0x0000;
+               break;
+       case SDP_SVC_ATTR_RSP:
+       case SDP_SVC_SEARCH_ATTR_RSP:
+               rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
+               SDPDBG("Attrlist byte count : %d\n", rsp_count);
+
+               /*
+                * Number of bytes in the AttributeLists parameter(without
+                * continuation state) + AttributeListsByteCount field size.
+                */
+               plen = sizeof(uint16_t) + rsp_count;
+
+               pdata += sizeof(uint16_t); /* points to attribute list */
+               status = 0x0000;
+               break;
+       case SDP_ERROR_RSP:
+               status = ntohs(bt_get_unaligned((uint16_t *) pdata));
+               size = ntohs(rsphdr->plen);
+
+               goto end;
+       default:
+               t->err = EPROTO;
+               SDPERR("Illegal PDU ID: 0x%x", rsphdr->pdu_id);
+               goto end;
+       }
+
+       pcstate = (sdp_cstate_t *) (pdata + rsp_count);
+
+       SDPDBG("Cstate length : %d\n", pcstate->length);
+
+       /*
+        * Check out of bound. Continuation state must have at least
+        * 1 byte: ZERO to indicate that it is not a partial response.
+        */
+       if ((n - (int) sizeof(sdp_pdu_hdr_t))  != (plen + pcstate->length + 1)) {
+               t->err = EPROTO;
+               SDPERR("Protocol error: wrong PDU size.");
+               status = 0xffff;
+               goto end;
+       }
+
+       /*
+        * This is a split response, need to concatenate intermediate
+        * responses and the last one which will have cstate length == 0
+        */
+       t->rsp_concat_buf.data = realloc(t->rsp_concat_buf.data, t->rsp_concat_buf.data_size + rsp_count);
+       targetPtr = t->rsp_concat_buf.data + t->rsp_concat_buf.data_size;
+       t->rsp_concat_buf.buf_size = t->rsp_concat_buf.data_size + rsp_count;
+       memcpy(targetPtr, pdata, rsp_count);
+       t->rsp_concat_buf.data_size += rsp_count;
+
+       if (pcstate->length > 0) {
+               int reqsize, cstate_len;
+
+               reqhdr->tid = htons(sdp_gen_tid(session));
+
+               /* add continuation state */
+               cstate_len = copy_cstate(t->reqbuf + t->reqsize,
+                               SDP_REQ_BUFFER_SIZE - t->reqsize, pcstate);
+
+               reqsize = t->reqsize + cstate_len;
+
+               /* set the request header's param length */
+               reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+
+               if (sdp_send_req(session, t->reqbuf, reqsize) < 0) {
+                       SDPERR("Error sendind data:%s(%d)", strerror(errno), errno);
+                       status = 0xffff;
+                       t->err = errno;
+                       goto end;
+               }
+               err = 0;
+       }
+
+end:
+       if (err) {
+               if (t->rsp_concat_buf.data_size != 0) {
+                       pdata = t->rsp_concat_buf.data;
+                       size = t->rsp_concat_buf.data_size;
+               }
+               if (t->cb)
+                       t->cb(pdu_id, status, pdata, size, t->udata);
+       }
+
+       free(rspbuf);
+
+       return err;
+}
+
+/*
+ * This is a service search request combined with the service
+ * attribute request. First a service class match is done and
+ * for matching service, requested attributes are extracted
+ *
+ * INPUT :
+ *
+ *   sdp_list_t *search
+ *     Singly linked list containing elements of the search
+ *     pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
+ *     of the service to be searched
+ *
+ *   AttributeSpecification attrSpec
+ *     Attribute identifiers are 16 bit unsigned integers specified
+ *     in one of 2 ways described below :
+ *     SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *        They are the actual attribute identifiers in ascending order
+ *
+ *     SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *        The high-order 16bits is the start of range
+ *        the low-order 16bits are the end of range
+ *        0x0000 to 0xFFFF gets all attributes
+ *
+ *   sdp_list_t *attrids
+ *     Singly linked list containing attribute identifiers desired.
+ *     Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *     or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ * OUTPUT :
+ *   int return value
+ *     0:
+ *       The request completed successfully. This does not
+ *       mean the requested services were found
+ *     -1:
+ *       On any error and sets errno
+ *
+ *   sdp_list_t **rsp
+ *     This variable is set on a successful return to point to
+ *     service(s) found. Each element of this list is of type
+ *     sdp_record_t* (of the services which matched the search list)
+ */
+int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrids, sdp_list_t **rsp)
+{
+       int status = 0;
+       uint32_t reqsize = 0, _reqsize;
+       uint32_t rspsize = 0;
+       int seqlen = 0, attr_list_len = 0;
+       int rsp_count = 0, cstate_len = 0;
+       unsigned int pdata_len;
+       uint8_t *pdata, *_pdata;
+       uint8_t *reqbuf, *rspbuf;
+       sdp_pdu_hdr_t *reqhdr, *rsphdr;
+       uint8_t dataType;
+       sdp_list_t *rec_list = NULL;
+       sdp_buf_t rsp_concat_buf;
+       sdp_cstate_t *cstate = NULL;
+
+       if (reqtype != SDP_ATTR_REQ_INDIVIDUAL && reqtype != SDP_ATTR_REQ_RANGE) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       memset(&rsp_concat_buf, 0, sizeof(sdp_buf_t));
+
+       reqbuf = malloc(SDP_REQ_BUFFER_SIZE);
+       rspbuf = malloc(SDP_RSP_BUFFER_SIZE);
+       if (!reqbuf || !rspbuf) {
+               errno = ENOMEM;
+               status = -1;
+               goto end;
+       }
+
+       reqhdr = (sdp_pdu_hdr_t *) reqbuf;
+       reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;
+
+       /* generate PDU */
+       pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
+       reqsize = sizeof(sdp_pdu_hdr_t);
+
+       /* add service class IDs for search */
+       seqlen = gen_searchseq_pdu(pdata, search);
+
+       SDPDBG("Data seq added : %d\n", seqlen);
+
+       /* now set the length and increment the pointer */
+       reqsize += seqlen;
+       pdata += seqlen;
+
+       bt_put_unaligned(htons(SDP_MAX_ATTR_LEN), (uint16_t *) pdata);
+       reqsize += sizeof(uint16_t);
+       pdata += sizeof(uint16_t);
+
+       SDPDBG("Max attr byte count : %d\n", SDP_MAX_ATTR_LEN);
+
+       /* get attr seq PDU form */
+       seqlen = gen_attridseq_pdu(pdata, attrids,
+               reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32);
+       if (seqlen == -1) {
+               status = EINVAL;
+               goto end;
+       }
+       pdata += seqlen;
+       SDPDBG("Attr list length : %d\n", seqlen);
+       reqsize += seqlen;
+       *rsp = 0;
+
+       /* save before Continuation State */
+       _pdata = pdata;
+       _reqsize = reqsize;
+
+       do {
+               reqhdr->tid = htons(sdp_gen_tid(session));
+
+               /* add continuation state (can be null) */
+               reqsize = _reqsize + copy_cstate(_pdata,
+                                       SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
+
+               /* set the request header's param length */
+               reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
+               rsphdr = (sdp_pdu_hdr_t *) rspbuf;
+               status = sdp_send_req_w4_rsp(session, reqbuf, rspbuf, reqsize, &rspsize);
+               if (rspsize < sizeof(sdp_pdu_hdr_t)) {
+                       SDPERR("Unexpected end of packet");
+                       status = -1;
+                       goto end;
+               }
+
+               if (status < 0) {
+                       SDPDBG("Status : 0x%x\n", rsphdr->pdu_id);
+                       goto end;
+               }
+
+               if (rsphdr->pdu_id == SDP_ERROR_RSP) {
+                       status = -1;
+                       goto end;
+               }
+
+               pdata = rspbuf + sizeof(sdp_pdu_hdr_t);
+               pdata_len = rspsize - sizeof(sdp_pdu_hdr_t);
+
+               if (pdata_len < sizeof(uint16_t)) {
+                       SDPERR("Unexpected end of packet");
+                       status = -1;
+                       goto end;
+               }
+
+               rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
+               attr_list_len += rsp_count;
+               pdata += sizeof(uint16_t); /* pdata points to attribute list */
+               pdata_len -= sizeof(uint16_t);
+
+               if (pdata_len < rsp_count + sizeof(uint8_t)) {
+                       SDPERR("Unexpected end of packet: continuation state data missing");
+                       status = -1;
+                       goto end;
+               }
+
+               cstate_len = *(uint8_t *) (pdata + rsp_count);
+
+               SDPDBG("Attrlist byte count : %d\n", attr_list_len);
+               SDPDBG("Response byte count : %d\n", rsp_count);
+               SDPDBG("Cstate length : %d\n", cstate_len);
+               /*
+                * This is a split response, need to concatenate intermediate
+                * responses and the last one which will have cstate_len == 0
+                */
+               if (cstate_len > 0 || rsp_concat_buf.data_size != 0) {
+                       uint8_t *targetPtr = NULL;
+
+                       cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0;
+
+                       /* build concatenated response buffer */
+                       rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count);
+                       targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size;
+                       rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count;
+                       memcpy(targetPtr, pdata, rsp_count);
+                       rsp_concat_buf.data_size += rsp_count;
+               }
+       } while (cstate);
+
+       if (attr_list_len > 0) {
+               int scanned = 0;
+
+               if (rsp_concat_buf.data_size != 0) {
+                       pdata = rsp_concat_buf.data;
+                       pdata_len = rsp_concat_buf.data_size;
+               }
+
+               /*
+                * Response is a sequence of sequence(s) for one or
+                * more data element sequence(s) representing services
+                * for which attributes are returned
+                */
+               scanned = sdp_extract_seqtype(pdata, pdata_len, &dataType, &seqlen);
+
+               SDPDBG("Bytes scanned : %d\n", scanned);
+               SDPDBG("Seq length : %d\n", seqlen);
+
+               if (scanned && seqlen) {
+                       pdata += scanned;
+                       pdata_len -= scanned;
+                       do {
+                               int recsize = 0;
+                               sdp_record_t *rec = sdp_extract_pdu(pdata, pdata_len, &recsize);
+                               if (rec == NULL) {
+                                       SDPERR("SVC REC is null\n");
+                                       status = -1;
+                                       goto end;
+                               }
+                               if (!recsize) {
+                                       sdp_record_free(rec);
+                                       break;
+                               }
+                               scanned += recsize;
+                               pdata += recsize;
+                               pdata_len -= recsize;
+
+                               SDPDBG("Loc seq length : %d\n", recsize);
+                               SDPDBG("Svc Rec Handle : 0x%x\n", rec->handle);
+                               SDPDBG("Bytes scanned : %d\n", scanned);
+                               SDPDBG("Attrlist byte count : %d\n", attr_list_len);
+                               rec_list = sdp_list_append(rec_list, rec);
+                       } while (scanned < attr_list_len && pdata_len > 0);
+
+                       SDPDBG("Successful scan of service attr lists\n");
+                       *rsp = rec_list;
+               }
+       }
+end:
+       free(rsp_concat_buf.data);
+       free(reqbuf);
+       free(rspbuf);
+       return status;
+}
+
+/*
+ * Find devices in the piconet.
+ */
+int sdp_general_inquiry(inquiry_info *ii, int num_dev, int duration, uint8_t *found)
+{
+       int n = hci_inquiry(-1, 10, num_dev, NULL, &ii, 0);
+       if (n < 0) {
+               SDPERR("Inquiry failed:%s", strerror(errno));
+               return -1;
+       }
+       *found = n;
+       return 0;
+}
+
+int sdp_close(sdp_session_t *session)
+{
+       struct sdp_transaction *t;
+       int ret;
+
+       if (!session)
+               return -1;
+
+       ret = close(session->sock);
+
+       t = session->priv;
+
+       if (t) {
+               free(t->reqbuf);
+
+               free(t->rsp_concat_buf.data);
+
+               free(t);
+       }
+       free(session);
+       return ret;
+}
+
+static inline int sdp_is_local(const bdaddr_t *device)
+{
+       return memcmp(device, BDADDR_LOCAL, sizeof(bdaddr_t)) == 0;
+}
+
+static int sdp_connect_local(sdp_session_t *session)
+{
+       struct sockaddr_un sa;
+
+       session->sock = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (session->sock < 0)
+               return -1;
+       session->local = 1;
+
+       sa.sun_family = AF_UNIX;
+       strcpy(sa.sun_path, SDP_UNIX_PATH);
+
+       return connect(session->sock, (struct sockaddr *) &sa, sizeof(sa));
+}
+
+static int sdp_connect_l2cap(const bdaddr_t *src,
+               const bdaddr_t *dst, sdp_session_t *session)
+{
+       uint32_t flags = session->flags;
+       struct sockaddr_l2 sa;
+       int sk;
+
+       session->sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+       if (session->sock < 0)
+               return -1;
+       session->local = 0;
+
+       sk = session->sock;
+
+       if (flags & SDP_NON_BLOCKING) {
+               long arg = fcntl(sk, F_GETFL, 0);
+               fcntl(sk, F_SETFL, arg | O_NONBLOCK);
+       }
+
+       memset(&sa, 0, sizeof(sa));
+
+       sa.l2_family = AF_BLUETOOTH;
+       sa.l2_psm = 0;
+
+       if (bacmp(src, BDADDR_ANY)) {
+               sa.l2_bdaddr = *src;
+               if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+                       return -1;
+       }
+
+       if (flags & SDP_WAIT_ON_CLOSE) {
+               struct linger l = { .l_onoff = 1, .l_linger = 1 };
+               setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
+       }
+
+       sa.l2_psm = htobs(SDP_PSM);
+       sa.l2_bdaddr = *dst;
+
+       do {
+               int ret = connect(sk, (struct sockaddr *) &sa, sizeof(sa));
+               if (!ret)
+                       return 0;
+               if (ret < 0 && (flags & SDP_NON_BLOCKING) &&
+                               (errno == EAGAIN || errno == EINPROGRESS))
+                       return 0;
+       } while (errno == EBUSY && (flags & SDP_RETRY_IF_BUSY));
+
+       return -1;
+}
+
+sdp_session_t *sdp_connect(const bdaddr_t *src,
+               const bdaddr_t *dst, uint32_t flags)
+{
+       sdp_session_t *session;
+       int err;
+
+       if ((flags & SDP_RETRY_IF_BUSY) && (flags & SDP_NON_BLOCKING)) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       session = sdp_create(-1, flags);
+       if (!session)
+               return NULL;
+
+       if (sdp_is_local(dst)) {
+               if (sdp_connect_local(session) < 0)
+                       goto fail;
+       } else {
+               if (sdp_connect_l2cap(src, dst, session) < 0)
+                       goto fail;
+       }
+
+       return session;
+
+fail:
+       err = errno;
+       if (session->sock >= 0)
+               close(session->sock);
+       free(session->priv);
+       free(session);
+       errno = err;
+
+       return NULL;
+}
+
+int sdp_get_socket(const sdp_session_t *session)
+{
+       return session->sock;
+}
+
+uint16_t sdp_gen_tid(sdp_session_t *session)
+{
+       return session->tid++;
+}
+
+/*
+ * Set the supported features
+ */
+int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf)
+{
+       const sdp_list_t *p, *r;
+       sdp_data_t *feat, *seq_feat;
+       int seqlen, i;
+       void **seqDTDs, **seqVals;
+
+       seqlen = sdp_list_len(sf);
+       seqDTDs = malloc(seqlen * sizeof(void *));
+       if (!seqDTDs)
+               return -1;
+       seqVals = malloc(seqlen * sizeof(void *));
+       if (!seqVals) {
+               free(seqDTDs);
+               return -1;
+       }
+
+       for (p = sf, i = 0; p; p = p->next, i++) {
+               int plen, j;
+               void **dtds, **vals;
+               int *lengths;
+
+               plen = sdp_list_len(p->data);
+               dtds = malloc(plen * sizeof(void *));
+               if (!dtds)
+                       goto fail;
+               vals = malloc(plen * sizeof(void *));
+               if (!vals) {
+                       free(dtds);
+                       goto fail;
+               }
+               lengths = malloc(plen * sizeof(int *));
+               if (!lengths) {
+                       free(dtds);
+                       free(vals);
+                       goto fail;
+               }
+               for (r = p->data, j = 0; r; r = r->next, j++) {
+                       sdp_data_t *data = (sdp_data_t*)r->data;
+                       dtds[j] = &data->dtd;
+                       switch (data->dtd) {
+                       case SDP_URL_STR8:
+                       case SDP_URL_STR16:
+                       case SDP_TEXT_STR8:
+                       case SDP_TEXT_STR16:
+                               vals[j] = data->val.str;
+                               lengths[j] = data->unitSize - sizeof(uint8_t);
+                               break;
+                       case SDP_ALT8:
+                       case SDP_ALT16:
+                       case SDP_ALT32:
+                       case SDP_SEQ8:
+                       case SDP_SEQ16:
+                       case SDP_SEQ32:
+                               vals[j] = data->val.dataseq;
+                               lengths[j] = 0;
+                               break;
+                       default:
+                               vals[j] = &data->val;
+                               lengths[j] = 0;
+                               break;
+                       }
+               }
+               feat = sdp_seq_alloc_with_length(dtds, vals, lengths, plen);
+               free(dtds);
+               free(vals);
+               free(lengths);
+               if (!feat)
+                       goto fail;
+               seqDTDs[i] = &feat->dtd;
+               seqVals[i] = feat;
+       }
+       seq_feat = sdp_seq_alloc(seqDTDs, seqVals, seqlen);
+       if (!seq_feat)
+               goto fail;
+       sdp_attr_replace(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST, seq_feat);
+
+       free(seqVals);
+       free(seqDTDs);
+       return 0;
+
+fail:
+       free(seqVals);
+       free(seqDTDs);
+       return -1;
+}
+
+/*
+ * Get the supported features
+ * If an error occurred -1 is returned and errno is set
+ */
+int sdp_get_supp_feat(const sdp_record_t *rec, sdp_list_t **seqp)
+{
+       sdp_data_t *sdpdata, *d;
+       sdp_list_t *tseq;
+       tseq = NULL;
+
+       sdpdata = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
+
+       if (!sdpdata || sdpdata->dtd < SDP_SEQ8 || sdpdata->dtd > SDP_SEQ32)
+               return sdp_get_uuidseq_attr(rec,
+                                       SDP_ATTR_SUPPORTED_FEATURES_LIST, seqp);
+
+       for (d = sdpdata->val.dataseq; d; d = d->next) {
+               sdp_data_t *dd;
+               sdp_list_t *subseq;
+
+               if (d->dtd < SDP_SEQ8 || d->dtd > SDP_SEQ32)
+                       goto fail;
+
+               subseq = NULL;
+
+               for (dd = d->val.dataseq; dd; dd = dd->next) {
+                       sdp_data_t *data;
+                       void *val;
+                       int length;
+
+                       switch (dd->dtd) {
+                       case SDP_URL_STR8:
+                       case SDP_URL_STR16:
+                       case SDP_TEXT_STR8:
+                       case SDP_TEXT_STR16:
+                               val = dd->val.str;
+                               length = dd->unitSize - sizeof(uint8_t);
+                               break;
+                       case SDP_UINT8:
+                       case SDP_UINT16:
+                               val = &dd->val;
+                               length = 0;
+                               break;
+                       default:
+                               goto fail;
+                       }
+
+                       data = sdp_data_alloc_with_length(dd->dtd, val, length);
+                       if (data)
+                               subseq = sdp_list_append(subseq, data);
+               }
+               tseq = sdp_list_append(tseq, subseq);
+       }
+       *seqp = tseq;
+       return 0;
+
+fail:
+       while (tseq) {
+               sdp_list_t * next;
+
+               next = tseq->next;
+               sdp_list_free(tseq, free);
+               tseq = next;
+       }
+       errno = EINVAL;
+       return -1;
+}
+
+void sdp_add_lang_attr(sdp_record_t *rec)
+{
+       sdp_lang_attr_t base_lang;
+       sdp_list_t *langs;
+
+       base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+       base_lang.encoding = 106;
+       base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+
+       langs = sdp_list_append(0, &base_lang);
+       sdp_set_lang_attr(rec, langs);
+       sdp_list_free(langs, NULL);
+}
diff --git a/lib/sdp.h b/lib/sdp.h
new file mode 100644 (file)
index 0000000..2fe74d5
--- /dev/null
+++ b/lib/sdp.h
@@ -0,0 +1,519 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __SDP_H
+#define __SDP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <bluetooth/bluetooth.h>
+
+#define SDP_UNIX_PATH "/var/run/sdp"
+#define SDP_RESPONSE_TIMEOUT   20
+#define SDP_REQ_BUFFER_SIZE    2048
+#define SDP_RSP_BUFFER_SIZE    65535
+#define SDP_PDU_CHUNK_SIZE     1024
+
+/*
+ * All definitions are based on Bluetooth Assigned Numbers
+ * of the Bluetooth Specification
+ */
+#define SDP_PSM                0x0001
+
+/*
+ * Protocol UUIDs
+ */
+#define SDP_UUID       0x0001
+#define UDP_UUID       0x0002
+#define RFCOMM_UUID    0x0003
+#define TCP_UUID       0x0004
+#define TCS_BIN_UUID   0x0005
+#define TCS_AT_UUID    0x0006
+#define ATT_UUID       0x0007
+#define OBEX_UUID      0x0008
+#define IP_UUID                0x0009
+#define FTP_UUID       0x000a
+#define HTTP_UUID      0x000c
+#define WSP_UUID       0x000e
+#define BNEP_UUID      0x000f
+#define UPNP_UUID      0x0010
+#define HIDP_UUID      0x0011
+#define HCRP_CTRL_UUID 0x0012
+#define HCRP_DATA_UUID 0x0014
+#define HCRP_NOTE_UUID 0x0016
+#define AVCTP_UUID     0x0017
+#define AVDTP_UUID     0x0019
+#define CMTP_UUID      0x001b
+#define UDI_UUID       0x001d
+#define MCAP_CTRL_UUID 0x001e
+#define MCAP_DATA_UUID 0x001f
+#define L2CAP_UUID     0x0100
+
+/*
+ * Service class identifiers of standard services and service groups
+ */
+#define SDP_SERVER_SVCLASS_ID          0x1000
+#define BROWSE_GRP_DESC_SVCLASS_ID     0x1001
+#define PUBLIC_BROWSE_GROUP            0x1002
+#define SERIAL_PORT_SVCLASS_ID         0x1101
+#define LAN_ACCESS_SVCLASS_ID          0x1102
+#define DIALUP_NET_SVCLASS_ID          0x1103
+#define IRMC_SYNC_SVCLASS_ID           0x1104
+#define OBEX_OBJPUSH_SVCLASS_ID                0x1105
+#define OBEX_FILETRANS_SVCLASS_ID      0x1106
+#define IRMC_SYNC_CMD_SVCLASS_ID       0x1107
+#define HEADSET_SVCLASS_ID             0x1108
+#define CORDLESS_TELEPHONY_SVCLASS_ID  0x1109
+#define AUDIO_SOURCE_SVCLASS_ID                0x110a
+#define AUDIO_SINK_SVCLASS_ID          0x110b
+#define AV_REMOTE_TARGET_SVCLASS_ID    0x110c
+#define ADVANCED_AUDIO_SVCLASS_ID      0x110d
+#define AV_REMOTE_SVCLASS_ID           0x110e
+#define VIDEO_CONF_SVCLASS_ID          0x110f
+#define INTERCOM_SVCLASS_ID            0x1110
+#define FAX_SVCLASS_ID                 0x1111
+#define HEADSET_AGW_SVCLASS_ID         0x1112
+#define WAP_SVCLASS_ID                 0x1113
+#define WAP_CLIENT_SVCLASS_ID          0x1114
+#define PANU_SVCLASS_ID                        0x1115
+#define NAP_SVCLASS_ID                 0x1116
+#define GN_SVCLASS_ID                  0x1117
+#define DIRECT_PRINTING_SVCLASS_ID     0x1118
+#define REFERENCE_PRINTING_SVCLASS_ID  0x1119
+#define IMAGING_SVCLASS_ID             0x111a
+#define IMAGING_RESPONDER_SVCLASS_ID   0x111b
+#define IMAGING_ARCHIVE_SVCLASS_ID     0x111c
+#define IMAGING_REFOBJS_SVCLASS_ID     0x111d
+#define HANDSFREE_SVCLASS_ID           0x111e
+#define HANDSFREE_AGW_SVCLASS_ID       0x111f
+#define DIRECT_PRT_REFOBJS_SVCLASS_ID  0x1120
+#define REFLECTED_UI_SVCLASS_ID                0x1121
+#define BASIC_PRINTING_SVCLASS_ID      0x1122
+#define PRINTING_STATUS_SVCLASS_ID     0x1123
+#define HID_SVCLASS_ID                 0x1124
+#define HCR_SVCLASS_ID                 0x1125
+#define HCR_PRINT_SVCLASS_ID           0x1126
+#define HCR_SCAN_SVCLASS_ID            0x1127
+#define CIP_SVCLASS_ID                 0x1128
+#define VIDEO_CONF_GW_SVCLASS_ID       0x1129
+#define UDI_MT_SVCLASS_ID              0x112a
+#define UDI_TA_SVCLASS_ID              0x112b
+#define AV_SVCLASS_ID                  0x112c
+#define SAP_SVCLASS_ID                 0x112d
+#define PBAP_PCE_SVCLASS_ID            0x112e
+#define PBAP_PSE_SVCLASS_ID            0x112f
+#define PBAP_SVCLASS_ID                        0x1130
+#define PNP_INFO_SVCLASS_ID            0x1200
+#define GENERIC_NETWORKING_SVCLASS_ID  0x1201
+#define GENERIC_FILETRANS_SVCLASS_ID   0x1202
+#define GENERIC_AUDIO_SVCLASS_ID       0x1203
+#define GENERIC_TELEPHONY_SVCLASS_ID   0x1204
+#define UPNP_SVCLASS_ID                        0x1205
+#define UPNP_IP_SVCLASS_ID             0x1206
+#define UPNP_PAN_SVCLASS_ID            0x1300
+#define UPNP_LAP_SVCLASS_ID            0x1301
+#define UPNP_L2CAP_SVCLASS_ID          0x1302
+#define VIDEO_SOURCE_SVCLASS_ID                0x1303
+#define VIDEO_SINK_SVCLASS_ID          0x1304
+#define VIDEO_DISTRIBUTION_SVCLASS_ID  0x1305
+#define HDP_SVCLASS_ID                 0x1400
+#define HDP_SOURCE_SVCLASS_ID          0x1401
+#define HDP_SINK_SVCLASS_ID            0x1402
+#define APPLE_AGENT_SVCLASS_ID         0x2112
+#define GENERIC_ATTRIB_SVCLASS_ID      0x1801
+
+/*
+ * Standard profile descriptor identifiers; note these
+ * may be identical to some of the service classes defined above
+ */
+#define SDP_SERVER_PROFILE_ID          SDP_SERVER_SVCLASS_ID
+#define BROWSE_GRP_DESC_PROFILE_ID     BROWSE_GRP_DESC_SVCLASS_ID
+#define SERIAL_PORT_PROFILE_ID         SERIAL_PORT_SVCLASS_ID
+#define LAN_ACCESS_PROFILE_ID          LAN_ACCESS_SVCLASS_ID
+#define DIALUP_NET_PROFILE_ID          DIALUP_NET_SVCLASS_ID
+#define IRMC_SYNC_PROFILE_ID           IRMC_SYNC_SVCLASS_ID
+#define OBEX_OBJPUSH_PROFILE_ID                OBEX_OBJPUSH_SVCLASS_ID
+#define OBEX_FILETRANS_PROFILE_ID      OBEX_FILETRANS_SVCLASS_ID
+#define IRMC_SYNC_CMD_PROFILE_ID       IRMC_SYNC_CMD_SVCLASS_ID
+#define HEADSET_PROFILE_ID             HEADSET_SVCLASS_ID
+#define CORDLESS_TELEPHONY_PROFILE_ID  CORDLESS_TELEPHONY_SVCLASS_ID
+#define AUDIO_SOURCE_PROFILE_ID                AUDIO_SOURCE_SVCLASS_ID
+#define AUDIO_SINK_PROFILE_ID          AUDIO_SINK_SVCLASS_ID
+#define AV_REMOTE_TARGET_PROFILE_ID    AV_REMOTE_TARGET_SVCLASS_ID
+#define ADVANCED_AUDIO_PROFILE_ID      ADVANCED_AUDIO_SVCLASS_ID
+#define AV_REMOTE_PROFILE_ID           AV_REMOTE_SVCLASS_ID
+#define VIDEO_CONF_PROFILE_ID          VIDEO_CONF_SVCLASS_ID
+#define INTERCOM_PROFILE_ID            INTERCOM_SVCLASS_ID
+#define FAX_PROFILE_ID                 FAX_SVCLASS_ID
+#define HEADSET_AGW_PROFILE_ID         HEADSET_AGW_SVCLASS_ID
+#define WAP_PROFILE_ID                 WAP_SVCLASS_ID
+#define WAP_CLIENT_PROFILE_ID          WAP_CLIENT_SVCLASS_ID
+#define PANU_PROFILE_ID                        PANU_SVCLASS_ID
+#define NAP_PROFILE_ID                 NAP_SVCLASS_ID
+#define GN_PROFILE_ID                  GN_SVCLASS_ID
+#define DIRECT_PRINTING_PROFILE_ID     DIRECT_PRINTING_SVCLASS_ID
+#define REFERENCE_PRINTING_PROFILE_ID  REFERENCE_PRINTING_SVCLASS_ID
+#define IMAGING_PROFILE_ID             IMAGING_SVCLASS_ID
+#define IMAGING_RESPONDER_PROFILE_ID   IMAGING_RESPONDER_SVCLASS_ID
+#define IMAGING_ARCHIVE_PROFILE_ID     IMAGING_ARCHIVE_SVCLASS_ID
+#define IMAGING_REFOBJS_PROFILE_ID     IMAGING_REFOBJS_SVCLASS_ID
+#define HANDSFREE_PROFILE_ID           HANDSFREE_SVCLASS_ID
+#define HANDSFREE_AGW_PROFILE_ID       HANDSFREE_AGW_SVCLASS_ID
+#define DIRECT_PRT_REFOBJS_PROFILE_ID  DIRECT_PRT_REFOBJS_SVCLASS_ID
+#define REFLECTED_UI_PROFILE_ID                REFLECTED_UI_SVCLASS_ID
+#define BASIC_PRINTING_PROFILE_ID      BASIC_PRINTING_SVCLASS_ID
+#define PRINTING_STATUS_PROFILE_ID     PRINTING_STATUS_SVCLASS_ID
+#define HID_PROFILE_ID                 HID_SVCLASS_ID
+#define HCR_PROFILE_ID                 HCR_SCAN_SVCLASS_ID
+#define HCR_PRINT_PROFILE_ID           HCR_PRINT_SVCLASS_ID
+#define HCR_SCAN_PROFILE_ID            HCR_SCAN_SVCLASS_ID
+#define CIP_PROFILE_ID                 CIP_SVCLASS_ID
+#define VIDEO_CONF_GW_PROFILE_ID       VIDEO_CONF_GW_SVCLASS_ID
+#define UDI_MT_PROFILE_ID              UDI_MT_SVCLASS_ID
+#define UDI_TA_PROFILE_ID              UDI_TA_SVCLASS_ID
+#define AV_PROFILE_ID                  AV_SVCLASS_ID
+#define SAP_PROFILE_ID                 SAP_SVCLASS_ID
+#define PBAP_PCE_PROFILE_ID            PBAP_PCE_SVCLASS_ID
+#define PBAP_PSE_PROFILE_ID            PBAP_PSE_SVCLASS_ID
+#define PBAP_PROFILE_ID                        PBAP_SVCLASS_ID
+#define PNP_INFO_PROFILE_ID            PNP_INFO_SVCLASS_ID
+#define GENERIC_NETWORKING_PROFILE_ID  GENERIC_NETWORKING_SVCLASS_ID
+#define GENERIC_FILETRANS_PROFILE_ID   GENERIC_FILETRANS_SVCLASS_ID
+#define GENERIC_AUDIO_PROFILE_ID       GENERIC_AUDIO_SVCLASS_ID
+#define GENERIC_TELEPHONY_PROFILE_ID   GENERIC_TELEPHONY_SVCLASS_ID
+#define UPNP_PROFILE_ID                        UPNP_SVCLASS_ID
+#define UPNP_IP_PROFILE_ID             UPNP_IP_SVCLASS_ID
+#define UPNP_PAN_PROFILE_ID            UPNP_PAN_SVCLASS_ID
+#define UPNP_LAP_PROFILE_ID            UPNP_LAP_SVCLASS_ID
+#define UPNP_L2CAP_PROFILE_ID          UPNP_L2CAP_SVCLASS_ID
+#define VIDEO_SOURCE_PROFILE_ID                VIDEO_SOURCE_SVCLASS_ID
+#define VIDEO_SINK_PROFILE_ID          VIDEO_SINK_SVCLASS_ID
+#define VIDEO_DISTRIBUTION_PROFILE_ID  VIDEO_DISTRIBUTION_SVCLASS_ID
+#define HDP_PROFILE_ID                 HDP_SVCLASS_ID
+#define HDP_SOURCE_PROFILE_ID          HDP_SOURCE_SVCLASS_ID
+#define HDP_SINK_PROFILE_ID            HDP_SINK_SVCLASS_ID
+#define APPLE_AGENT_PROFILE_ID         APPLE_AGENT_SVCLASS_ID
+#define GENERIC_ACCESS_PROFILE_ID      0x1800
+#define GENERIC_ATTRIB_PROFILE_ID      GENERIC_ATTRIB_SVCLASS_ID
+
+/*
+ * Compatibility macros for the old MDP acronym
+ */
+#define MDP_SVCLASS_ID                 HDP_SVCLASS_ID
+#define MDP_SOURCE_SVCLASS_ID          HDP_SOURCE_SVCLASS_ID
+#define MDP_SINK_SVCLASS_ID            HDP_SINK_SVCLASS_ID
+#define MDP_PROFILE_ID                 HDP_PROFILE_ID
+#define MDP_SOURCE_PROFILE_ID          HDP_SOURCE_PROFILE_ID
+#define MDP_SINK_PROFILE_ID            HDP_SINK_PROFILE_ID
+
+/*
+ * Attribute identifier codes
+ */
+#define SDP_SERVER_RECORD_HANDLE               0x0000
+
+/*
+ * Possible values for attribute-id are listed below.
+ * See SDP Spec, section "Service Attribute Definitions" for more details.
+ */
+#define SDP_ATTR_RECORD_HANDLE                 0x0000
+#define SDP_ATTR_SVCLASS_ID_LIST               0x0001
+#define SDP_ATTR_RECORD_STATE                  0x0002
+#define SDP_ATTR_SERVICE_ID                    0x0003
+#define SDP_ATTR_PROTO_DESC_LIST               0x0004
+#define SDP_ATTR_BROWSE_GRP_LIST               0x0005
+#define SDP_ATTR_LANG_BASE_ATTR_ID_LIST                0x0006
+#define SDP_ATTR_SVCINFO_TTL                   0x0007
+#define SDP_ATTR_SERVICE_AVAILABILITY          0x0008
+#define SDP_ATTR_PFILE_DESC_LIST               0x0009
+#define SDP_ATTR_DOC_URL                       0x000a
+#define SDP_ATTR_CLNT_EXEC_URL                 0x000b
+#define SDP_ATTR_ICON_URL                      0x000c
+#define SDP_ATTR_ADD_PROTO_DESC_LIST           0x000d
+
+#define SDP_ATTR_GROUP_ID                      0x0200
+#define SDP_ATTR_IP_SUBNET                     0x0200
+#define SDP_ATTR_VERSION_NUM_LIST              0x0200
+#define SDP_ATTR_SUPPORTED_FEATURES_LIST       0x0200
+#define SDP_ATTR_SVCDB_STATE                   0x0201
+
+#define SDP_ATTR_SERVICE_VERSION               0x0300
+#define SDP_ATTR_EXTERNAL_NETWORK              0x0301
+#define SDP_ATTR_SUPPORTED_DATA_STORES_LIST    0x0301
+#define SDP_ATTR_DATA_EXCHANGE_SPEC            0x0301
+#define SDP_ATTR_FAX_CLASS1_SUPPORT            0x0302
+#define SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL   0x0302
+#define SDP_ATTR_MCAP_SUPPORTED_PROCEDURES     0x0302
+#define SDP_ATTR_FAX_CLASS20_SUPPORT           0x0303
+#define SDP_ATTR_SUPPORTED_FORMATS_LIST                0x0303
+#define SDP_ATTR_FAX_CLASS2_SUPPORT            0x0304
+#define SDP_ATTR_AUDIO_FEEDBACK_SUPPORT                0x0305
+#define SDP_ATTR_NETWORK_ADDRESS               0x0306
+#define SDP_ATTR_WAP_GATEWAY                   0x0307
+#define SDP_ATTR_HOMEPAGE_URL                  0x0308
+#define SDP_ATTR_WAP_STACK_TYPE                        0x0309
+#define SDP_ATTR_SECURITY_DESC                 0x030a
+#define SDP_ATTR_NET_ACCESS_TYPE               0x030b
+#define SDP_ATTR_MAX_NET_ACCESSRATE            0x030c
+#define SDP_ATTR_IP4_SUBNET                    0x030d
+#define SDP_ATTR_IP6_SUBNET                    0x030e
+#define SDP_ATTR_SUPPORTED_CAPABILITIES                0x0310
+#define SDP_ATTR_SUPPORTED_FEATURES            0x0311
+#define SDP_ATTR_SUPPORTED_FUNCTIONS           0x0312
+#define SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY   0x0313
+#define SDP_ATTR_SUPPORTED_REPOSITORIES                0x0314
+
+#define SDP_ATTR_SPECIFICATION_ID              0x0200
+#define SDP_ATTR_VENDOR_ID                     0x0201
+#define SDP_ATTR_PRODUCT_ID                    0x0202
+#define SDP_ATTR_VERSION                       0x0203
+#define SDP_ATTR_PRIMARY_RECORD                        0x0204
+#define SDP_ATTR_VENDOR_ID_SOURCE              0x0205
+
+#define SDP_ATTR_HID_DEVICE_RELEASE_NUMBER     0x0200
+#define SDP_ATTR_HID_PARSER_VERSION            0x0201
+#define SDP_ATTR_HID_DEVICE_SUBCLASS           0x0202
+#define SDP_ATTR_HID_COUNTRY_CODE              0x0203
+#define SDP_ATTR_HID_VIRTUAL_CABLE             0x0204
+#define SDP_ATTR_HID_RECONNECT_INITIATE                0x0205
+#define SDP_ATTR_HID_DESCRIPTOR_LIST           0x0206
+#define SDP_ATTR_HID_LANG_ID_BASE_LIST         0x0207
+#define SDP_ATTR_HID_SDP_DISABLE               0x0208
+#define SDP_ATTR_HID_BATTERY_POWER             0x0209
+#define SDP_ATTR_HID_REMOTE_WAKEUP             0x020a
+#define SDP_ATTR_HID_PROFILE_VERSION           0x020b
+#define SDP_ATTR_HID_SUPERVISION_TIMEOUT       0x020c
+#define SDP_ATTR_HID_NORMALLY_CONNECTABLE      0x020d
+#define SDP_ATTR_HID_BOOT_DEVICE               0x020e
+
+/*
+ * These identifiers are based on the SDP spec stating that
+ * "base attribute id of the primary (universal) language must be 0x0100"
+ *
+ * Other languages should have their own offset; e.g.:
+ * #define XXXLangBase yyyy
+ * #define AttrServiceName_XXX 0x0000+XXXLangBase
+ */
+#define SDP_PRIMARY_LANG_BASE          0x0100
+
+#define SDP_ATTR_SVCNAME_PRIMARY       0x0000 + SDP_PRIMARY_LANG_BASE
+#define SDP_ATTR_SVCDESC_PRIMARY       0x0001 + SDP_PRIMARY_LANG_BASE
+#define SDP_ATTR_PROVNAME_PRIMARY      0x0002 + SDP_PRIMARY_LANG_BASE
+
+/*
+ * The Data representation in SDP PDUs (pps 339, 340 of BT SDP Spec)
+ * These are the exact data type+size descriptor values
+ * that go into the PDU buffer.
+ *
+ * The datatype (leading 5bits) + size descriptor (last 3 bits)
+ * is 8 bits. The size descriptor is critical to extract the
+ * right number of bytes for the data value from the PDU.
+ *
+ * For most basic types, the datatype+size descriptor is
+ * straightforward. However for constructed types and strings,
+ * the size of the data is in the next "n" bytes following the
+ * 8 bits (datatype+size) descriptor. Exactly what the "n" is
+ * specified in the 3 bits of the data size descriptor.
+ *
+ * TextString and URLString can be of size 2^{8, 16, 32} bytes
+ * DataSequence and DataSequenceAlternates can be of size 2^{8, 16, 32}
+ * The size are computed post-facto in the API and are not known apriori
+ */
+#define SDP_DATA_NIL           0x00
+#define SDP_UINT8              0x08
+#define SDP_UINT16             0x09
+#define SDP_UINT32             0x0A
+#define SDP_UINT64             0x0B
+#define SDP_UINT128            0x0C
+#define SDP_INT8               0x10
+#define SDP_INT16              0x11
+#define SDP_INT32              0x12
+#define SDP_INT64              0x13
+#define SDP_INT128             0x14
+#define SDP_UUID_UNSPEC                0x18
+#define SDP_UUID16             0x19
+#define SDP_UUID32             0x1A
+#define SDP_UUID128            0x1C
+#define SDP_TEXT_STR_UNSPEC    0x20
+#define SDP_TEXT_STR8          0x25
+#define SDP_TEXT_STR16         0x26
+#define SDP_TEXT_STR32         0x27
+#define SDP_BOOL               0x28
+#define SDP_SEQ_UNSPEC         0x30
+#define SDP_SEQ8               0x35
+#define SDP_SEQ16              0x36
+#define SDP_SEQ32              0x37
+#define SDP_ALT_UNSPEC         0x38
+#define SDP_ALT8               0x3D
+#define SDP_ALT16              0x3E
+#define SDP_ALT32              0x3F
+#define SDP_URL_STR_UNSPEC     0x40
+#define SDP_URL_STR8           0x45
+#define SDP_URL_STR16          0x46
+#define SDP_URL_STR32          0x47
+
+/*
+ * The PDU identifiers of SDP packets between client and server
+ */
+#define SDP_ERROR_RSP          0x01
+#define SDP_SVC_SEARCH_REQ     0x02
+#define SDP_SVC_SEARCH_RSP     0x03
+#define SDP_SVC_ATTR_REQ       0x04
+#define SDP_SVC_ATTR_RSP       0x05
+#define SDP_SVC_SEARCH_ATTR_REQ        0x06
+#define SDP_SVC_SEARCH_ATTR_RSP        0x07
+
+/*
+ * Some additions to support service registration.
+ * These are outside the scope of the Bluetooth specification
+ */
+#define SDP_SVC_REGISTER_REQ   0x75
+#define SDP_SVC_REGISTER_RSP   0x76
+#define SDP_SVC_UPDATE_REQ     0x77
+#define SDP_SVC_UPDATE_RSP     0x78
+#define SDP_SVC_REMOVE_REQ     0x79
+#define SDP_SVC_REMOVE_RSP     0x80
+
+/*
+ * SDP Error codes
+ */
+#define SDP_INVALID_VERSION            0x0001
+#define SDP_INVALID_RECORD_HANDLE      0x0002
+#define SDP_INVALID_SYNTAX             0x0003
+#define SDP_INVALID_PDU_SIZE           0x0004
+#define SDP_INVALID_CSTATE             0x0005
+
+/*
+ * SDP PDU
+ */
+typedef struct {
+       uint8_t  pdu_id;
+       uint16_t tid;
+       uint16_t plen;
+} __attribute__ ((packed)) sdp_pdu_hdr_t;
+
+/*
+ * Common definitions for attributes in the SDP.
+ * Should the type of any of these change, you need only make a change here.
+ */
+
+typedef struct {
+       uint8_t type;
+       union {
+               uint16_t  uuid16;
+               uint32_t  uuid32;
+               uint128_t uuid128;
+       } value;
+} uuid_t;
+
+#define SDP_IS_UUID(x) ((x) == SDP_UUID16 || (x) == SDP_UUID32 || (x) ==SDP_UUID128)
+#define SDP_IS_SEQ(x)  ((x) == SDP_SEQ8 || (x) == SDP_SEQ16 || (x) == SDP_SEQ32)
+
+typedef struct _sdp_list sdp_list_t;
+struct _sdp_list {
+       sdp_list_t *next;
+       void *data;
+};
+
+/*
+ * User-visible strings can be in many languages
+ * in addition to the universal language.
+ *
+ * Language meta-data includes language code in ISO639
+ * followed by the encoding format. The third field in this
+ * structure is the attribute offset for the language.
+ * User-visible strings in the specified language can be
+ * obtained at this offset.
+ */
+typedef struct {
+       uint16_t code_ISO639;
+       uint16_t encoding;
+       uint16_t base_offset;
+} sdp_lang_attr_t;
+
+/*
+ * Profile descriptor is the Bluetooth profile metadata. If a
+ * service conforms to a well-known profile, then its profile
+ * identifier (UUID) is an attribute of the service. In addition,
+ * if the profile has a version number it is specified here.
+ */
+typedef struct {
+       uuid_t uuid;
+       uint16_t version;
+} sdp_profile_desc_t;
+
+typedef struct {
+       uint8_t major;
+       uint8_t minor;
+} sdp_version_t;
+
+typedef struct {
+       uint8_t *data;
+       uint32_t data_size;
+       uint32_t buf_size;
+} sdp_buf_t;
+
+typedef struct {
+       uint32_t handle;
+
+       /* Search pattern: a sequence of all UUIDs seen in this record */
+       sdp_list_t *pattern;
+       sdp_list_t *attrlist;
+
+       /* Main service class for Extended Inquiry Response */
+       uuid_t svclass;
+} sdp_record_t;
+
+typedef struct sdp_data_struct sdp_data_t;
+struct sdp_data_struct {
+       uint8_t dtd;
+       uint16_t attrId;
+       union {
+               int8_t    int8;
+               int16_t   int16;
+               int32_t   int32;
+               int64_t   int64;
+               uint128_t int128;
+               uint8_t   uint8;
+               uint16_t  uint16;
+               uint32_t  uint32;
+               uint64_t  uint64;
+               uint128_t uint128;
+               uuid_t    uuid;
+               char     *str;
+               sdp_data_t *dataseq;
+       } val;
+       sdp_data_t *next;
+       int unitSize;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SDP_H */
diff --git a/lib/sdp_lib.h b/lib/sdp_lib.h
new file mode 100644 (file)
index 0000000..6e1eb91
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __SDP_LIB_H
+#define __SDP_LIB_H
+
+#include <sys/socket.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * SDP lists
+ */
+typedef void(*sdp_list_func_t)(void *, void *);
+typedef void(*sdp_free_func_t)(void *);
+typedef int (*sdp_comp_func_t)(const void *, const void *);
+
+sdp_list_t *sdp_list_append(sdp_list_t *list, void *d);
+sdp_list_t *sdp_list_remove(sdp_list_t *list, void *d);
+sdp_list_t *sdp_list_insert_sorted(sdp_list_t *list, void *data, sdp_comp_func_t f);
+void        sdp_list_free(sdp_list_t *list, sdp_free_func_t f);
+
+static inline int sdp_list_len(const sdp_list_t *list)
+{
+       int n = 0;
+       for (; list; list = list->next)
+               n++;
+       return n;
+}
+
+static inline sdp_list_t *sdp_list_find(sdp_list_t *list, void *u, sdp_comp_func_t f)
+{
+       for (; list; list = list->next)
+               if (f(list->data, u) == 0)
+                       return list;
+       return NULL;
+}
+
+static inline void sdp_list_foreach(sdp_list_t *list, sdp_list_func_t f, void *u)
+{
+       for (; list; list = list->next)
+               f(list->data, u);
+}
+
+/*
+ * Values of the flags parameter to sdp_record_register
+ */
+#define SDP_RECORD_PERSIST     0x01
+#define SDP_DEVICE_RECORD      0x02
+
+/*
+ * Values of the flags parameter to sdp_connect
+ */
+#define SDP_RETRY_IF_BUSY      0x01
+#define SDP_WAIT_ON_CLOSE      0x02
+#define SDP_NON_BLOCKING       0x04
+
+/*
+ * a session with an SDP server
+ */
+typedef struct {
+       int sock;
+       int state;
+       int local;
+       int flags;
+       uint16_t tid;   /* Current transaction ID */
+       void *priv;
+} sdp_session_t;
+
+typedef enum {
+       /*
+        *  Attributes are specified as individual elements
+        */
+       SDP_ATTR_REQ_INDIVIDUAL = 1,
+       /*
+        *  Attributes are specified as a range
+        */
+       SDP_ATTR_REQ_RANGE
+} sdp_attrreq_type_t;
+
+/*
+ *     When the pdu_id(type) is a sdp error response, check the status value
+ *     to figure out the error reason. For status values 0x0001-0x0006 check
+ *     Bluetooth SPEC. If the status is 0xffff, call sdp_get_error function
+ *     to get the real reason:
+ *         - wrong transaction ID(EPROTO)
+ *         - wrong PDU id or(EPROTO)
+ *         - I/O error
+ */
+typedef void sdp_callback_t(uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *udata);
+
+/*
+ * create an L2CAP connection to a Bluetooth device
+ *
+ * INPUT:
+ *
+ *  bdaddr_t *src:
+ *     Address of the local device to use to make the connection
+ *     (or BDADDR_ANY)
+ *
+ *  bdaddr_t *dst:
+ *    Address of the SDP server device
+ */
+sdp_session_t *sdp_connect(const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags);
+int sdp_close(sdp_session_t *session);
+int sdp_get_socket(const sdp_session_t *session);
+
+/*
+ * SDP transaction: functions for asynchronous search.
+ */
+sdp_session_t *sdp_create(int sk, uint32_t flags);
+int sdp_get_error(sdp_session_t *session);
+int sdp_process(sdp_session_t *session);
+int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata);
+
+int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num);
+int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list);
+int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list);
+
+uint16_t sdp_gen_tid(sdp_session_t *session);
+
+/*
+ * find all devices in the piconet
+ */
+int sdp_general_inquiry(inquiry_info *ii, int dev_num, int duration, uint8_t *found);
+
+/* flexible extraction of basic attributes - Jean II */
+int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attr, int *value);
+int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attr, char *value, int valuelen);
+
+/*
+ * Basic sdp data functions
+ */
+sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value);
+sdp_data_t *sdp_data_alloc_with_length(uint8_t dtd, const void *value, uint32_t length);
+void sdp_data_free(sdp_data_t *data);
+sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attr_id);
+
+sdp_data_t *sdp_seq_alloc(void **dtds, void **values, int len);
+sdp_data_t *sdp_seq_alloc_with_length(void **dtds, void **values, int *length, int len);
+sdp_data_t *sdp_seq_append(sdp_data_t *seq, sdp_data_t *data);
+
+int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *data);
+void sdp_attr_remove(sdp_record_t *rec, uint16_t attr);
+void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *data);
+int sdp_set_uuidseq_attr(sdp_record_t *rec, uint16_t attr, sdp_list_t *seq);
+int sdp_get_uuidseq_attr(const sdp_record_t *rec, uint16_t attr, sdp_list_t **seqp);
+
+/*
+ * NOTE that none of the functions below will update the SDP server,
+ * unless the {register, update}sdp_record_t() function is invoked.
+ * All functions which return an integer value, return 0 on success
+ * or -1 on failure.
+ */
+
+/*
+ * Create an attribute and add it to the service record's attribute list.
+ * This consists of the data type descriptor of the attribute,
+ * the value of the attribute and the attribute identifier.
+ */
+int sdp_attr_add_new(sdp_record_t *rec, uint16_t attr, uint8_t dtd, const void *p);
+
+/*
+ * Set the information attributes of the service record.
+ * The set of attributes comprises service name, description
+ * and provider name
+ */
+void sdp_set_info_attr(sdp_record_t *rec, const char *name, const char *prov, const char *desc);
+
+/*
+ * Set the ServiceClassID attribute to the sequence specified by seq.
+ * Note that the identifiers need to be in sorted order from the most
+ * specific to the most generic service class that this service
+ * conforms to.
+ */
+static inline int sdp_set_service_classes(sdp_record_t *rec, sdp_list_t *seq)
+{
+       return sdp_set_uuidseq_attr(rec, SDP_ATTR_SVCLASS_ID_LIST, seq);
+}
+
+/*
+ * Get the service classes to which the service conforms.
+ *
+ * When set, the list contains elements of ServiceClassIdentifer(uint16_t)
+ * ordered from most specific to most generic
+ */
+static inline int sdp_get_service_classes(const sdp_record_t *rec, sdp_list_t **seqp)
+{
+       return sdp_get_uuidseq_attr(rec, SDP_ATTR_SVCLASS_ID_LIST, seqp);
+}
+
+/*
+ * Set the BrowseGroupList attribute to the list specified by seq.
+ *
+ * A service can belong to one or more service groups
+ * and the list comprises such group identifiers (UUIDs)
+ */
+static inline int sdp_set_browse_groups(sdp_record_t *rec, sdp_list_t *seq)
+{
+       return sdp_set_uuidseq_attr(rec, SDP_ATTR_BROWSE_GRP_LIST, seq);
+}
+
+/*
+ * Set the access protocols of the record to those specified in proto
+ */
+int sdp_set_access_protos(sdp_record_t *rec, const sdp_list_t *proto);
+
+/*
+ * Set the additional access protocols of the record to those specified in proto
+ */
+int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *proto);
+
+/*
+ * Get protocol port (i.e. PSM for L2CAP, Channel for RFCOMM)
+ */
+int sdp_get_proto_port(const sdp_list_t *list, int proto);
+
+/*
+ * Get protocol descriptor.
+ */
+sdp_data_t *sdp_get_proto_desc(sdp_list_t *list, int proto);
+
+/*
+ * Set the LanguageBase attributes to the values specified in list
+ * (a linked list of sdp_lang_attr_t objects, one for each language in
+ * which user-visible attributes are present).
+ */
+int sdp_set_lang_attr(sdp_record_t *rec, const sdp_list_t *list);
+
+/*
+ * Set the ServiceInfoTimeToLive attribute of the service.
+ * This is the number of seconds that this record is guaranteed
+ * not to change after being obtained by a client.
+ */
+static inline int sdp_set_service_ttl(sdp_record_t *rec, uint32_t ttl)
+{
+       return sdp_attr_add_new(rec, SDP_ATTR_SVCINFO_TTL, SDP_UINT32, &ttl);
+}
+
+/*
+ * Set the ServiceRecordState attribute of a service. This is
+ * guaranteed to change if there is any kind of modification to
+ * the record.
+ */
+static inline int sdp_set_record_state(sdp_record_t *rec, uint32_t state)
+{
+       return sdp_attr_add_new(rec, SDP_ATTR_RECORD_STATE, SDP_UINT32, &state);
+}
+
+/*
+ * Set the ServiceID attribute of a service.
+ */
+void sdp_set_service_id(sdp_record_t *rec, uuid_t uuid);
+
+/*
+ * Set the GroupID attribute of a service
+ */
+void sdp_set_group_id(sdp_record_t *rec, uuid_t grouuuid);
+
+/*
+ * Set the ServiceAvailability attribute of a service.
+ *
+ * Note that this represents the relative availability
+ * of the service: 0x00 means completely unavailable;
+ * 0xFF means maximum availability.
+ */
+static inline int sdp_set_service_avail(sdp_record_t *rec, uint8_t avail)
+{
+       return sdp_attr_add_new(rec, SDP_ATTR_SERVICE_AVAILABILITY, SDP_UINT8, &avail);
+}
+
+/*
+ * Set the profile descriptor list attribute of a record.
+ *
+ * Each element in the list is an object of type
+ * sdp_profile_desc_t which is a definition of the
+ * Bluetooth profile that this service conforms to.
+ */
+int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *desc);
+
+/*
+ * Set URL attributes of a record.
+ *
+ * ClientExecutableURL: a URL to a client's platform specific (WinCE,
+ * PalmOS) executable code that can be used to access this service.
+ *
+ * DocumentationURL: a URL pointing to service documentation
+ *
+ * IconURL: a URL to an icon that can be used to represent this service.
+ *
+ * Note: pass NULL for any URLs that you don't want to set or remove
+ */
+void sdp_set_url_attr(sdp_record_t *rec, const char *clientExecURL, const char *docURL, const char *iconURL);
+
+/*
+ * a service search request.
+ *
+ *  INPUT :
+ *
+ *    sdp_list_t *search
+ *      list containing elements of the search
+ *      pattern. Each entry in the list is a UUID
+ *      of the service to be searched
+ *
+ *    uint16_t max_rec_num
+ *       An integer specifying the maximum number of
+ *       entries that the client can handle in the response.
+ *
+ *  OUTPUT :
+ *
+ *    int return value
+ *      0
+ *        The request completed successfully. This does not
+ *        mean the requested services were found
+ *      -1
+ *        The request completed unsuccessfully
+ *
+ *    sdp_list_t *rsp_list
+ *      This variable is set on a successful return if there are
+ *      non-zero service handles. It is a singly linked list of
+ *      service record handles (uint16_t)
+ */
+int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num, sdp_list_t **rsp_list);
+
+/*
+ *  a service attribute request.
+ *
+ *  INPUT :
+ *
+ *    uint32_t handle
+ *      The handle of the service for which the attribute(s) are
+ *      requested
+ *
+ *    sdp_attrreq_type_t reqtype
+ *      Attribute identifiers are 16 bit unsigned integers specified
+ *      in one of 2 ways described below :
+ *      SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *         They are the actual attribute identifiers in ascending order
+ *
+ *      SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *         The high-order 16bits is the start of range
+ *         the low-order 16bits are the end of range
+ *         0x0000 to 0xFFFF gets all attributes
+ *
+ *    sdp_list_t *attrid_list
+ *      Singly linked list containing attribute identifiers desired.
+ *      Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *      or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ *  OUTPUT :
+ *    int return value
+ *      0
+ *        The request completed successfully. This does not
+ *        mean the requested services were found
+ *      -1
+ *        The request completed unsuccessfully due to a timeout
+ */
+sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list);
+
+/*
+ *  This is a service search request combined with the service
+ *  attribute request. First a service class match is done and
+ *  for matching service, requested attributes are extracted
+ *
+ *  INPUT :
+ *
+ *    sdp_list_t *search
+ *      Singly linked list containing elements of the search
+ *      pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16)
+ *      of the service to be searched
+ *
+ *    AttributeSpecification attrSpec
+ *      Attribute identifiers are 16 bit unsigned integers specified
+ *      in one of 2 ways described below :
+ *      SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers
+ *         They are the actual attribute identifiers in ascending order
+ *
+ *      SDP_ATTR_REQ_RANGE - 32bit identifier range
+ *         The high-order 16bits is the start of range
+ *         the low-order 16bits are the end of range
+ *         0x0000 to 0xFFFF gets all attributes
+ *
+ *    sdp_list_t *attrid_list
+ *      Singly linked list containing attribute identifiers desired.
+ *      Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL)
+ *      or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE)
+ *
+ *  OUTPUT :
+ *    int return value
+ *      0
+ *        The request completed successfully. This does not
+ *        mean the requested services were found
+ *      -1
+ *        The request completed unsuccessfully due to a timeout
+ *
+ *    sdp_list_t *rsp_list
+ *      This variable is set on a successful return to point to
+ *      service(s) found. Each element of this list is of type
+ *      sdp_record_t *.
+ */
+int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list, sdp_list_t **rsp_list);
+
+/*
+ * Allocate/free a service record and its attributes
+ */
+sdp_record_t *sdp_record_alloc(void);
+void sdp_record_free(sdp_record_t *rec);
+
+/*
+ * Register a service record.
+ *
+ * Note: It is the responsbility of the Service Provider to create the
+ * record first and set its attributes using setXXX() methods.
+ *
+ * The service provider must then call sdp_record_register() to make
+ * the service record visible to SDP clients.  This function returns 0
+ * on success or -1 on failure (and sets errno).
+ */
+int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle);
+int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags);
+int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags);
+
+/*
+ * Unregister a service record.
+ */
+int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle);
+int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec);
+int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec);
+
+/*
+ * Update an existing service record.  (Calling this function
+ * before a previous call to sdp_record_register() will result
+ * in an error.)
+ */
+int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size);
+int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec);
+int sdp_record_update(sdp_session_t *sess, const sdp_record_t *rec);
+
+void sdp_record_print(const sdp_record_t *rec);
+
+/*
+ * UUID functions
+ */
+uuid_t *sdp_uuid16_create(uuid_t *uuid, uint16_t data);
+uuid_t *sdp_uuid32_create(uuid_t *uuid, uint32_t data);
+uuid_t *sdp_uuid128_create(uuid_t *uuid, const void *data);
+int sdp_uuid16_cmp(const void *p1, const void *p2);
+int sdp_uuid128_cmp(const void *p1, const void *p2);
+int sdp_uuid_cmp(const void *p1, const void *p2);
+uuid_t *sdp_uuid_to_uuid128(const uuid_t *uuid);
+void sdp_uuid16_to_uuid128(uuid_t *uuid128, const uuid_t *uuid16);
+void sdp_uuid32_to_uuid128(uuid_t *uuid128, const uuid_t *uuid32);
+int sdp_uuid128_to_uuid(uuid_t *uuid);
+int sdp_uuid_to_proto(uuid_t *uuid);
+int sdp_uuid_extract(const uint8_t *buffer, int bufsize, uuid_t *uuid, int *scanned);
+void sdp_uuid_print(const uuid_t *uuid);
+
+#define MAX_LEN_UUID_STR 37
+#define MAX_LEN_PROTOCOL_UUID_STR 8
+#define MAX_LEN_SERVICECLASS_UUID_STR 28
+#define MAX_LEN_PROFILEDESCRIPTOR_UUID_STR 28
+
+int sdp_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+int sdp_proto_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+int sdp_svclass_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+int sdp_profile_uuid2strn(const uuid_t *uuid, char *str, size_t n);
+
+/*
+ * In all the sdp_get_XXX(handle, XXX *xxx) functions below,
+ * the XXX * is set to point to the value, should it exist
+ * and 0 is returned. If the value does not exist, -1 is
+ * returned and errno set to ENODATA.
+ *
+ * In all the methods below, the memory management rules are
+ * simple. Don't free anything! The pointer returned, in the
+ * case of constructed types, is a pointer to the contents
+ * of the sdp_record_t.
+ */
+
+/*
+ * Get the access protocols from the service record
+ */
+int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **protos);
+
+/*
+ * Get the additional access protocols from the service record
+ */
+int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **protos);
+
+/*
+ * Extract the list of browse groups to which the service belongs.
+ * When set, seqp contains elements of GroupID (uint16_t)
+ */
+static inline int sdp_get_browse_groups(const sdp_record_t *rec, sdp_list_t **seqp)
+{
+       return sdp_get_uuidseq_attr(rec, SDP_ATTR_BROWSE_GRP_LIST, seqp);
+}
+
+/*
+ * Extract language attribute meta-data of the service record.
+ * For each language in the service record, LangSeq has a struct of type
+ * sdp_lang_attr_t.
+ */
+int sdp_get_lang_attr(const sdp_record_t *rec, sdp_list_t **langSeq);
+
+/*
+ * Extract the Bluetooth profile descriptor sequence from a record.
+ * Each element in the list is of type sdp_profile_desc_t
+ * which contains the UUID of the profile and its version number
+ * (encoded as major and minor in the high-order 8bits
+ * and low-order 8bits respectively of the uint16_t)
+ */
+int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDesc);
+
+/*
+ * Extract SDP server version numbers
+ *
+ * Note: that this is an attribute of the SDP server only and
+ * contains a list of uint16_t each of which represent the
+ * major and minor SDP version numbers supported by this server
+ */
+int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **pVnumList);
+
+int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid);
+int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid);
+int sdp_get_record_state(const sdp_record_t *rec, uint32_t *svcRecState);
+int sdp_get_service_avail(const sdp_record_t *rec, uint8_t *svcAvail);
+int sdp_get_service_ttl(const sdp_record_t *rec, uint32_t *svcTTLInfo);
+int sdp_get_database_state(const sdp_record_t *rec, uint32_t *svcDBState);
+
+static inline int sdp_get_service_name(const sdp_record_t *rec, char *str, int len)
+{
+       return sdp_get_string_attr(rec, SDP_ATTR_SVCNAME_PRIMARY, str, len);
+}
+
+static inline int sdp_get_service_desc(const sdp_record_t *rec, char *str, int len)
+{
+       return sdp_get_string_attr(rec, SDP_ATTR_SVCDESC_PRIMARY, str, len);
+}
+
+static inline int sdp_get_provider_name(const sdp_record_t *rec, char *str, int len)
+{
+       return sdp_get_string_attr(rec, SDP_ATTR_PROVNAME_PRIMARY, str, len);
+}
+
+static inline int sdp_get_doc_url(const sdp_record_t *rec, char *str, int len)
+{
+       return sdp_get_string_attr(rec, SDP_ATTR_DOC_URL, str, len);
+}
+
+static inline int sdp_get_clnt_exec_url(const sdp_record_t *rec, char *str, int len)
+{
+       return sdp_get_string_attr(rec, SDP_ATTR_CLNT_EXEC_URL, str, len);
+}
+
+static inline int sdp_get_icon_url(const sdp_record_t *rec, char *str, int len)
+{
+       return sdp_get_string_attr(rec, SDP_ATTR_ICON_URL, str, len);
+}
+
+/*
+ * Set the supported features
+ * sf should be a list of list with each feature data
+ * Returns 0 on success -1 on fail
+ */
+int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf);
+
+/*
+ * Get the supported features
+ * seqp is set to a list of list with each feature data
+ * Returns 0 on success, if an error occurred -1 is returned and errno is set
+ */
+int sdp_get_supp_feat(const sdp_record_t *rec, sdp_list_t **seqp);
+
+sdp_record_t *sdp_extract_pdu(const uint8_t *pdata, int bufsize, int *scanned);
+sdp_record_t *sdp_copy_record(sdp_record_t *rec);
+
+void sdp_data_print(sdp_data_t *data);
+void sdp_print_service_attr(sdp_list_t *alist);
+
+int sdp_attrid_comp_func(const void *key1, const void *key2);
+
+void sdp_set_seq_len(uint8_t *ptr, uint32_t length);
+void sdp_set_attrid(sdp_buf_t *pdu, uint16_t id);
+void sdp_append_to_pdu(sdp_buf_t *dst, sdp_data_t *d);
+void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len);
+
+int sdp_gen_pdu(sdp_buf_t *pdu, sdp_data_t *data);
+int sdp_gen_record_pdu(const sdp_record_t *rec, sdp_buf_t *pdu);
+
+int sdp_extract_seqtype(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size);
+
+sdp_data_t *sdp_extract_attr(const uint8_t *pdata, int bufsize, int *extractedLength, sdp_record_t *rec);
+
+void sdp_pattern_add_uuid(sdp_record_t *rec, uuid_t *uuid);
+void sdp_pattern_add_uuidseq(sdp_record_t *rec, sdp_list_t *seq);
+
+int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *req, uint8_t *rsp, uint32_t reqsize, uint32_t *rspsize);
+
+void sdp_add_lang_attr(sdp_record_t *rec);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SDP_LIB_H */
diff --git a/lib/uuid.c b/lib/uuid.c
new file mode 100644 (file)
index 0000000..a3e2a1a
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "uuid.h"
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+static uint128_t bluetooth_base_uuid = {
+       .data = {       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }
+};
+
+#define BASE_UUID16_OFFSET     2
+#define BASE_UUID32_OFFSET     0
+
+#else
+static uint128_t bluetooth_base_uuid = {
+       .data = {       0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
+                       0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+};
+
+#define BASE_UUID16_OFFSET     12
+#define BASE_UUID32_OFFSET     BASE_UUID16_OFFSET
+
+#endif
+
+static void bt_uuid16_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
+{
+       dst->value.u128 = bluetooth_base_uuid;
+       dst->type = BT_UUID128;
+
+       memcpy(&dst->value.u128.data[BASE_UUID16_OFFSET],
+                       &src->value.u16, sizeof(src->value.u16));
+}
+
+static void bt_uuid32_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
+{
+       dst->value.u128 = bluetooth_base_uuid;
+       dst->type = BT_UUID128;
+
+       memcpy(&dst->value.u128.data[BASE_UUID32_OFFSET],
+                               &src->value.u32, sizeof(src->value.u32));
+}
+
+void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
+{
+       switch (src->type) {
+       case BT_UUID128:
+               *dst = *src;
+               break;
+       case BT_UUID32:
+               bt_uuid32_to_uuid128(src, dst);
+               break;
+       case BT_UUID16:
+               bt_uuid16_to_uuid128(src, dst);
+               break;
+       default:
+               break;
+       }
+}
+
+static int bt_uuid128_cmp(const bt_uuid_t *u1, const bt_uuid_t *u2)
+{
+       return memcmp(&u1->value.u128, &u2->value.u128, sizeof(uint128_t));
+}
+
+int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value)
+{
+       memset(btuuid, 0, sizeof(bt_uuid_t));
+       btuuid->type = BT_UUID16;
+       btuuid->value.u16 = value;
+
+       return 0;
+}
+
+int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value)
+{
+       memset(btuuid, 0, sizeof(bt_uuid_t));
+       btuuid->type = BT_UUID32;
+       btuuid->value.u32 = value;
+
+       return 0;
+}
+
+int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value)
+{
+       memset(btuuid, 0, sizeof(bt_uuid_t));
+       btuuid->type = BT_UUID128;
+       btuuid->value.u128 = value;
+
+       return 0;
+}
+
+int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2)
+{
+       bt_uuid_t u1, u2;
+
+       bt_uuid_to_uuid128(uuid1, &u1);
+       bt_uuid_to_uuid128(uuid2, &u2);
+
+       return bt_uuid128_cmp(&u1, &u2);
+}
+
+/*
+ * convert the UUID to string, copying a maximum of n characters.
+ */
+int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n)
+{
+       if (!uuid) {
+               snprintf(str, n, "NULL");
+               return -EINVAL;
+       }
+
+       switch (uuid->type) {
+       case BT_UUID16:
+               snprintf(str, n, "%.4x", uuid->value.u16);
+               break;
+       case BT_UUID32:
+               snprintf(str, n, "%.8x", uuid->value.u32);
+               break;
+       case BT_UUID128: {
+               unsigned int   data0;
+               unsigned short data1;
+               unsigned short data2;
+               unsigned short data3;
+               unsigned int   data4;
+               unsigned short data5;
+
+               uint128_t nvalue;
+               const uint8_t *data = (uint8_t *) &nvalue;
+
+               hton128(&uuid->value.u128, &nvalue);
+
+               memcpy(&data0, &data[0], 4);
+               memcpy(&data1, &data[4], 2);
+               memcpy(&data2, &data[6], 2);
+               memcpy(&data3, &data[8], 2);
+               memcpy(&data4, &data[10], 4);
+               memcpy(&data5, &data[14], 2);
+
+               snprintf(str, n, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+                               ntohl(data0), ntohs(data1),
+                               ntohs(data2), ntohs(data3),
+                               ntohl(data4), ntohs(data5));
+               }
+               break;
+       default:
+               snprintf(str, n, "Type of UUID (%x) unknown.", uuid->type);
+               return -EINVAL; /* Enum type of UUID not set */
+       }
+
+       return 0;
+}
+
+static inline int is_uuid128(const char *string)
+{
+       return (strlen(string) == 36 &&
+                       string[8] == '-' &&
+                       string[13] == '-' &&
+                       string[18] == '-' &&
+                       string[23] == '-');
+}
+
+static inline int is_uuid32(const char *string)
+{
+       return (strlen(string) == 8 || strlen(string) == 10);
+}
+
+static inline int is_uuid16(const char *string)
+{
+       return (strlen(string) == 4 || strlen(string) == 6);
+}
+
+static int bt_string_to_uuid16(bt_uuid_t *uuid, const char *string)
+{
+       uint16_t u16;
+       char *endptr = NULL;
+
+       u16 = strtol(string, &endptr, 16);
+       if (endptr && *endptr == '\0') {
+               bt_uuid16_create(uuid, u16);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int bt_string_to_uuid32(bt_uuid_t *uuid, const char *string)
+{
+       uint32_t u32;
+       char *endptr = NULL;
+
+       u32 = strtol(string, &endptr, 16);
+       if (endptr && *endptr == '\0') {
+               bt_uuid32_create(uuid, u32);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int bt_string_to_uuid128(bt_uuid_t *uuid, const char *string)
+{
+       uint32_t data0, data4;
+       uint16_t data1, data2, data3, data5;
+       uint128_t n128, u128;
+       uint8_t *val = (uint8_t *) &n128;
+
+       if (sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
+                               &data0, &data1, &data2,
+                               &data3, &data4, &data5) != 6)
+               return -EINVAL;
+
+       data0 = htonl(data0);
+       data1 = htons(data1);
+       data2 = htons(data2);
+       data3 = htons(data3);
+       data4 = htonl(data4);
+       data5 = htons(data5);
+
+       memcpy(&val[0], &data0, 4);
+       memcpy(&val[4], &data1, 2);
+       memcpy(&val[6], &data2, 2);
+       memcpy(&val[8], &data3, 2);
+       memcpy(&val[10], &data4, 4);
+       memcpy(&val[14], &data5, 2);
+
+       ntoh128(&n128, &u128);
+
+       bt_uuid128_create(uuid, u128);
+
+       return 0;
+}
+
+int bt_string_to_uuid(bt_uuid_t *uuid, const char *string)
+{
+       if (is_uuid128(string))
+               return bt_string_to_uuid128(uuid, string);
+       else if (is_uuid32(string))
+               return bt_string_to_uuid32(uuid, string);
+       else if (is_uuid16(string))
+               return bt_string_to_uuid16(uuid, string);
+
+       return -EINVAL;
+}
diff --git a/lib/uuid.h b/lib/uuid.h
new file mode 100644 (file)
index 0000000..2c2b351
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __BLUETOOTH_UUID_H
+#define __BLUETOOTH_UUID_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <bluetooth/bluetooth.h>
+
+#define GENERIC_AUDIO_UUID     "00001203-0000-1000-8000-00805f9b34fb"
+
+#define HSP_HS_UUID            "00001108-0000-1000-8000-00805f9b34fb"
+#define HSP_AG_UUID            "00001112-0000-1000-8000-00805f9b34fb"
+
+#define HFP_HS_UUID            "0000111e-0000-1000-8000-00805f9b34fb"
+#define HFP_AG_UUID            "0000111f-0000-1000-8000-00805f9b34fb"
+
+#define ADVANCED_AUDIO_UUID    "0000110d-0000-1000-8000-00805f9b34fb"
+
+#define A2DP_SOURCE_UUID       "0000110a-0000-1000-8000-00805f9b34fb"
+#define A2DP_SINK_UUID         "0000110b-0000-1000-8000-00805f9b34fb"
+
+#define AVRCP_REMOTE_UUID      "0000110e-0000-1000-8000-00805f9b34fb"
+#define AVRCP_TARGET_UUID      "0000110c-0000-1000-8000-00805f9b34fb"
+
+#define PANU_UUID              "00001115-0000-1000-8000-00805f9b34fb"
+#define NAP_UUID               "00001116-0000-1000-8000-00805f9b34fb"
+#define GN_UUID                        "00001117-0000-1000-8000-00805f9b34fb"
+#define BNEP_SVC_UUID          "0000000f-0000-1000-8000-00805f9b34fb"
+
+#define PNPID_UUID             "00002a50-0000-1000-8000-00805f9b34fb"
+#define DEVICE_INFORMATION_UUID        "0000180a-0000-1000-8000-00805f9b34fb"
+
+#define GATT_UUID              "00001801-0000-1000-8000-00805f9b34fb"
+#define IMMEDIATE_ALERT_UUID   "00001802-0000-1000-8000-00805f9b34fb"
+#define LINK_LOSS_UUID         "00001803-0000-1000-8000-00805f9b34fb"
+#define TX_POWER_UUID          "00001804-0000-1000-8000-00805f9b34fb"
+
+#define SAP_UUID               "0000112D-0000-1000-8000-00805f9b34fb"
+
+#define HEALTH_THERMOMETER_UUID                "00001809-0000-1000-8000-00805f9b34fb"
+#define TEMPERATURE_MEASUREMENT_UUID   "00002a1c-0000-1000-8000-00805f9b34fb"
+#define TEMPERATURE_TYPE_UUID          "00002a1d-0000-1000-8000-00805f9b34fb"
+#define INTERMEDIATE_TEMPERATURE_UUID  "00002a1e-0000-1000-8000-00805f9b34fb"
+#define MEASUREMENT_INTERVAL_UUID      "00002a21-0000-1000-8000-00805f9b34fb"
+
+#define RFCOMM_UUID_STR                "00000003-0000-1000-8000-00805f9b34fb"
+
+#define HDP_UUID               "00001400-0000-1000-8000-00805f9b34fb"
+#define HDP_SOURCE_UUID                "00001401-0000-1000-8000-00805f9b34fb"
+#define HDP_SINK_UUID          "00001402-0000-1000-8000-00805f9b34fb"
+
+#define HSP_HS_UUID            "00001108-0000-1000-8000-00805f9b34fb"
+#define HID_UUID               "00001124-0000-1000-8000-00805f9b34fb"
+
+#define DUN_GW_UUID            "00001103-0000-1000-8000-00805f9b34fb"
+
+#define GAP_SVC_UUID           "00001800-0000-1000-8000-00805f9b34fb"
+#define PNP_UUID               "00001200-0000-1000-8000-00805f9b34fb"
+
+typedef struct {
+       enum {
+               BT_UUID_UNSPEC = 0,
+               BT_UUID16 = 16,
+               BT_UUID32 = 32,
+               BT_UUID128 = 128,
+       } type;
+       union {
+               uint16_t  u16;
+               uint32_t  u32;
+               uint128_t u128;
+       } value;
+} bt_uuid_t;
+
+int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value);
+int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value);
+int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value);
+
+int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2);
+void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst);
+
+#define MAX_LEN_UUID_STR 37
+
+int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n);
+int bt_string_to_uuid(bt_uuid_t *uuid, const char *string);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BLUETOOTH_UUID_H */
diff --git a/ltmain.sh b/ltmain.sh
new file mode 100644 (file)
index 0000000..c7d06c3
--- /dev/null
+++ b/ltmain.sh
@@ -0,0 +1,9661 @@
+
+# libtool (GNU libtool) 2.4.2
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006,
+# 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions.  There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Libtool; see the file COPYING.  If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html,
+# or obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Usage: $progname [OPTION]... [MODE-ARG]...
+#
+# Provide generalized library-building support services.
+#
+#       --config             show all configuration variables
+#       --debug              enable verbose shell tracing
+#   -n, --dry-run            display commands without modifying any files
+#       --features           display basic configuration information and exit
+#       --mode=MODE          use operation mode MODE
+#       --preserve-dup-deps  don't remove duplicate dependency libraries
+#       --quiet, --silent    don't print informational messages
+#       --no-quiet, --no-silent
+#                            print informational messages (default)
+#       --no-warn            don't display warning messages
+#       --tag=TAG            use configuration variables from tag TAG
+#   -v, --verbose            print more informational messages than default
+#       --no-verbose         don't print the extra informational messages
+#       --version            print version information
+#   -h, --help, --help-all   print short, long, or detailed help message
+#
+# MODE must be one of the following:
+#
+#         clean              remove files from the build directory
+#         compile            compile a source file into a libtool object
+#         execute            automatically set library path, then run a program
+#         finish             complete the installation of libtool libraries
+#         install            install libraries or executables
+#         link               create a library or an executable
+#         uninstall          remove libraries from an installed directory
+#
+# MODE-ARGS vary depending on the MODE.  When passed as first option,
+# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that.
+# Try `$progname --help --mode=MODE' for a more detailed description of MODE.
+#
+# When reporting a bug, please describe a test case to reproduce it and
+# include the following information:
+#
+#         host-triplet:        $host
+#         shell:               $SHELL
+#         compiler:            $LTCC
+#         compiler flags:              $LTCFLAGS
+#         linker:              $LD (gnu? $with_gnu_ld)
+#         $progname:   (GNU libtool) 2.4.2 Debian-2.4.2-1
+#         automake:    $automake_version
+#         autoconf:    $autoconf_version
+#
+# Report bugs to <bug-libtool@gnu.org>.
+# GNU libtool home page: <http://www.gnu.org/software/libtool/>.
+# General help using GNU software: <http://www.gnu.org/gethelp/>.
+
+PROGRAM=libtool
+PACKAGE=libtool
+VERSION="2.4.2 Debian-2.4.2-1"
+TIMESTAMP=""
+package_revision=1.3337
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+
+# NLS nuisances: We save the old values to restore during execute mode.
+lt_user_locale=
+lt_safe_locale=
+for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+  eval "if test \"\${$lt_var+set}\" = set; then
+          save_$lt_var=\$$lt_var
+          $lt_var=C
+         export $lt_var
+         lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\"
+         lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\"
+       fi"
+done
+LC_ALL=C
+LANGUAGE=C
+export LANGUAGE LC_ALL
+
+$lt_unset CDPATH
+
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath="$0"
+
+
+
+: ${CP="cp -f"}
+test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+: ${Xsed="$SED -e 1s/^X//"}
+
+# Global variables:
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63  # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77     # $? = 77 is used to indicate a skipped test to automake.
+
+exit_status=$EXIT_SUCCESS
+
+# Make sure IFS has a sensible default
+lt_nl='
+'
+IFS="  $lt_nl"
+
+dirname="s,/[^/]*$,,"
+basename="s,^.*/,,"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+    func_dirname_result=`$ECHO "${1}" | $SED "$dirname"`
+    if test "X$func_dirname_result" = "X${1}"; then
+      func_dirname_result="${3}"
+    else
+      func_dirname_result="$func_dirname_result${2}"
+    fi
+} # func_dirname may be replaced by extended shell implementation
+
+
+# func_basename file
+func_basename ()
+{
+    func_basename_result=`$ECHO "${1}" | $SED "$basename"`
+} # func_basename may be replaced by extended shell implementation
+
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+#   dirname:  Compute the dirname of FILE.  If nonempty,
+#             add APPEND to the result, otherwise set result
+#             to NONDIR_REPLACEMENT.
+#             value returned in "$func_dirname_result"
+#   basename: Compute filename of FILE.
+#             value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+    # Extract subdirectory from the argument.
+    func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"`
+    if test "X$func_dirname_result" = "X${1}"; then
+      func_dirname_result="${3}"
+    else
+      func_dirname_result="$func_dirname_result${2}"
+    fi
+    func_basename_result=`$ECHO "${1}" | $SED -e "$basename"`
+} # func_dirname_and_basename may be replaced by extended shell implementation
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+    case ${2} in
+      .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
+      *)  func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+    esac
+} # func_stripname may be replaced by extended shell implementation
+
+
+# These SED scripts presuppose an absolute path with a trailing slash.
+pathcar='s,^/\([^/]*\).*$,\1,'
+pathcdr='s,^/[^/]*,,'
+removedotparts=':dotsl
+               s@/\./@/@g
+               t dotsl
+               s,/\.$,/,'
+collapseslashes='s@/\{1,\}@/@g'
+finalslash='s,/*$,/,'
+
+# func_normal_abspath PATH
+# Remove doubled-up and trailing slashes, "." path components,
+# and cancel out any ".." path components in PATH after making
+# it an absolute path.
+#             value returned in "$func_normal_abspath_result"
+func_normal_abspath ()
+{
+  # Start from root dir and reassemble the path.
+  func_normal_abspath_result=
+  func_normal_abspath_tpath=$1
+  func_normal_abspath_altnamespace=
+  case $func_normal_abspath_tpath in
+    "")
+      # Empty path, that just means $cwd.
+      func_stripname '' '/' "`pwd`"
+      func_normal_abspath_result=$func_stripname_result
+      return
+    ;;
+    # The next three entries are used to spot a run of precisely
+    # two leading slashes without using negated character classes;
+    # we take advantage of case's first-match behaviour.
+    ///*)
+      # Unusual form of absolute path, do nothing.
+    ;;
+    //*)
+      # Not necessarily an ordinary path; POSIX reserves leading '//'
+      # and for example Cygwin uses it to access remote file shares
+      # over CIFS/SMB, so we conserve a leading double slash if found.
+      func_normal_abspath_altnamespace=/
+    ;;
+    /*)
+      # Absolute path, do nothing.
+    ;;
+    *)
+      # Relative path, prepend $cwd.
+      func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
+    ;;
+  esac
+  # Cancel out all the simple stuff to save iterations.  We also want
+  # the path to end with a slash for ease of parsing, so make sure
+  # there is one (and only one) here.
+  func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+        -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"`
+  while :; do
+    # Processed it all yet?
+    if test "$func_normal_abspath_tpath" = / ; then
+      # If we ascended to the root using ".." the result may be empty now.
+      if test -z "$func_normal_abspath_result" ; then
+        func_normal_abspath_result=/
+      fi
+      break
+    fi
+    func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
+        -e "$pathcar"`
+    func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+        -e "$pathcdr"`
+    # Figure out what to do with it
+    case $func_normal_abspath_tcomponent in
+      "")
+        # Trailing empty path component, ignore it.
+      ;;
+      ..)
+        # Parent dir; strip last assembled component from result.
+        func_dirname "$func_normal_abspath_result"
+        func_normal_abspath_result=$func_dirname_result
+      ;;
+      *)
+        # Actual path component, append it.
+        func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent
+      ;;
+    esac
+  done
+  # Restore leading double-slash if one was found on entry.
+  func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
+}
+
+# func_relative_path SRCDIR DSTDIR
+# generates a relative path from SRCDIR to DSTDIR, with a trailing
+# slash if non-empty, suitable for immediately appending a filename
+# without needing to append a separator.
+#             value returned in "$func_relative_path_result"
+func_relative_path ()
+{
+  func_relative_path_result=
+  func_normal_abspath "$1"
+  func_relative_path_tlibdir=$func_normal_abspath_result
+  func_normal_abspath "$2"
+  func_relative_path_tbindir=$func_normal_abspath_result
+
+  # Ascend the tree starting from libdir
+  while :; do
+    # check if we have found a prefix of bindir
+    case $func_relative_path_tbindir in
+      $func_relative_path_tlibdir)
+        # found an exact match
+        func_relative_path_tcancelled=
+        break
+        ;;
+      $func_relative_path_tlibdir*)
+        # found a matching prefix
+        func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
+        func_relative_path_tcancelled=$func_stripname_result
+        if test -z "$func_relative_path_result"; then
+          func_relative_path_result=.
+        fi
+        break
+        ;;
+      *)
+        func_dirname $func_relative_path_tlibdir
+        func_relative_path_tlibdir=${func_dirname_result}
+        if test "x$func_relative_path_tlibdir" = x ; then
+          # Have to descend all the way to the root!
+          func_relative_path_result=../$func_relative_path_result
+          func_relative_path_tcancelled=$func_relative_path_tbindir
+          break
+        fi
+        func_relative_path_result=../$func_relative_path_result
+        ;;
+    esac
+  done
+
+  # Now calculate path; take care to avoid doubling-up slashes.
+  func_stripname '' '/' "$func_relative_path_result"
+  func_relative_path_result=$func_stripname_result
+  func_stripname '/' '/' "$func_relative_path_tcancelled"
+  if test "x$func_stripname_result" != x ; then
+    func_relative_path_result=${func_relative_path_result}/${func_stripname_result}
+  fi
+
+  # Normalisation. If bindir is libdir, return empty string,
+  # else relative path ending with a slash; either way, target
+  # file name can be directly appended.
+  if test ! -z "$func_relative_path_result"; then
+    func_stripname './' '' "$func_relative_path_result/"
+    func_relative_path_result=$func_stripname_result
+  fi
+}
+
+# The name of this program:
+func_dirname_and_basename "$progpath"
+progname=$func_basename_result
+
+# Make sure we have an absolute path for reexecution:
+case $progpath in
+  [\\/]*|[A-Za-z]:\\*) ;;
+  *[\\/]*)
+     progdir=$func_dirname_result
+     progdir=`cd "$progdir" && pwd`
+     progpath="$progdir/$progname"
+     ;;
+  *)
+     save_IFS="$IFS"
+     IFS=${PATH_SEPARATOR-:}
+     for progdir in $PATH; do
+       IFS="$save_IFS"
+       test -x "$progdir/$progname" && break
+     done
+     IFS="$save_IFS"
+     test -n "$progdir" || progdir=`pwd`
+     progpath="$progdir/$progname"
+     ;;
+esac
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed="${SED}"' -e 1s/^X//'
+sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution that turns a string into a regex matching for the
+# string literally.
+sed_make_literal_regex='s,[].[^$\\*\/],\\&,g'
+
+# Sed substitution that converts a w32 file name or path
+# which contains forward slashes, into one that contains
+# (escaped) backslashes.  A very naive implementation.
+lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+
+# Re-`\' parameter expansions in output of double_quote_subst that were
+# `\'-ed in input to the same.  If an odd number of `\' preceded a '$'
+# in input to double_quote_subst, that '$' was protected from expansion.
+# Since each input `\' is now two `\'s, look for any number of runs of
+# four `\'s followed by two `\'s and then a '$'.  `\' that '$'.
+bs='\\'
+bs2='\\\\'
+bs4='\\\\\\\\'
+dollar='\$'
+sed_double_backslash="\
+  s/$bs4/&\\
+/g
+  s/^$bs2$dollar/$bs&/
+  s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g
+  s/\n//g"
+
+# Standard options:
+opt_dry_run=false
+opt_help=false
+opt_quiet=false
+opt_verbose=false
+opt_warning=:
+
+# func_echo arg...
+# Echo program name prefixed message, along with the current mode
+# name if it has been set yet.
+func_echo ()
+{
+    $ECHO "$progname: ${opt_mode+$opt_mode: }$*"
+}
+
+# func_verbose arg...
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+    $opt_verbose && func_echo ${1+"$@"}
+
+    # A bug in bash halts the script if the last line of a function
+    # fails when set -e is in force, so we need another command to
+    # work around that:
+    :
+}
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO "$*"
+}
+
+# func_error arg...
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+    $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2
+}
+
+# func_warning arg...
+# Echo program name prefixed warning message to standard error.
+func_warning ()
+{
+    $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2
+
+    # bash bug again:
+    :
+}
+
+# func_fatal_error arg...
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+    func_error ${1+"$@"}
+    exit $EXIT_FAILURE
+}
+
+# func_fatal_help arg...
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+    func_error ${1+"$@"}
+    func_fatal_error "$help"
+}
+help="Try \`$progname --help' for more information."  ## default
+
+
+# func_grep expression filename
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+    $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_mkdir_p directory-path
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+    my_directory_path="$1"
+    my_dir_list=
+
+    if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then
+
+      # Protect directory names starting with `-'
+      case $my_directory_path in
+        -*) my_directory_path="./$my_directory_path" ;;
+      esac
+
+      # While some portion of DIR does not yet exist...
+      while test ! -d "$my_directory_path"; do
+        # ...make a list in topmost first order.  Use a colon delimited
+       # list incase some portion of path contains whitespace.
+        my_dir_list="$my_directory_path:$my_dir_list"
+
+        # If the last portion added has no slash in it, the list is done
+        case $my_directory_path in */*) ;; *) break ;; esac
+
+        # ...otherwise throw away the child directory and loop
+        my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"`
+      done
+      my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'`
+
+      save_mkdir_p_IFS="$IFS"; IFS=':'
+      for my_dir in $my_dir_list; do
+       IFS="$save_mkdir_p_IFS"
+        # mkdir can fail with a `File exist' error if two processes
+        # try to create one of the directories concurrently.  Don't
+        # stop in that case!
+        $MKDIR "$my_dir" 2>/dev/null || :
+      done
+      IFS="$save_mkdir_p_IFS"
+
+      # Bail out if we (or some other process) failed to create a directory.
+      test -d "$my_directory_path" || \
+        func_fatal_error "Failed to create \`$1'"
+    fi
+}
+
+
+# func_mktempdir [string]
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible.  If
+# given, STRING is the basename for that directory.
+func_mktempdir ()
+{
+    my_template="${TMPDIR-/tmp}/${1-$progname}"
+
+    if test "$opt_dry_run" = ":"; then
+      # Return a directory name, but don't create it in dry-run mode
+      my_tmpdir="${my_template}-$$"
+    else
+
+      # If mktemp works, use that first and foremost
+      my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null`
+
+      if test ! -d "$my_tmpdir"; then
+        # Failing that, at least try and use $RANDOM to avoid a race
+        my_tmpdir="${my_template}-${RANDOM-0}$$"
+
+        save_mktempdir_umask=`umask`
+        umask 0077
+        $MKDIR "$my_tmpdir"
+        umask $save_mktempdir_umask
+      fi
+
+      # If we're not in dry-run mode, bomb out on failure
+      test -d "$my_tmpdir" || \
+        func_fatal_error "cannot create temporary directory \`$my_tmpdir'"
+    fi
+
+    $ECHO "$my_tmpdir"
+}
+
+
+# func_quote_for_eval arg
+# Aesthetically quote ARG to be evaled later.
+# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT
+# is double-quoted, suitable for a subsequent eval, whereas
+# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters
+# which are still active within double quotes backslashified.
+func_quote_for_eval ()
+{
+    case $1 in
+      *[\\\`\"\$]*)
+       func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;;
+      *)
+        func_quote_for_eval_unquoted_result="$1" ;;
+    esac
+
+    case $func_quote_for_eval_unquoted_result in
+      # Double-quote args containing shell metacharacters to delay
+      # word splitting, command substitution and and variable
+      # expansion for a subsequent eval.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*|"")
+        func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\""
+        ;;
+      *)
+        func_quote_for_eval_result="$func_quote_for_eval_unquoted_result"
+    esac
+}
+
+
+# func_quote_for_expand arg
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+    case $1 in
+      *[\\\`\"]*)
+       my_arg=`$ECHO "$1" | $SED \
+           -e "$double_quote_subst" -e "$sed_double_backslash"` ;;
+      *)
+        my_arg="$1" ;;
+    esac
+
+    case $my_arg in
+      # Double-quote args containing shell metacharacters to delay
+      # word splitting and command substitution for a subsequent eval.
+      # Many Bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \    ]*|*]*|"")
+        my_arg="\"$my_arg\""
+        ;;
+    esac
+
+    func_quote_for_expand_result="$my_arg"
+}
+
+
+# func_show_eval cmd [fail_exp]
+# Unless opt_silent is true, then output CMD.  Then, if opt_dryrun is
+# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+    my_cmd="$1"
+    my_fail_exp="${2-:}"
+
+    ${opt_silent-false} || {
+      func_quote_for_expand "$my_cmd"
+      eval "func_echo $func_quote_for_expand_result"
+    }
+
+    if ${opt_dry_run-false}; then :; else
+      eval "$my_cmd"
+      my_status=$?
+      if test "$my_status" -eq 0; then :; else
+       eval "(exit $my_status); $my_fail_exp"
+      fi
+    fi
+}
+
+
+# func_show_eval_locale cmd [fail_exp]
+# Unless opt_silent is true, then output CMD.  Then, if opt_dryrun is
+# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.  Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+    my_cmd="$1"
+    my_fail_exp="${2-:}"
+
+    ${opt_silent-false} || {
+      func_quote_for_expand "$my_cmd"
+      eval "func_echo $func_quote_for_expand_result"
+    }
+
+    if ${opt_dry_run-false}; then :; else
+      eval "$lt_user_locale
+           $my_cmd"
+      my_status=$?
+      eval "$lt_safe_locale"
+      if test "$my_status" -eq 0; then :; else
+       eval "(exit $my_status); $my_fail_exp"
+      fi
+    fi
+}
+
+# func_tr_sh
+# Turn $1 into a string suitable for a shell variable name.
+# Result is stored in $func_tr_sh_result.  All characters
+# not in the set a-zA-Z0-9_ are replaced with '_'. Further,
+# if $1 begins with a digit, a '_' is prepended as well.
+func_tr_sh ()
+{
+  case $1 in
+  [0-9]* | *[!a-zA-Z0-9_]*)
+    func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'`
+    ;;
+  * )
+    func_tr_sh_result=$1
+    ;;
+  esac
+}
+
+
+# func_version
+# Echo version message to standard output and exit.
+func_version ()
+{
+    $opt_debug
+
+    $SED -n '/(C)/!b go
+       :more
+       /\./!{
+         N
+         s/\n# / /
+         b more
+       }
+       :go
+       /^# '$PROGRAM' (GNU /,/# warranty; / {
+        s/^# //
+       s/^# *$//
+        s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/
+        p
+     }' < "$progpath"
+     exit $?
+}
+
+# func_usage
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+    $opt_debug
+
+    $SED -n '/^# Usage:/,/^#  *.*--help/ {
+        s/^# //
+       s/^# *$//
+       s/\$progname/'$progname'/
+       p
+    }' < "$progpath"
+    echo
+    $ECHO "run \`$progname --help | more' for full usage"
+    exit $?
+}
+
+# func_help [NOEXIT]
+# Echo long help message to standard output and exit,
+# unless 'noexit' is passed as argument.
+func_help ()
+{
+    $opt_debug
+
+    $SED -n '/^# Usage:/,/# Report bugs to/ {
+       :print
+        s/^# //
+       s/^# *$//
+       s*\$progname*'$progname'*
+       s*\$host*'"$host"'*
+       s*\$SHELL*'"$SHELL"'*
+       s*\$LTCC*'"$LTCC"'*
+       s*\$LTCFLAGS*'"$LTCFLAGS"'*
+       s*\$LD*'"$LD"'*
+       s/\$with_gnu_ld/'"$with_gnu_ld"'/
+       s/\$automake_version/'"`(${AUTOMAKE-automake} --version) 2>/dev/null |$SED 1q`"'/
+       s/\$autoconf_version/'"`(${AUTOCONF-autoconf} --version) 2>/dev/null |$SED 1q`"'/
+       p
+       d
+     }
+     /^# .* home page:/b print
+     /^# General help using/b print
+     ' < "$progpath"
+    ret=$?
+    if test -z "$1"; then
+      exit $ret
+    fi
+}
+
+# func_missing_arg argname
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+    $opt_debug
+
+    func_error "missing argument for $1."
+    exit_cmd=exit
+}
+
+
+# func_split_short_opt shortopt
+# Set func_split_short_opt_name and func_split_short_opt_arg shell
+# variables after splitting SHORTOPT after the 2nd character.
+func_split_short_opt ()
+{
+    my_sed_short_opt='1s/^\(..\).*$/\1/;q'
+    my_sed_short_rest='1s/^..\(.*\)$/\1/;q'
+
+    func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"`
+    func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"`
+} # func_split_short_opt may be replaced by extended shell implementation
+
+
+# func_split_long_opt longopt
+# Set func_split_long_opt_name and func_split_long_opt_arg shell
+# variables after splitting LONGOPT at the `=' sign.
+func_split_long_opt ()
+{
+    my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q'
+    my_sed_long_arg='1s/^--[^=]*=//'
+
+    func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"`
+    func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"`
+} # func_split_long_opt may be replaced by extended shell implementation
+
+exit_cmd=:
+
+
+
+
+
+magic="%%%MAGIC variable%%%"
+magic_exe="%%%MAGIC EXE variable%%%"
+
+# Global variables.
+nonopt=
+preserve_args=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+extracted_archives=
+extracted_serial=0
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end.  This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+    eval "${1}=\$${1}\${2}"
+} # func_append may be replaced by extended shell implementation
+
+# func_append_quoted var value
+# Quote VALUE and append to the end of shell variable VAR, separated
+# by a space.
+func_append_quoted ()
+{
+    func_quote_for_eval "${2}"
+    eval "${1}=\$${1}\\ \$func_quote_for_eval_result"
+} # func_append_quoted may be replaced by extended shell implementation
+
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+    func_arith_result=`expr "${@}"`
+} # func_arith may be replaced by extended shell implementation
+
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+    func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len`
+} # func_len may be replaced by extended shell implementation
+
+
+# func_lo2o object
+func_lo2o ()
+{
+    func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"`
+} # func_lo2o may be replaced by extended shell implementation
+
+
+# func_xform libobj-or-source
+func_xform ()
+{
+    func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'`
+} # func_xform may be replaced by extended shell implementation
+
+
+# func_fatal_configuration arg...
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+    func_error ${1+"$@"}
+    func_error "See the $PACKAGE documentation for more information."
+    func_fatal_error "Fatal configuration error."
+}
+
+
+# func_config
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+    re_begincf='^# ### BEGIN LIBTOOL'
+    re_endcf='^# ### END LIBTOOL'
+
+    # Default configuration.
+    $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+    # Now print the configurations for the tags.
+    for tagname in $taglist; do
+      $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+    done
+
+    exit $?
+}
+
+# func_features
+# Display the features supported by this script.
+func_features ()
+{
+    echo "host: $host"
+    if test "$build_libtool_libs" = yes; then
+      echo "enable shared libraries"
+    else
+      echo "disable shared libraries"
+    fi
+    if test "$build_old_libs" = yes; then
+      echo "enable static libraries"
+    else
+      echo "disable static libraries"
+    fi
+
+    exit $?
+}
+
+# func_enable_tag tagname
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag.  We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+  # Global variable:
+  tagname="$1"
+
+  re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+  re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+  sed_extractcf="/$re_begincf/,/$re_endcf/p"
+
+  # Validate tagname.
+  case $tagname in
+    *[!-_A-Za-z0-9,/]*)
+      func_fatal_error "invalid tag name: $tagname"
+      ;;
+  esac
+
+  # Don't test for the "default" C tag, as we know it's
+  # there but not specially marked.
+  case $tagname in
+    CC) ;;
+    *)
+      if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+       taglist="$taglist $tagname"
+
+       # Evaluate the configuration.  Be careful to quote the path
+       # and the sed script, to avoid splitting on whitespace, but
+       # also don't use non-portable quotes within backquotes within
+       # quotes we have to do it in 2 steps:
+       extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+       eval "$extractedcf"
+      else
+       func_error "ignoring unknown tag $tagname"
+      fi
+      ;;
+  esac
+}
+
+# func_check_version_match
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+  if test "$package_revision" != "$macro_revision"; then
+    if test "$VERSION" != "$macro_version"; then
+      if test -z "$macro_version"; then
+        cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+      else
+        cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+      fi
+    else
+      cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+    fi
+
+    exit $EXIT_MISMATCH
+  fi
+}
+
+
+# Shorthand for --mode=foo, only valid as the first argument
+case $1 in
+clean|clea|cle|cl)
+  shift; set dummy --mode clean ${1+"$@"}; shift
+  ;;
+compile|compil|compi|comp|com|co|c)
+  shift; set dummy --mode compile ${1+"$@"}; shift
+  ;;
+execute|execut|execu|exec|exe|ex|e)
+  shift; set dummy --mode execute ${1+"$@"}; shift
+  ;;
+finish|finis|fini|fin|fi|f)
+  shift; set dummy --mode finish ${1+"$@"}; shift
+  ;;
+install|instal|insta|inst|ins|in|i)
+  shift; set dummy --mode install ${1+"$@"}; shift
+  ;;
+link|lin|li|l)
+  shift; set dummy --mode link ${1+"$@"}; shift
+  ;;
+uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+  shift; set dummy --mode uninstall ${1+"$@"}; shift
+  ;;
+esac
+
+
+
+# Option defaults:
+opt_debug=:
+opt_dry_run=false
+opt_config=false
+opt_preserve_dup_deps=false
+opt_features=false
+opt_finish=false
+opt_help=false
+opt_help_all=false
+opt_silent=:
+opt_warning=:
+opt_verbose=:
+opt_silent=false
+opt_verbose=false
+
+
+# Parse options once, thoroughly.  This comes as soon as possible in the
+# script to make things like `--version' happen as quickly as we can.
+{
+  # this just eases exit handling
+  while test $# -gt 0; do
+    opt="$1"
+    shift
+    case $opt in
+      --debug|-x)      opt_debug='set -x'
+                       func_echo "enabling shell trace mode"
+                       $opt_debug
+                       ;;
+      --dry-run|--dryrun|-n)
+                       opt_dry_run=:
+                       ;;
+      --config)
+                       opt_config=:
+func_config
+                       ;;
+      --dlopen|-dlopen)
+                       optarg="$1"
+                       opt_dlopen="${opt_dlopen+$opt_dlopen
+}$optarg"
+                       shift
+                       ;;
+      --preserve-dup-deps)
+                       opt_preserve_dup_deps=:
+                       ;;
+      --features)
+                       opt_features=:
+func_features
+                       ;;
+      --finish)
+                       opt_finish=:
+set dummy --mode finish ${1+"$@"}; shift
+                       ;;
+      --help)
+                       opt_help=:
+                       ;;
+      --help-all)
+                       opt_help_all=:
+opt_help=': help-all'
+                       ;;
+      --mode)
+                       test $# = 0 && func_missing_arg $opt && break
+                       optarg="$1"
+                       opt_mode="$optarg"
+case $optarg in
+  # Valid mode arguments:
+  clean|compile|execute|finish|install|link|relink|uninstall) ;;
+
+  # Catch anything else as an error
+  *) func_error "invalid argument for $opt"
+     exit_cmd=exit
+     break
+     ;;
+esac
+                       shift
+                       ;;
+      --no-silent|--no-quiet)
+                       opt_silent=false
+func_append preserve_args " $opt"
+                       ;;
+      --no-warning|--no-warn)
+                       opt_warning=false
+func_append preserve_args " $opt"
+                       ;;
+      --no-verbose)
+                       opt_verbose=false
+func_append preserve_args " $opt"
+                       ;;
+      --silent|--quiet)
+                       opt_silent=:
+func_append preserve_args " $opt"
+        opt_verbose=false
+                       ;;
+      --verbose|-v)
+                       opt_verbose=:
+func_append preserve_args " $opt"
+opt_silent=false
+                       ;;
+      --tag)
+                       test $# = 0 && func_missing_arg $opt && break
+                       optarg="$1"
+                       opt_tag="$optarg"
+func_append preserve_args " $opt $optarg"
+func_enable_tag "$optarg"
+                       shift
+                       ;;
+
+      -\?|-h)          func_usage                              ;;
+      --help)          func_help                               ;;
+      --version)       func_version                            ;;
+
+      # Separate optargs to long options:
+      --*=*)
+                       func_split_long_opt "$opt"
+                       set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"}
+                       shift
+                       ;;
+
+      # Separate non-argument short options:
+      -\?*|-h*|-n*|-v*)
+                       func_split_short_opt "$opt"
+                       set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"}
+                       shift
+                       ;;
+
+      --)              break                                   ;;
+      -*)              func_fatal_help "unrecognized option \`$opt'" ;;
+      *)               set dummy "$opt" ${1+"$@"};     shift; break  ;;
+    esac
+  done
+
+  # Validate options:
+
+  # save first non-option argument
+  if test "$#" -gt 0; then
+    nonopt="$opt"
+    shift
+  fi
+
+  # preserve --debug
+  test "$opt_debug" = : || func_append preserve_args " --debug"
+
+  case $host in
+    *cygwin* | *mingw* | *pw32* | *cegcc*)
+      # don't eliminate duplications in $postdeps and $predeps
+      opt_duplicate_compiler_generated_deps=:
+      ;;
+    *)
+      opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
+      ;;
+  esac
+
+  $opt_help || {
+    # Sanity checks first:
+    func_check_version_match
+
+    if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+      func_fatal_configuration "not configured to build any kind of library"
+    fi
+
+    # Darwin sucks
+    eval std_shrext=\"$shrext_cmds\"
+
+    # Only execute mode is allowed to have -dlopen flags.
+    if test -n "$opt_dlopen" && test "$opt_mode" != execute; then
+      func_error "unrecognized option \`-dlopen'"
+      $ECHO "$help" 1>&2
+      exit $EXIT_FAILURE
+    fi
+
+    # Change the help message to a mode-specific one.
+    generic_help="$help"
+    help="Try \`$progname --help --mode=$opt_mode' for more information."
+  }
+
+
+  # Bail if the options were screwed
+  $exit_cmd $EXIT_FAILURE
+}
+
+
+
+
+## ----------- ##
+##    Main.    ##
+## ----------- ##
+
+# func_lalib_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+    test -f "$1" &&
+      $SED -e 4q "$1" 2>/dev/null \
+        | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs.  To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway.  Works if `file' does not exist.
+func_lalib_unsafe_p ()
+{
+    lalib_p=no
+    if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
+       for lalib_p_l in 1 2 3 4
+       do
+           read lalib_p_line
+           case "$lalib_p_line" in
+               \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+           esac
+       done
+       exec 0<&5 5<&-
+    fi
+    test "$lalib_p" = yes
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+    func_lalib_p "$1"
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+    func_ltwrapper_exec_suffix=
+    case $1 in
+    *.exe) ;;
+    *) func_ltwrapper_exec_suffix=.exe ;;
+    esac
+    $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+    func_dirname_and_basename "$1" "" "."
+    func_stripname '' '.exe' "$func_basename_result"
+    func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper"
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+    func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+    $opt_debug
+    save_ifs=$IFS; IFS='~'
+    for cmd in $1; do
+      IFS=$save_ifs
+      eval cmd=\"$cmd\"
+      func_show_eval "$cmd" "${2-:}"
+    done
+    IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)!  Also, sourcing
+# `FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+    $opt_debug
+    case $1 in
+    */* | *\\*)        . "$1" ;;
+    *)         . "./$1" ;;
+    esac
+}
+
+
+# func_resolve_sysroot PATH
+# Replace a leading = in PATH with a sysroot.  Store the result into
+# func_resolve_sysroot_result
+func_resolve_sysroot ()
+{
+  func_resolve_sysroot_result=$1
+  case $func_resolve_sysroot_result in
+  =*)
+    func_stripname '=' '' "$func_resolve_sysroot_result"
+    func_resolve_sysroot_result=$lt_sysroot$func_stripname_result
+    ;;
+  esac
+}
+
+# func_replace_sysroot PATH
+# If PATH begins with the sysroot, replace it with = and
+# store the result into func_replace_sysroot_result.
+func_replace_sysroot ()
+{
+  case "$lt_sysroot:$1" in
+  ?*:"$lt_sysroot"*)
+    func_stripname "$lt_sysroot" '' "$1"
+    func_replace_sysroot_result="=$func_stripname_result"
+    ;;
+  *)
+    # Including no sysroot.
+    func_replace_sysroot_result=$1
+    ;;
+  esac
+}
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+    $opt_debug
+    if test -n "$available_tags" && test -z "$tagname"; then
+      CC_quoted=
+      for arg in $CC; do
+       func_append_quoted CC_quoted "$arg"
+      done
+      CC_expanded=`func_echo_all $CC`
+      CC_quoted_expanded=`func_echo_all $CC_quoted`
+      case $@ in
+      # Blanks in the command may have been stripped by the calling shell,
+      # but not from the CC environment variable when configure was run.
+      " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+      " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;;
+      # Blanks at the start of $base_compile will cause this to fail
+      # if we don't check for them as well.
+      *)
+       for z in $available_tags; do
+         if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+           # Evaluate the configuration.
+           eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+           CC_quoted=
+           for arg in $CC; do
+             # Double-quote args containing other shell metacharacters.
+             func_append_quoted CC_quoted "$arg"
+           done
+           CC_expanded=`func_echo_all $CC`
+           CC_quoted_expanded=`func_echo_all $CC_quoted`
+           case "$@ " in
+           " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+           " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*)
+             # The compiler in the base compile command matches
+             # the one in the tagged configuration.
+             # Assume this is the tagged configuration we want.
+             tagname=$z
+             break
+             ;;
+           esac
+         fi
+       done
+       # If $tagname still isn't set, then no tagged configuration
+       # was found and let the user know that the "--tag" command
+       # line option must be used.
+       if test -z "$tagname"; then
+         func_echo "unable to infer tagged configuration"
+         func_fatal_error "specify a tag with \`--tag'"
+#      else
+#        func_verbose "using $tagname tagged configuration"
+       fi
+       ;;
+      esac
+    fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+    write_libobj=${1}
+    if test "$build_libtool_libs" = yes; then
+      write_lobj=\'${2}\'
+    else
+      write_lobj=none
+    fi
+
+    if test "$build_old_libs" = yes; then
+      write_oldobj=\'${3}\'
+    else
+      write_oldobj=none
+    fi
+
+    $opt_dry_run || {
+      cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+      $MV "${write_libobj}T" "${write_libobj}"
+    }
+}
+
+
+##################################################
+# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS #
+##################################################
+
+# func_convert_core_file_wine_to_w32 ARG
+# Helper function used by file name conversion functions when $build is *nix,
+# and $host is mingw, cygwin, or some other w32 environment. Relies on a
+# correctly configured wine environment available, with the winepath program
+# in $build's $PATH.
+#
+# ARG is the $build file name to be converted to w32 format.
+# Result is available in $func_convert_core_file_wine_to_w32_result, and will
+# be empty on error (or when ARG is empty)
+func_convert_core_file_wine_to_w32 ()
+{
+  $opt_debug
+  func_convert_core_file_wine_to_w32_result="$1"
+  if test -n "$1"; then
+    # Unfortunately, winepath does not exit with a non-zero error code, so we
+    # are forced to check the contents of stdout. On the other hand, if the
+    # command is not found, the shell will set an exit code of 127 and print
+    # *an error message* to stdout. So we must check for both error code of
+    # zero AND non-empty stdout, which explains the odd construction:
+    func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null`
+    if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then
+      func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" |
+        $SED -e "$lt_sed_naive_backslashify"`
+    else
+      func_convert_core_file_wine_to_w32_result=
+    fi
+  fi
+}
+# end: func_convert_core_file_wine_to_w32
+
+
+# func_convert_core_path_wine_to_w32 ARG
+# Helper function used by path conversion functions when $build is *nix, and
+# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly
+# configured wine environment available, with the winepath program in $build's
+# $PATH. Assumes ARG has no leading or trailing path separator characters.
+#
+# ARG is path to be converted from $build format to win32.
+# Result is available in $func_convert_core_path_wine_to_w32_result.
+# Unconvertible file (directory) names in ARG are skipped; if no directory names
+# are convertible, then the result may be empty.
+func_convert_core_path_wine_to_w32 ()
+{
+  $opt_debug
+  # unfortunately, winepath doesn't convert paths, only file names
+  func_convert_core_path_wine_to_w32_result=""
+  if test -n "$1"; then
+    oldIFS=$IFS
+    IFS=:
+    for func_convert_core_path_wine_to_w32_f in $1; do
+      IFS=$oldIFS
+      func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f"
+      if test -n "$func_convert_core_file_wine_to_w32_result" ; then
+        if test -z "$func_convert_core_path_wine_to_w32_result"; then
+          func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result"
+        else
+          func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result"
+        fi
+      fi
+    done
+    IFS=$oldIFS
+  fi
+}
+# end: func_convert_core_path_wine_to_w32
+
+
+# func_cygpath ARGS...
+# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when
+# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2)
+# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or
+# (2), returns the Cygwin file name or path in func_cygpath_result (input
+# file name or path is assumed to be in w32 format, as previously converted
+# from $build's *nix or MSYS format). In case (3), returns the w32 file name
+# or path in func_cygpath_result (input file name or path is assumed to be in
+# Cygwin format). Returns an empty string on error.
+#
+# ARGS are passed to cygpath, with the last one being the file name or path to
+# be converted.
+#
+# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH
+# environment variable; do not put it in $PATH.
+func_cygpath ()
+{
+  $opt_debug
+  if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then
+    func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null`
+    if test "$?" -ne 0; then
+      # on failure, ensure result is empty
+      func_cygpath_result=
+    fi
+  else
+    func_cygpath_result=
+    func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'"
+  fi
+}
+#end: func_cygpath
+
+
+# func_convert_core_msys_to_w32 ARG
+# Convert file name or path ARG from MSYS format to w32 format.  Return
+# result in func_convert_core_msys_to_w32_result.
+func_convert_core_msys_to_w32 ()
+{
+  $opt_debug
+  # awkward: cmd appends spaces to result
+  func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null |
+    $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"`
+}
+#end: func_convert_core_msys_to_w32
+
+
+# func_convert_file_check ARG1 ARG2
+# Verify that ARG1 (a file name in $build format) was converted to $host
+# format in ARG2. Otherwise, emit an error message, but continue (resetting
+# func_to_host_file_result to ARG1).
+func_convert_file_check ()
+{
+  $opt_debug
+  if test -z "$2" && test -n "$1" ; then
+    func_error "Could not determine host file name corresponding to"
+    func_error "  \`$1'"
+    func_error "Continuing, but uninstalled executables may not work."
+    # Fallback:
+    func_to_host_file_result="$1"
+  fi
+}
+# end func_convert_file_check
+
+
+# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH
+# Verify that FROM_PATH (a path in $build format) was converted to $host
+# format in TO_PATH. Otherwise, emit an error message, but continue, resetting
+# func_to_host_file_result to a simplistic fallback value (see below).
+func_convert_path_check ()
+{
+  $opt_debug
+  if test -z "$4" && test -n "$3"; then
+    func_error "Could not determine the host path corresponding to"
+    func_error "  \`$3'"
+    func_error "Continuing, but uninstalled executables may not work."
+    # Fallback.  This is a deliberately simplistic "conversion" and
+    # should not be "improved".  See libtool.info.
+    if test "x$1" != "x$2"; then
+      lt_replace_pathsep_chars="s|$1|$2|g"
+      func_to_host_path_result=`echo "$3" |
+        $SED -e "$lt_replace_pathsep_chars"`
+    else
+      func_to_host_path_result="$3"
+    fi
+  fi
+}
+# end func_convert_path_check
+
+
+# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG
+# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT
+# and appending REPL if ORIG matches BACKPAT.
+func_convert_path_front_back_pathsep ()
+{
+  $opt_debug
+  case $4 in
+  $1 ) func_to_host_path_result="$3$func_to_host_path_result"
+    ;;
+  esac
+  case $4 in
+  $2 ) func_append func_to_host_path_result "$3"
+    ;;
+  esac
+}
+# end func_convert_path_front_back_pathsep
+
+
+##################################################
+# $build to $host FILE NAME CONVERSION FUNCTIONS #
+##################################################
+# invoked via `$to_host_file_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# Result will be available in $func_to_host_file_result.
+
+
+# func_to_host_file ARG
+# Converts the file name ARG from $build format to $host format. Return result
+# in func_to_host_file_result.
+func_to_host_file ()
+{
+  $opt_debug
+  $to_host_file_cmd "$1"
+}
+# end func_to_host_file
+
+
+# func_to_tool_file ARG LAZY
+# converts the file name ARG from $build format to toolchain format. Return
+# result in func_to_tool_file_result.  If the conversion in use is listed
+# in (the comma separated) LAZY, no conversion takes place.
+func_to_tool_file ()
+{
+  $opt_debug
+  case ,$2, in
+    *,"$to_tool_file_cmd",*)
+      func_to_tool_file_result=$1
+      ;;
+    *)
+      $to_tool_file_cmd "$1"
+      func_to_tool_file_result=$func_to_host_file_result
+      ;;
+  esac
+}
+# end func_to_tool_file
+
+
+# func_convert_file_noop ARG
+# Copy ARG to func_to_host_file_result.
+func_convert_file_noop ()
+{
+  func_to_host_file_result="$1"
+}
+# end func_convert_file_noop
+
+
+# func_convert_file_msys_to_w32 ARG
+# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper.  Returns result in
+# func_to_host_file_result.
+func_convert_file_msys_to_w32 ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    func_convert_core_msys_to_w32 "$1"
+    func_to_host_file_result="$func_convert_core_msys_to_w32_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_w32
+
+
+# func_convert_file_cygwin_to_w32 ARG
+# Convert file name ARG from Cygwin to w32 format.  Returns result in
+# func_to_host_file_result.
+func_convert_file_cygwin_to_w32 ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    # because $build is cygwin, we call "the" cygpath in $PATH; no need to use
+    # LT_CYGPATH in this case.
+    func_to_host_file_result=`cygpath -m "$1"`
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_cygwin_to_w32
+
+
+# func_convert_file_nix_to_w32 ARG
+# Convert file name ARG from *nix to w32 format.  Requires a wine environment
+# and a working winepath. Returns result in func_to_host_file_result.
+func_convert_file_nix_to_w32 ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    func_convert_core_file_wine_to_w32 "$1"
+    func_to_host_file_result="$func_convert_core_file_wine_to_w32_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_w32
+
+
+# func_convert_file_msys_to_cygwin ARG
+# Convert file name ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_file_msys_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    func_convert_core_msys_to_w32 "$1"
+    func_cygpath -u "$func_convert_core_msys_to_w32_result"
+    func_to_host_file_result="$func_cygpath_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_cygwin
+
+
+# func_convert_file_nix_to_cygwin ARG
+# Convert file name ARG from *nix to Cygwin format.  Requires Cygwin installed
+# in a wine environment, working winepath, and LT_CYGPATH set.  Returns result
+# in func_to_host_file_result.
+func_convert_file_nix_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_file_result="$1"
+  if test -n "$1"; then
+    # convert from *nix to w32, then use cygpath to convert from w32 to cygwin.
+    func_convert_core_file_wine_to_w32 "$1"
+    func_cygpath -u "$func_convert_core_file_wine_to_w32_result"
+    func_to_host_file_result="$func_cygpath_result"
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_cygwin
+
+
+#############################################
+# $build to $host PATH CONVERSION FUNCTIONS #
+#############################################
+# invoked via `$to_host_path_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# The result will be available in $func_to_host_path_result.
+#
+# Path separators are also converted from $build format to $host format.  If
+# ARG begins or ends with a path separator character, it is preserved (but
+# converted to $host format) on output.
+#
+# All path conversion functions are named using the following convention:
+#   file name conversion function    : func_convert_file_X_to_Y ()
+#   path conversion function         : func_convert_path_X_to_Y ()
+# where, for any given $build/$host combination the 'X_to_Y' value is the
+# same.  If conversion functions are added for new $build/$host combinations,
+# the two new functions must follow this pattern, or func_init_to_host_path_cmd
+# will break.
+
+
+# func_init_to_host_path_cmd
+# Ensures that function "pointer" variable $to_host_path_cmd is set to the
+# appropriate value, based on the value of $to_host_file_cmd.
+to_host_path_cmd=
+func_init_to_host_path_cmd ()
+{
+  $opt_debug
+  if test -z "$to_host_path_cmd"; then
+    func_stripname 'func_convert_file_' '' "$to_host_file_cmd"
+    to_host_path_cmd="func_convert_path_${func_stripname_result}"
+  fi
+}
+
+
+# func_to_host_path ARG
+# Converts the path ARG from $build format to $host format. Return result
+# in func_to_host_path_result.
+func_to_host_path ()
+{
+  $opt_debug
+  func_init_to_host_path_cmd
+  $to_host_path_cmd "$1"
+}
+# end func_to_host_path
+
+
+# func_convert_path_noop ARG
+# Copy ARG to func_to_host_path_result.
+func_convert_path_noop ()
+{
+  func_to_host_path_result="$1"
+}
+# end func_convert_path_noop
+
+
+# func_convert_path_msys_to_w32 ARG
+# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper.  Returns result in
+# func_to_host_path_result.
+func_convert_path_msys_to_w32 ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # Remove leading and trailing path separator characters from ARG.  MSYS
+    # behavior is inconsistent here; cygpath turns them into '.;' and ';.';
+    # and winepath ignores them completely.
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+    func_to_host_path_result="$func_convert_core_msys_to_w32_result"
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_msys_to_w32
+
+
+# func_convert_path_cygwin_to_w32 ARG
+# Convert path ARG from Cygwin to w32 format.  Returns result in
+# func_to_host_file_result.
+func_convert_path_cygwin_to_w32 ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"`
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_cygwin_to_w32
+
+
+# func_convert_path_nix_to_w32 ARG
+# Convert path ARG from *nix to w32 format.  Requires a wine environment and
+# a working winepath.  Returns result in func_to_host_file_result.
+func_convert_path_nix_to_w32 ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+    func_to_host_path_result="$func_convert_core_path_wine_to_w32_result"
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_nix_to_w32
+
+
+# func_convert_path_msys_to_cygwin ARG
+# Convert path ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_path_msys_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+    func_cygpath -u -p "$func_convert_core_msys_to_w32_result"
+    func_to_host_path_result="$func_cygpath_result"
+    func_convert_path_check : : \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+  fi
+}
+# end func_convert_path_msys_to_cygwin
+
+
+# func_convert_path_nix_to_cygwin ARG
+# Convert path ARG from *nix to Cygwin format.  Requires Cygwin installed in a
+# a wine environment, working winepath, and LT_CYGPATH set.  Returns result in
+# func_to_host_file_result.
+func_convert_path_nix_to_cygwin ()
+{
+  $opt_debug
+  func_to_host_path_result="$1"
+  if test -n "$1"; then
+    # Remove leading and trailing path separator characters from
+    # ARG. msys behavior is inconsistent here, cygpath turns them
+    # into '.;' and ';.', and winepath ignores them completely.
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+    func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result"
+    func_to_host_path_result="$func_cygpath_result"
+    func_convert_path_check : : \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+  fi
+}
+# end func_convert_path_nix_to_cygwin
+
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+    $opt_debug
+    # Get the compilation command and the source file.
+    base_compile=
+    srcfile="$nonopt"  #  always keep a non-empty value in "srcfile"
+    suppress_opt=yes
+    suppress_output=
+    arg_mode=normal
+    libobj=
+    later=
+    pie_flag=
+
+    for arg
+    do
+      case $arg_mode in
+      arg  )
+       # do not "continue".  Instead, add this to base_compile
+       lastarg="$arg"
+       arg_mode=normal
+       ;;
+
+      target )
+       libobj="$arg"
+       arg_mode=normal
+       continue
+       ;;
+
+      normal )
+       # Accept any command-line options.
+       case $arg in
+       -o)
+         test -n "$libobj" && \
+           func_fatal_error "you cannot specify \`-o' more than once"
+         arg_mode=target
+         continue
+         ;;
+
+       -pie | -fpie | -fPIE)
+          func_append pie_flag " $arg"
+         continue
+         ;;
+
+       -shared | -static | -prefer-pic | -prefer-non-pic)
+         func_append later " $arg"
+         continue
+         ;;
+
+       -no-suppress)
+         suppress_opt=no
+         continue
+         ;;
+
+       -Xcompiler)
+         arg_mode=arg  #  the next one goes into the "base_compile" arg list
+         continue      #  The current "srcfile" will either be retained or
+         ;;            #  replaced later.  I would guess that would be a bug.
+
+       -Wc,*)
+         func_stripname '-Wc,' '' "$arg"
+         args=$func_stripname_result
+         lastarg=
+         save_ifs="$IFS"; IFS=','
+         for arg in $args; do
+           IFS="$save_ifs"
+           func_append_quoted lastarg "$arg"
+         done
+         IFS="$save_ifs"
+         func_stripname ' ' '' "$lastarg"
+         lastarg=$func_stripname_result
+
+         # Add the arguments to base_compile.
+         func_append base_compile " $lastarg"
+         continue
+         ;;
+
+       *)
+         # Accept the current argument as the source file.
+         # The previous "srcfile" becomes the current argument.
+         #
+         lastarg="$srcfile"
+         srcfile="$arg"
+         ;;
+       esac  #  case $arg
+       ;;
+      esac    #  case $arg_mode
+
+      # Aesthetically quote the previous argument.
+      func_append_quoted base_compile "$lastarg"
+    done # for arg
+
+    case $arg_mode in
+    arg)
+      func_fatal_error "you must specify an argument for -Xcompile"
+      ;;
+    target)
+      func_fatal_error "you must specify a target with \`-o'"
+      ;;
+    *)
+      # Get the name of the library object.
+      test -z "$libobj" && {
+       func_basename "$srcfile"
+       libobj="$func_basename_result"
+      }
+      ;;
+    esac
+
+    # Recognize several different file suffixes.
+    # If the user specifies -o file.o, it is replaced with file.lo
+    case $libobj in
+    *.[cCFSifmso] | \
+    *.ada | *.adb | *.ads | *.asm | \
+    *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+    *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup)
+      func_xform "$libobj"
+      libobj=$func_xform_result
+      ;;
+    esac
+
+    case $libobj in
+    *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+    *)
+      func_fatal_error "cannot determine name of library object from \`$libobj'"
+      ;;
+    esac
+
+    func_infer_tag $base_compile
+
+    for arg in $later; do
+      case $arg in
+      -shared)
+       test "$build_libtool_libs" != yes && \
+         func_fatal_configuration "can not build a shared library"
+       build_old_libs=no
+       continue
+       ;;
+
+      -static)
+       build_libtool_libs=no
+       build_old_libs=yes
+       continue
+       ;;
+
+      -prefer-pic)
+       pic_mode=yes
+       continue
+       ;;
+
+      -prefer-non-pic)
+       pic_mode=no
+       continue
+       ;;
+      esac
+    done
+
+    func_quote_for_eval "$libobj"
+    test "X$libobj" != "X$func_quote_for_eval_result" \
+      && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"'   &()|`$[]' \
+      && func_warning "libobj name \`$libobj' may not contain shell special characters."
+    func_dirname_and_basename "$obj" "/" ""
+    objname="$func_basename_result"
+    xdir="$func_dirname_result"
+    lobj=${xdir}$objdir/$objname
+
+    test -z "$base_compile" && \
+      func_fatal_help "you must specify a compilation command"
+
+    # Delete any leftover library objects.
+    if test "$build_old_libs" = yes; then
+      removelist="$obj $lobj $libobj ${libobj}T"
+    else
+      removelist="$lobj $libobj ${libobj}T"
+    fi
+
+    # On Cygwin there's no "real" PIC flag so we must build both object types
+    case $host_os in
+    cygwin* | mingw* | pw32* | os2* | cegcc*)
+      pic_mode=default
+      ;;
+    esac
+    if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then
+      # non-PIC code in shared libraries is not supported
+      pic_mode=default
+    fi
+
+    # Calculate the filename of the output object if compiler does
+    # not support -o with -c
+    if test "$compiler_c_o" = no; then
+      output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext}
+      lockfile="$output_obj.lock"
+    else
+      output_obj=
+      need_locks=no
+      lockfile=
+    fi
+
+    # Lock this critical section if it is needed
+    # We use this script file to make the link, it avoids creating a new file
+    if test "$need_locks" = yes; then
+      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+       func_echo "Waiting for $lockfile to be removed"
+       sleep 2
+      done
+    elif test "$need_locks" = warn; then
+      if test -f "$lockfile"; then
+       $ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $opt_dry_run || $RM $removelist
+       exit $EXIT_FAILURE
+      fi
+      func_append removelist " $output_obj"
+      $ECHO "$srcfile" > "$lockfile"
+    fi
+
+    $opt_dry_run || $RM $removelist
+    func_append removelist " $lockfile"
+    trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+    func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
+    srcfile=$func_to_tool_file_result
+    func_quote_for_eval "$srcfile"
+    qsrcfile=$func_quote_for_eval_result
+
+    # Only build a PIC object if we are building libtool libraries.
+    if test "$build_libtool_libs" = yes; then
+      # Without this assignment, base_compile gets emptied.
+      fbsd_hideous_sh_bug=$base_compile
+
+      if test "$pic_mode" != no; then
+       command="$base_compile $qsrcfile $pic_flag"
+      else
+       # Don't build PIC code
+       command="$base_compile $qsrcfile"
+      fi
+
+      func_mkdir_p "$xdir$objdir"
+
+      if test -z "$output_obj"; then
+       # Place PIC objects in $objdir
+       func_append command " -o $lobj"
+      fi
+
+      func_show_eval_locale "$command" \
+          'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+      if test "$need_locks" = warn &&
+        test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+       $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $opt_dry_run || $RM $removelist
+       exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed, then go on to compile the next one
+      if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+       func_show_eval '$MV "$output_obj" "$lobj"' \
+         'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+      fi
+
+      # Allow error messages only from the first compilation.
+      if test "$suppress_opt" = yes; then
+       suppress_output=' >/dev/null 2>&1'
+      fi
+    fi
+
+    # Only build a position-dependent object if we build old libraries.
+    if test "$build_old_libs" = yes; then
+      if test "$pic_mode" != yes; then
+       # Don't build PIC code
+       command="$base_compile $qsrcfile$pie_flag"
+      else
+       command="$base_compile $qsrcfile $pic_flag"
+      fi
+      if test "$compiler_c_o" = yes; then
+       func_append command " -o $obj"
+      fi
+
+      # Suppress compiler output if we already did a PIC compilation.
+      func_append command "$suppress_output"
+      func_show_eval_locale "$command" \
+        '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+      if test "$need_locks" = warn &&
+        test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+       $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+       $opt_dry_run || $RM $removelist
+       exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed
+      if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+       func_show_eval '$MV "$output_obj" "$obj"' \
+         'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+      fi
+    fi
+
+    $opt_dry_run || {
+      func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+      # Unlock the critical section if it was locked
+      if test "$need_locks" != no; then
+       removelist=$lockfile
+        $RM "$lockfile"
+      fi
+    }
+
+    exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+  test "$opt_mode" = compile && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+    # We need to display help for each of the modes.
+    case $opt_mode in
+      "")
+        # Generic help is extracted from the usage comments
+        # at the start of this file.
+        func_help
+        ;;
+
+      clean)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+        ;;
+
+      compile)
+      $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+  -o OUTPUT-FILE    set the output file name to OUTPUT-FILE
+  -no-suppress      do not suppress compiler output for multiple passes
+  -prefer-pic       try to build PIC objects only
+  -prefer-non-pic   try to build non-PIC objects only
+  -shared           do not build a \`.o' file suitable for static linking
+  -static           only build a \`.o' file suitable for static linking
+  -Wc,FLAG          pass FLAG directly to the compiler
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+        ;;
+
+      execute)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+  -dlopen FILE      add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+        ;;
+
+      finish)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges.  Use
+the \`--dry-run' option if you just want to see what would be executed."
+        ;;
+
+      install)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command.  The first component should be
+either the \`install' or \`cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+  -inst-prefix-dir PREFIX-DIR  Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+        ;;
+
+      link)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+  -all-static       do not do any dynamic linking at all
+  -avoid-version    do not add a version suffix if possible
+  -bindir BINDIR    specify path to binaries directory (for systems where
+                    libraries must be found in the PATH setting at runtime)
+  -dlopen FILE      \`-dlpreopen' FILE if it cannot be dlopened at runtime
+  -dlpreopen FILE   link in FILE and add its symbols to lt_preloaded_symbols
+  -export-dynamic   allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+  -export-symbols SYMFILE
+                    try to export only the symbols listed in SYMFILE
+  -export-symbols-regex REGEX
+                    try to export only the symbols matching REGEX
+  -LLIBDIR          search LIBDIR for required installed libraries
+  -lNAME            OUTPUT-FILE requires the installed library libNAME
+  -module           build a library that can dlopened
+  -no-fast-install  disable the fast-install mode
+  -no-install       link a not-installable executable
+  -no-undefined     declare that a library does not refer to external symbols
+  -o OUTPUT-FILE    create OUTPUT-FILE from the specified objects
+  -objectlist FILE  Use a list of object files found in FILE to specify objects
+  -precious-files-regex REGEX
+                    don't remove output files matching REGEX
+  -release RELEASE  specify package release information
+  -rpath LIBDIR     the created library will eventually be installed in LIBDIR
+  -R[ ]LIBDIR       add LIBDIR to the runtime path of programs and libraries
+  -shared           only do dynamic linking of libtool libraries
+  -shrext SUFFIX    override the standard shared library file extension
+  -static           do not do any dynamic linking of uninstalled libtool libraries
+  -static-libtool-libs
+                    do not do any dynamic linking of libtool libraries
+  -version-info CURRENT[:REVISION[:AGE]]
+                    specify library version info [each variable defaults to 0]
+  -weak LIBNAME     declare that the target provides the LIBNAME interface
+  -Wc,FLAG
+  -Xcompiler FLAG   pass linker-specific FLAG directly to the compiler
+  -Wl,FLAG
+  -Xlinker FLAG     pass linker-specific FLAG directly to the linker
+  -XCClinker FLAG   pass link-specific FLAG to the compiler driver (CC)
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename.  Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+        ;;
+
+      uninstall)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm').  RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+        ;;
+
+      *)
+        func_fatal_help "invalid operation mode \`$opt_mode'"
+        ;;
+    esac
+
+    echo
+    $ECHO "Try \`$progname --help' for more information about other modes."
+}
+
+# Now that we've collected a possible --mode arg, show help if necessary
+if $opt_help; then
+  if test "$opt_help" = :; then
+    func_mode_help
+  else
+    {
+      func_help noexit
+      for opt_mode in compile link execute install finish uninstall clean; do
+       func_mode_help
+      done
+    } | sed -n '1p; 2,$s/^Usage:/  or: /p'
+    {
+      func_help noexit
+      for opt_mode in compile link execute install finish uninstall clean; do
+       echo
+       func_mode_help
+      done
+    } |
+    sed '1d
+      /^When reporting/,/^Report/{
+       H
+       d
+      }
+      $x
+      /information about other modes/d
+      /more detailed .*MODE/d
+      s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/'
+  fi
+  exit $?
+fi
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+    $opt_debug
+    # The first argument is the command name.
+    cmd="$nonopt"
+    test -z "$cmd" && \
+      func_fatal_help "you must specify a COMMAND"
+
+    # Handle -dlopen flags immediately.
+    for file in $opt_dlopen; do
+      test -f "$file" \
+       || func_fatal_help "\`$file' is not a file"
+
+      dir=
+      case $file in
+      *.la)
+       func_resolve_sysroot "$file"
+       file=$func_resolve_sysroot_result
+
+       # Check to see that this really is a libtool archive.
+       func_lalib_unsafe_p "$file" \
+         || func_fatal_help "\`$lib' is not a valid libtool archive"
+
+       # Read the libtool library.
+       dlname=
+       library_names=
+       func_source "$file"
+
+       # Skip this library if it cannot be dlopened.
+       if test -z "$dlname"; then
+         # Warn if it was a shared library.
+         test -n "$library_names" && \
+           func_warning "\`$file' was not linked with \`-export-dynamic'"
+         continue
+       fi
+
+       func_dirname "$file" "" "."
+       dir="$func_dirname_result"
+
+       if test -f "$dir/$objdir/$dlname"; then
+         func_append dir "/$objdir"
+       else
+         if test ! -f "$dir/$dlname"; then
+           func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'"
+         fi
+       fi
+       ;;
+
+      *.lo)
+       # Just add the directory containing the .lo file.
+       func_dirname "$file" "" "."
+       dir="$func_dirname_result"
+       ;;
+
+      *)
+       func_warning "\`-dlopen' is ignored for non-libtool libraries and objects"
+       continue
+       ;;
+      esac
+
+      # Get the absolute pathname.
+      absdir=`cd "$dir" && pwd`
+      test -n "$absdir" && dir="$absdir"
+
+      # Now add the directory to shlibpath_var.
+      if eval "test -z \"\$$shlibpath_var\""; then
+       eval "$shlibpath_var=\"\$dir\""
+      else
+       eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+      fi
+    done
+
+    # This variable tells wrapper scripts just to set shlibpath_var
+    # rather than running their programs.
+    libtool_execute_magic="$magic"
+
+    # Check if any of the arguments is a wrapper script.
+    args=
+    for file
+    do
+      case $file in
+      -* | *.la | *.lo ) ;;
+      *)
+       # Do a test to see if this is really a libtool program.
+       if func_ltwrapper_script_p "$file"; then
+         func_source "$file"
+         # Transform arg to wrapped name.
+         file="$progdir/$program"
+       elif func_ltwrapper_executable_p "$file"; then
+         func_ltwrapper_scriptname "$file"
+         func_source "$func_ltwrapper_scriptname_result"
+         # Transform arg to wrapped name.
+         file="$progdir/$program"
+       fi
+       ;;
+      esac
+      # Quote arguments (to preserve shell metacharacters).
+      func_append_quoted args "$file"
+    done
+
+    if test "X$opt_dry_run" = Xfalse; then
+      if test -n "$shlibpath_var"; then
+       # Export the shlibpath_var.
+       eval "export $shlibpath_var"
+      fi
+
+      # Restore saved environment variables
+      for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+      do
+       eval "if test \"\${save_$lt_var+set}\" = set; then
+                $lt_var=\$save_$lt_var; export $lt_var
+             else
+               $lt_unset $lt_var
+             fi"
+      done
+
+      # Now prepare to actually exec the command.
+      exec_cmd="\$cmd$args"
+    else
+      # Display what would be done.
+      if test -n "$shlibpath_var"; then
+       eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+       echo "export $shlibpath_var"
+      fi
+      $ECHO "$cmd$args"
+      exit $EXIT_SUCCESS
+    fi
+}
+
+test "$opt_mode" = execute && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+    $opt_debug
+    libs=
+    libdirs=
+    admincmds=
+
+    for opt in "$nonopt" ${1+"$@"}
+    do
+      if test -d "$opt"; then
+       func_append libdirs " $opt"
+
+      elif test -f "$opt"; then
+       if func_lalib_unsafe_p "$opt"; then
+         func_append libs " $opt"
+       else
+         func_warning "\`$opt' is not a valid libtool archive"
+       fi
+
+      else
+       func_fatal_error "invalid argument \`$opt'"
+      fi
+    done
+
+    if test -n "$libs"; then
+      if test -n "$lt_sysroot"; then
+        sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"`
+        sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;"
+      else
+        sysroot_cmd=
+      fi
+
+      # Remove sysroot references
+      if $opt_dry_run; then
+        for lib in $libs; do
+          echo "removing references to $lt_sysroot and \`=' prefixes from $lib"
+        done
+      else
+        tmpdir=`func_mktempdir`
+        for lib in $libs; do
+         sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
+           > $tmpdir/tmp-la
+         mv -f $tmpdir/tmp-la $lib
+       done
+        ${RM}r "$tmpdir"
+      fi
+    fi
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      for libdir in $libdirs; do
+       if test -n "$finish_cmds"; then
+         # Do each command in the finish commands.
+         func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+       fi
+       if test -n "$finish_eval"; then
+         # Do the single finish_eval.
+         eval cmds=\"$finish_eval\"
+         $opt_dry_run || eval "$cmds" || func_append admincmds "
+       $cmds"
+       fi
+      done
+    fi
+
+    # Exit here if they wanted silent mode.
+    $opt_silent && exit $EXIT_SUCCESS
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      echo "----------------------------------------------------------------------"
+      echo "Libraries have been installed in:"
+      for libdir in $libdirs; do
+       $ECHO "   $libdir"
+      done
+      echo
+      echo "If you ever happen to want to link against installed libraries"
+      echo "in a given directory, LIBDIR, you must either use libtool, and"
+      echo "specify the full pathname of the library, or use the \`-LLIBDIR'"
+      echo "flag during linking and do at least one of the following:"
+      if test -n "$shlibpath_var"; then
+       echo "   - add LIBDIR to the \`$shlibpath_var' environment variable"
+       echo "     during execution"
+      fi
+      if test -n "$runpath_var"; then
+       echo "   - add LIBDIR to the \`$runpath_var' environment variable"
+       echo "     during linking"
+      fi
+      if test -n "$hardcode_libdir_flag_spec"; then
+       libdir=LIBDIR
+       eval flag=\"$hardcode_libdir_flag_spec\"
+
+       $ECHO "   - use the \`$flag' linker flag"
+      fi
+      if test -n "$admincmds"; then
+       $ECHO "   - have your system administrator run these commands:$admincmds"
+      fi
+      if test -f /etc/ld.so.conf; then
+       echo "   - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+      fi
+      echo
+
+      echo "See any operating system documentation about shared libraries for"
+      case $host in
+       solaris2.[6789]|solaris2.1[0-9])
+         echo "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+         echo "pages."
+         ;;
+       *)
+         echo "more information, such as the ld(1) and ld.so(8) manual pages."
+         ;;
+      esac
+      echo "----------------------------------------------------------------------"
+    fi
+    exit $EXIT_SUCCESS
+}
+
+test "$opt_mode" = finish && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+    $opt_debug
+    # There may be an optional sh(1) argument at the beginning of
+    # install_prog (especially on Windows NT).
+    if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh ||
+       # Allow the use of GNU shtool's install command.
+       case $nonopt in *shtool*) :;; *) false;; esac; then
+      # Aesthetically quote it.
+      func_quote_for_eval "$nonopt"
+      install_prog="$func_quote_for_eval_result "
+      arg=$1
+      shift
+    else
+      install_prog=
+      arg=$nonopt
+    fi
+
+    # The real first argument should be the name of the installation program.
+    # Aesthetically quote it.
+    func_quote_for_eval "$arg"
+    func_append install_prog "$func_quote_for_eval_result"
+    install_shared_prog=$install_prog
+    case " $install_prog " in
+      *[\\\ /]cp\ *) install_cp=: ;;
+      *) install_cp=false ;;
+    esac
+
+    # We need to accept at least all the BSD install flags.
+    dest=
+    files=
+    opts=
+    prev=
+    install_type=
+    isdir=no
+    stripme=
+    no_mode=:
+    for arg
+    do
+      arg2=
+      if test -n "$dest"; then
+       func_append files " $dest"
+       dest=$arg
+       continue
+      fi
+
+      case $arg in
+      -d) isdir=yes ;;
+      -f)
+       if $install_cp; then :; else
+         prev=$arg
+       fi
+       ;;
+      -g | -m | -o)
+       prev=$arg
+       ;;
+      -s)
+       stripme=" -s"
+       continue
+       ;;
+      -*)
+       ;;
+      *)
+       # If the previous option needed an argument, then skip it.
+       if test -n "$prev"; then
+         if test "x$prev" = x-m && test -n "$install_override_mode"; then
+           arg2=$install_override_mode
+           no_mode=false
+         fi
+         prev=
+       else
+         dest=$arg
+         continue
+       fi
+       ;;
+      esac
+
+      # Aesthetically quote the argument.
+      func_quote_for_eval "$arg"
+      func_append install_prog " $func_quote_for_eval_result"
+      if test -n "$arg2"; then
+       func_quote_for_eval "$arg2"
+      fi
+      func_append install_shared_prog " $func_quote_for_eval_result"
+    done
+
+    test -z "$install_prog" && \
+      func_fatal_help "you must specify an install program"
+
+    test -n "$prev" && \
+      func_fatal_help "the \`$prev' option requires an argument"
+
+    if test -n "$install_override_mode" && $no_mode; then
+      if $install_cp; then :; else
+       func_quote_for_eval "$install_override_mode"
+       func_append install_shared_prog " -m $func_quote_for_eval_result"
+      fi
+    fi
+
+    if test -z "$files"; then
+      if test -z "$dest"; then
+       func_fatal_help "no file or destination specified"
+      else
+       func_fatal_help "you must specify a destination"
+      fi
+    fi
+
+    # Strip any trailing slash from the destination.
+    func_stripname '' '/' "$dest"
+    dest=$func_stripname_result
+
+    # Check to see that the destination is a directory.
+    test -d "$dest" && isdir=yes
+    if test "$isdir" = yes; then
+      destdir="$dest"
+      destname=
+    else
+      func_dirname_and_basename "$dest" "" "."
+      destdir="$func_dirname_result"
+      destname="$func_basename_result"
+
+      # Not a directory, so check to see that there is only one file specified.
+      set dummy $files; shift
+      test "$#" -gt 1 && \
+       func_fatal_help "\`$dest' is not a directory"
+    fi
+    case $destdir in
+    [\\/]* | [A-Za-z]:[\\/]*) ;;
+    *)
+      for file in $files; do
+       case $file in
+       *.lo) ;;
+       *)
+         func_fatal_help "\`$destdir' must be an absolute directory name"
+         ;;
+       esac
+      done
+      ;;
+    esac
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    staticlibs=
+    future_libdirs=
+    current_libdirs=
+    for file in $files; do
+
+      # Do each installation.
+      case $file in
+      *.$libext)
+       # Do the static libraries later.
+       func_append staticlibs " $file"
+       ;;
+
+      *.la)
+       func_resolve_sysroot "$file"
+       file=$func_resolve_sysroot_result
+
+       # Check to see that this really is a libtool archive.
+       func_lalib_unsafe_p "$file" \
+         || func_fatal_help "\`$file' is not a valid libtool archive"
+
+       library_names=
+       old_library=
+       relink_command=
+       func_source "$file"
+
+       # Add the libdir to current_libdirs if it is the destination.
+       if test "X$destdir" = "X$libdir"; then
+         case "$current_libdirs " in
+         *" $libdir "*) ;;
+         *) func_append current_libdirs " $libdir" ;;
+         esac
+       else
+         # Note the libdir as a future libdir.
+         case "$future_libdirs " in
+         *" $libdir "*) ;;
+         *) func_append future_libdirs " $libdir" ;;
+         esac
+       fi
+
+       func_dirname "$file" "/" ""
+       dir="$func_dirname_result"
+       func_append dir "$objdir"
+
+       if test -n "$relink_command"; then
+         # Determine the prefix the user has applied to our future dir.
+         inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"`
+
+         # Don't allow the user to place us outside of our expected
+         # location b/c this prevents finding dependent libraries that
+         # are installed to the same prefix.
+         # At present, this check doesn't affect windows .dll's that
+         # are installed into $libdir/../bin (currently, that works fine)
+         # but it's something to keep an eye on.
+         test "$inst_prefix_dir" = "$destdir" && \
+           func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir"
+
+         if test -n "$inst_prefix_dir"; then
+           # Stick the inst_prefix_dir data into the link command.
+           relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+         else
+           relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"`
+         fi
+
+         func_warning "relinking \`$file'"
+         func_show_eval "$relink_command" \
+           'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"'
+       fi
+
+       # See the names of the shared library.
+       set dummy $library_names; shift
+       if test -n "$1"; then
+         realname="$1"
+         shift
+
+         srcname="$realname"
+         test -n "$relink_command" && srcname="$realname"T
+
+         # Install the shared library and build the symlinks.
+         func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \
+             'exit $?'
+         tstripme="$stripme"
+         case $host_os in
+         cygwin* | mingw* | pw32* | cegcc*)
+           case $realname in
+           *.dll.a)
+             tstripme=""
+             ;;
+           esac
+           ;;
+         esac
+         if test -n "$tstripme" && test -n "$striplib"; then
+           func_show_eval "$striplib $destdir/$realname" 'exit $?'
+         fi
+
+         if test "$#" -gt 0; then
+           # Delete the old symlinks, and create new ones.
+           # Try `ln -sf' first, because the `ln' binary might depend on
+           # the symlink we replace!  Solaris /bin/ln does not understand -f,
+           # so we also need to try rm && ln -s.
+           for linkname
+           do
+             test "$linkname" != "$realname" \
+               && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+           done
+         fi
+
+         # Do each command in the postinstall commands.
+         lib="$destdir/$realname"
+         func_execute_cmds "$postinstall_cmds" 'exit $?'
+       fi
+
+       # Install the pseudo-library for information purposes.
+       func_basename "$file"
+       name="$func_basename_result"
+       instname="$dir/$name"i
+       func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+       # Maybe install the static library, too.
+       test -n "$old_library" && func_append staticlibs " $dir/$old_library"
+       ;;
+
+      *.lo)
+       # Install (i.e. copy) a libtool object.
+
+       # Figure out destination file name, if it wasn't already specified.
+       if test -n "$destname"; then
+         destfile="$destdir/$destname"
+       else
+         func_basename "$file"
+         destfile="$func_basename_result"
+         destfile="$destdir/$destfile"
+       fi
+
+       # Deduce the name of the destination old-style object file.
+       case $destfile in
+       *.lo)
+         func_lo2o "$destfile"
+         staticdest=$func_lo2o_result
+         ;;
+       *.$objext)
+         staticdest="$destfile"
+         destfile=
+         ;;
+       *)
+         func_fatal_help "cannot copy a libtool object to \`$destfile'"
+         ;;
+       esac
+
+       # Install the libtool object if requested.
+       test -n "$destfile" && \
+         func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+       # Install the old object if enabled.
+       if test "$build_old_libs" = yes; then
+         # Deduce the name of the old-style object file.
+         func_lo2o "$file"
+         staticobj=$func_lo2o_result
+         func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+       fi
+       exit $EXIT_SUCCESS
+       ;;
+
+      *)
+       # Figure out destination file name, if it wasn't already specified.
+       if test -n "$destname"; then
+         destfile="$destdir/$destname"
+       else
+         func_basename "$file"
+         destfile="$func_basename_result"
+         destfile="$destdir/$destfile"
+       fi
+
+       # If the file is missing, and there is a .exe on the end, strip it
+       # because it is most likely a libtool script we actually want to
+       # install
+       stripped_ext=""
+       case $file in
+         *.exe)
+           if test ! -f "$file"; then
+             func_stripname '' '.exe' "$file"
+             file=$func_stripname_result
+             stripped_ext=".exe"
+           fi
+           ;;
+       esac
+
+       # Do a test to see if this is really a libtool program.
+       case $host in
+       *cygwin* | *mingw*)
+           if func_ltwrapper_executable_p "$file"; then
+             func_ltwrapper_scriptname "$file"
+             wrapper=$func_ltwrapper_scriptname_result
+           else
+             func_stripname '' '.exe' "$file"
+             wrapper=$func_stripname_result
+           fi
+           ;;
+       *)
+           wrapper=$file
+           ;;
+       esac
+       if func_ltwrapper_script_p "$wrapper"; then
+         notinst_deplibs=
+         relink_command=
+
+         func_source "$wrapper"
+
+         # Check the variables that should have been set.
+         test -z "$generated_by_libtool_version" && \
+           func_fatal_error "invalid libtool wrapper script \`$wrapper'"
+
+         finalize=yes
+         for lib in $notinst_deplibs; do
+           # Check to see that each library is installed.
+           libdir=
+           if test -f "$lib"; then
+             func_source "$lib"
+           fi
+           libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test
+           if test -n "$libdir" && test ! -f "$libfile"; then
+             func_warning "\`$lib' has not been installed in \`$libdir'"
+             finalize=no
+           fi
+         done
+
+         relink_command=
+         func_source "$wrapper"
+
+         outputname=
+         if test "$fast_install" = no && test -n "$relink_command"; then
+           $opt_dry_run || {
+             if test "$finalize" = yes; then
+               tmpdir=`func_mktempdir`
+               func_basename "$file$stripped_ext"
+               file="$func_basename_result"
+               outputname="$tmpdir/$file"
+               # Replace the output file specification.
+               relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
+
+               $opt_silent || {
+                 func_quote_for_expand "$relink_command"
+                 eval "func_echo $func_quote_for_expand_result"
+               }
+               if eval "$relink_command"; then :
+                 else
+                 func_error "error: relink \`$file' with the above command before installing it"
+                 $opt_dry_run || ${RM}r "$tmpdir"
+                 continue
+               fi
+               file="$outputname"
+             else
+               func_warning "cannot relink \`$file'"
+             fi
+           }
+         else
+           # Install the binary that we compiled earlier.
+           file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"`
+         fi
+       fi
+
+       # remove .exe since cygwin /usr/bin/install will append another
+       # one anyway
+       case $install_prog,$host in
+       */usr/bin/install*,*cygwin*)
+         case $file:$destfile in
+         *.exe:*.exe)
+           # this is ok
+           ;;
+         *.exe:*)
+           destfile=$destfile.exe
+           ;;
+         *:*.exe)
+           func_stripname '' '.exe' "$destfile"
+           destfile=$func_stripname_result
+           ;;
+         esac
+         ;;
+       esac
+       func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+       $opt_dry_run || if test -n "$outputname"; then
+         ${RM}r "$tmpdir"
+       fi
+       ;;
+      esac
+    done
+
+    for file in $staticlibs; do
+      func_basename "$file"
+      name="$func_basename_result"
+
+      # Set up the ranlib parameters.
+      oldlib="$destdir/$name"
+      func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+      tool_oldlib=$func_to_tool_file_result
+
+      func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+      if test -n "$stripme" && test -n "$old_striplib"; then
+       func_show_eval "$old_striplib $tool_oldlib" 'exit $?'
+      fi
+
+      # Do each command in the postinstall commands.
+      func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+    done
+
+    test -n "$future_libdirs" && \
+      func_warning "remember to run \`$progname --finish$future_libdirs'"
+
+    if test -n "$current_libdirs"; then
+      # Maybe just do a dry run.
+      $opt_dry_run && current_libdirs=" -n$current_libdirs"
+      exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs'
+    else
+      exit $EXIT_SUCCESS
+    fi
+}
+
+test "$opt_mode" = install && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+    $opt_debug
+    my_outputname="$1"
+    my_originator="$2"
+    my_pic_p="${3-no}"
+    my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'`
+    my_dlsyms=
+
+    if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+      if test -n "$NM" && test -n "$global_symbol_pipe"; then
+       my_dlsyms="${my_outputname}S.c"
+      else
+       func_error "not configured to extract global symbols from dlpreopened files"
+      fi
+    fi
+
+    if test -n "$my_dlsyms"; then
+      case $my_dlsyms in
+      "") ;;
+      *.c)
+       # Discover the nlist of each of the dlfiles.
+       nlist="$output_objdir/${my_outputname}.nm"
+
+       func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+       # Parse the name list into a source file.
+       func_verbose "creating $output_objdir/$my_dlsyms"
+
+       $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
+#endif
+
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
+/* DATA imports from DLLs on WIN32 con't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT_DLSYM_CONST
+#elif defined(__osf__)
+/* This system does not cope well with relocations in const data.  */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+/* External symbol declarations for the compiler. */\
+"
+
+       if test "$dlself" = yes; then
+         func_verbose "generating symbol list for \`$output'"
+
+         $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+         # Add our own program objects to the symbol list.
+         progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+         for progfile in $progfiles; do
+           func_to_tool_file "$progfile" func_convert_file_msys_to_w32
+           func_verbose "extracting global C symbols from \`$func_to_tool_file_result'"
+           $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'"
+         done
+
+         if test -n "$exclude_expsyms"; then
+           $opt_dry_run || {
+             eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+             eval '$MV "$nlist"T "$nlist"'
+           }
+         fi
+
+         if test -n "$export_symbols_regex"; then
+           $opt_dry_run || {
+             eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+             eval '$MV "$nlist"T "$nlist"'
+           }
+         fi
+
+         # Prepare the list of exported symbols
+         if test -z "$export_symbols"; then
+           export_symbols="$output_objdir/$outputname.exp"
+           $opt_dry_run || {
+             $RM $export_symbols
+             eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+             case $host in
+             *cygwin* | *mingw* | *cegcc* )
+                eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+                eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+               ;;
+             esac
+           }
+         else
+           $opt_dry_run || {
+             eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+             eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+             eval '$MV "$nlist"T "$nlist"'
+             case $host in
+               *cygwin* | *mingw* | *cegcc* )
+                 eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+                 eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+                 ;;
+             esac
+           }
+         fi
+       fi
+
+       for dlprefile in $dlprefiles; do
+         func_verbose "extracting global C symbols from \`$dlprefile'"
+         func_basename "$dlprefile"
+         name="$func_basename_result"
+          case $host in
+           *cygwin* | *mingw* | *cegcc* )
+             # if an import library, we need to obtain dlname
+             if func_win32_import_lib_p "$dlprefile"; then
+               func_tr_sh "$dlprefile"
+               eval "curr_lafile=\$libfile_$func_tr_sh_result"
+               dlprefile_dlbasename=""
+               if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then
+                 # Use subshell, to avoid clobbering current variable values
+                 dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"`
+                 if test -n "$dlprefile_dlname" ; then
+                   func_basename "$dlprefile_dlname"
+                   dlprefile_dlbasename="$func_basename_result"
+                 else
+                   # no lafile. user explicitly requested -dlpreopen <import library>.
+                   $sharedlib_from_linklib_cmd "$dlprefile"
+                   dlprefile_dlbasename=$sharedlib_from_linklib_result
+                 fi
+               fi
+               $opt_dry_run || {
+                 if test -n "$dlprefile_dlbasename" ; then
+                   eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"'
+                 else
+                   func_warning "Could not compute DLL name from $name"
+                   eval '$ECHO ": $name " >> "$nlist"'
+                 fi
+                 func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+                 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
+                   $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
+               }
+             else # not an import lib
+               $opt_dry_run || {
+                 eval '$ECHO ": $name " >> "$nlist"'
+                 func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+                 eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+               }
+             fi
+           ;;
+           *)
+             $opt_dry_run || {
+               eval '$ECHO ": $name " >> "$nlist"'
+               func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+               eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+             }
+           ;;
+          esac
+       done
+
+       $opt_dry_run || {
+         # Make sure we have at least an empty file.
+         test -f "$nlist" || : > "$nlist"
+
+         if test -n "$exclude_expsyms"; then
+           $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+           $MV "$nlist"T "$nlist"
+         fi
+
+         # Try sorting and uniquifying the output.
+         if $GREP -v "^: " < "$nlist" |
+             if sort -k 3 </dev/null >/dev/null 2>&1; then
+               sort -k 3
+             else
+               sort +2
+             fi |
+             uniq > "$nlist"S; then
+           :
+         else
+           $GREP -v "^: " < "$nlist" > "$nlist"S
+         fi
+
+         if test -f "$nlist"S; then
+           eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+         else
+           echo '/* NONE */' >> "$output_objdir/$my_dlsyms"
+         fi
+
+         echo >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols.  */
+typedef struct {
+  const char *name;
+  void *address;
+} lt_dlsymlist;
+extern LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];
+LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{\
+  { \"$my_originator\", (void *) 0 },"
+
+         case $need_lib_prefix in
+         no)
+           eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+           ;;
+         *)
+           eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+           ;;
+         esac
+         echo >> "$output_objdir/$my_dlsyms" "\
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+       } # !$opt_dry_run
+
+       pic_flag_for_symtable=
+       case "$compile_command " in
+       *" -static "*) ;;
+       *)
+         case $host in
+         # compiling the symbol table file with pic_flag works around
+         # a FreeBSD bug that causes programs to crash when -lm is
+         # linked before any other PIC object.  But we must not use
+         # pic_flag when linking with -static.  The problem exists in
+         # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+         *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+           pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+         *-*-hpux*)
+           pic_flag_for_symtable=" $pic_flag"  ;;
+         *)
+           if test "X$my_pic_p" != Xno; then
+             pic_flag_for_symtable=" $pic_flag"
+           fi
+           ;;
+         esac
+         ;;
+       esac
+       symtab_cflags=
+       for arg in $LTCFLAGS; do
+         case $arg in
+         -pie | -fpie | -fPIE) ;;
+         *) func_append symtab_cflags " $arg" ;;
+         esac
+       done
+
+       # Now compile the dynamic symbol file.
+       func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+       # Clean up the generated files.
+       func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"'
+
+       # Transform the symbol file into the correct name.
+       symfileobj="$output_objdir/${my_outputname}S.$objext"
+       case $host in
+       *cygwin* | *mingw* | *cegcc* )
+         if test -f "$output_objdir/$my_outputname.def"; then
+           compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+           finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+         else
+           compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+           finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+         fi
+         ;;
+       *)
+         compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+         finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+         ;;
+       esac
+       ;;
+      *)
+       func_fatal_error "unknown suffix for \`$my_dlsyms'"
+       ;;
+      esac
+    else
+      # We keep going just in case the user didn't refer to
+      # lt_preloaded_symbols.  The linker will fail if global_symbol_pipe
+      # really was required.
+
+      # Nullify the symbol file.
+      compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"`
+      finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"`
+    fi
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+# Despite the name, also deal with 64 bit binaries.
+func_win32_libid ()
+{
+  $opt_debug
+  win32_libid_type="unknown"
+  win32_fileres=`file -L $1 2>/dev/null`
+  case $win32_fileres in
+  *ar\ archive\ import\ library*) # definitely import
+    win32_libid_type="x86 archive import"
+    ;;
+  *ar\ archive*) # could be an import, or static
+    # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
+    if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+       $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
+      func_to_tool_file "$1" func_convert_file_msys_to_w32
+      win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
+       $SED -n -e '
+           1,100{
+               / I /{
+                   s,.*,import,
+                   p
+                   q
+               }
+           }'`
+      case $win32_nmres in
+      import*)  win32_libid_type="x86 archive import";;
+      *)        win32_libid_type="x86 archive static";;
+      esac
+    fi
+    ;;
+  *DLL*)
+    win32_libid_type="x86 DLL"
+    ;;
+  *executable*) # but shell scripts are "executable" too...
+    case $win32_fileres in
+    *MS\ Windows\ PE\ Intel*)
+      win32_libid_type="x86 DLL"
+      ;;
+    esac
+    ;;
+  esac
+  $ECHO "$win32_libid_type"
+}
+
+# func_cygming_dll_for_implib ARG
+#
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+# Invoked by eval'ing the libtool variable
+#    $sharedlib_from_linklib_cmd
+# Result is available in the variable
+#    $sharedlib_from_linklib_result
+func_cygming_dll_for_implib ()
+{
+  $opt_debug
+  sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"`
+}
+
+# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs
+#
+# The is the core of a fallback implementation of a
+# platform-specific function to extract the name of the
+# DLL associated with the specified import library LIBNAME.
+#
+# SECTION_NAME is either .idata$6 or .idata$7, depending
+# on the platform and compiler that created the implib.
+#
+# Echos the name of the DLL associated with the
+# specified import library.
+func_cygming_dll_for_implib_fallback_core ()
+{
+  $opt_debug
+  match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"`
+  $OBJDUMP -s --section "$1" "$2" 2>/dev/null |
+    $SED '/^Contents of section '"$match_literal"':/{
+      # Place marker at beginning of archive member dllname section
+      s/.*/====MARK====/
+      p
+      d
+    }
+    # These lines can sometimes be longer than 43 characters, but
+    # are always uninteresting
+    /:[         ]*file format pe[i]\{,1\}-/d
+    /^In archive [^:]*:/d
+    # Ensure marker is printed
+    /^====MARK====/p
+    # Remove all lines with less than 43 characters
+    /^.\{43\}/!d
+    # From remaining lines, remove first 43 characters
+    s/^.\{43\}//' |
+    $SED -n '
+      # Join marker and all lines until next marker into a single line
+      /^====MARK====/ b para
+      H
+      $ b para
+      b
+      :para
+      x
+      s/\n//g
+      # Remove the marker
+      s/^====MARK====//
+      # Remove trailing dots and whitespace
+      s/[\. \t]*$//
+      # Print
+      /./p' |
+    # we now have a list, one entry per line, of the stringified
+    # contents of the appropriate section of all members of the
+    # archive which possess that section. Heuristic: eliminate
+    # all those which have a first or second character that is
+    # a '.' (that is, objdump's representation of an unprintable
+    # character.) This should work for all archives with less than
+    # 0x302f exports -- but will fail for DLLs whose name actually
+    # begins with a literal '.' or a single character followed by
+    # a '.'.
+    #
+    # Of those that remain, print the first one.
+    $SED -e '/^\./d;/^.\./d;q'
+}
+
+# func_cygming_gnu_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is a GNU/binutils-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_gnu_implib_p ()
+{
+  $opt_debug
+  func_to_tool_file "$1" func_convert_file_msys_to_w32
+  func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
+  test -n "$func_cygming_gnu_implib_tmp"
+}
+
+# func_cygming_ms_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is an MS-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_ms_implib_p ()
+{
+  $opt_debug
+  func_to_tool_file "$1" func_convert_file_msys_to_w32
+  func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
+  test -n "$func_cygming_ms_implib_tmp"
+}
+
+# func_cygming_dll_for_implib_fallback ARG
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+#
+# This fallback implementation is for use when $DLLTOOL
+# does not support the --identify-strict option.
+# Invoked by eval'ing the libtool variable
+#    $sharedlib_from_linklib_cmd
+# Result is available in the variable
+#    $sharedlib_from_linklib_result
+func_cygming_dll_for_implib_fallback ()
+{
+  $opt_debug
+  if func_cygming_gnu_implib_p "$1" ; then
+    # binutils import library
+    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"`
+  elif func_cygming_ms_implib_p "$1" ; then
+    # ms-generated import library
+    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"`
+  else
+    # unknown
+    sharedlib_from_linklib_result=""
+  fi
+}
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+    $opt_debug
+    f_ex_an_ar_dir="$1"; shift
+    f_ex_an_ar_oldlib="$1"
+    if test "$lock_old_archive_extraction" = yes; then
+      lockfile=$f_ex_an_ar_oldlib.lock
+      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+       func_echo "Waiting for $lockfile to be removed"
+       sleep 2
+      done
+    fi
+    func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \
+                  'stat=$?; rm -f "$lockfile"; exit $stat'
+    if test "$lock_old_archive_extraction" = yes; then
+      $opt_dry_run || rm -f "$lockfile"
+    fi
+    if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+     :
+    else
+      func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+    fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+    $opt_debug
+    my_gentop="$1"; shift
+    my_oldlibs=${1+"$@"}
+    my_oldobjs=""
+    my_xlib=""
+    my_xabs=""
+    my_xdir=""
+
+    for my_xlib in $my_oldlibs; do
+      # Extract the objects.
+      case $my_xlib in
+       [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;;
+       *) my_xabs=`pwd`"/$my_xlib" ;;
+      esac
+      func_basename "$my_xlib"
+      my_xlib="$func_basename_result"
+      my_xlib_u=$my_xlib
+      while :; do
+        case " $extracted_archives " in
+       *" $my_xlib_u "*)
+         func_arith $extracted_serial + 1
+         extracted_serial=$func_arith_result
+         my_xlib_u=lt$extracted_serial-$my_xlib ;;
+       *) break ;;
+       esac
+      done
+      extracted_archives="$extracted_archives $my_xlib_u"
+      my_xdir="$my_gentop/$my_xlib_u"
+
+      func_mkdir_p "$my_xdir"
+
+      case $host in
+      *-darwin*)
+       func_verbose "Extracting $my_xabs"
+       # Do not bother doing anything if just a dry run
+       $opt_dry_run || {
+         darwin_orig_dir=`pwd`
+         cd $my_xdir || exit $?
+         darwin_archive=$my_xabs
+         darwin_curdir=`pwd`
+         darwin_base_archive=`basename "$darwin_archive"`
+         darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+         if test -n "$darwin_arches"; then
+           darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+           darwin_arch=
+           func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+           for darwin_arch in  $darwin_arches ; do
+             func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+             $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}"
+             cd "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+             func_extract_an_archive "`pwd`" "${darwin_base_archive}"
+             cd "$darwin_curdir"
+             $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}"
+           done # $darwin_arches
+            ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+           darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u`
+           darwin_file=
+           darwin_files=
+           for darwin_file in $darwin_filelist; do
+             darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`
+             $LIPO -create -output "$darwin_file" $darwin_files
+           done # $darwin_filelist
+           $RM -rf unfat-$$
+           cd "$darwin_orig_dir"
+         else
+           cd $darwin_orig_dir
+           func_extract_an_archive "$my_xdir" "$my_xabs"
+         fi # $darwin_arches
+       } # !$opt_dry_run
+       ;;
+      *)
+        func_extract_an_archive "$my_xdir" "$my_xabs"
+       ;;
+      esac
+      my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
+    done
+
+    func_extract_archives_result="$my_oldobjs"
+}
+
+
+# func_emit_wrapper [arg=no]
+#
+# Emit a libtool wrapper script on stdout.
+# Don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable.  Must ONLY be called from within
+# func_mode_link because it depends on a number of variables
+# set therein.
+#
+# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take.  If 'yes', then the emitted script
+# will assume that the directory in which it is stored is
+# the $objdir directory.  This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+       func_emit_wrapper_arg1=${1-no}
+
+       $ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+  # install mode needs the following variables:
+  generated_by_libtool_version='$macro_version'
+  notinst_deplibs='$notinst_deplibs'
+else
+  # When we are sourced in execute mode, \$file and \$ECHO are already set.
+  if test \"\$libtool_execute_magic\" != \"$magic\"; then
+    file=\"\$0\""
+
+    qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+    $ECHO "\
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+    ECHO=\"$qECHO\"
+  fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ which is used only on
+# windows platforms, and (c) all begin with the string "--lt-"
+# (application programs are unlikely to have options which match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's $0 value, followed by "$@".
+lt_option_debug=
+func_parse_lt_options ()
+{
+  lt_script_arg0=\$0
+  shift
+  for lt_opt
+  do
+    case \"\$lt_opt\" in
+    --lt-debug) lt_option_debug=1 ;;
+    --lt-dump-script)
+        lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\`
+        test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=.
+        lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\`
+        cat \"\$lt_dump_D/\$lt_dump_F\"
+        exit 0
+      ;;
+    --lt-*)
+        \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2
+        exit 1
+      ;;
+    esac
+  done
+
+  # Print the debug banner immediately:
+  if test -n \"\$lt_option_debug\"; then
+    echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2
+  fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+  lt_dump_args_N=1;
+  for lt_arg
+  do
+    \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\"
+    lt_dump_args_N=\`expr \$lt_dump_args_N + 1\`
+  done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+"
+  case $host in
+  # Backslashes separate directories on plain windows
+  *-*-mingw | *-*-os2* | *-cegcc*)
+    $ECHO "\
+      if test -n \"\$lt_option_debug\"; then
+        \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2
+        func_lt_dump_args \${1+\"\$@\"} 1>&2
+      fi
+      exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+    ;;
+
+  *)
+    $ECHO "\
+      if test -n \"\$lt_option_debug\"; then
+        \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2
+        func_lt_dump_args \${1+\"\$@\"} 1>&2
+      fi
+      exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+    ;;
+  esac
+  $ECHO "\
+      \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+      exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from \$@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+  case \" \$* \" in
+  *\\ --lt-*)
+    for lt_wr_arg
+    do
+      case \$lt_wr_arg in
+      --lt-*) ;;
+      *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
+      esac
+      shift
+    done ;;
+  esac
+  func_exec_program_core \${1+\"\$@\"}
+}
+
+  # Parse options
+  func_parse_lt_options \"\$0\" \${1+\"\$@\"}
+
+  # Find the directory that this script lives in.
+  thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\`
+  test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+  # Follow symbolic links until we get to the real thisdir.
+  file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\`
+  while test -n \"\$file\"; do
+    destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\`
+
+    # If there was a directory component, then change thisdir.
+    if test \"x\$destdir\" != \"x\$file\"; then
+      case \"\$destdir\" in
+      [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+      *) thisdir=\"\$thisdir/\$destdir\" ;;
+      esac
+    fi
+
+    file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\`
+    file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\`
+  done
+
+  # Usually 'no', except on cygwin/mingw when embedded into
+  # the cwrapper.
+  WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1
+  if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+    # special case for '.'
+    if test \"\$thisdir\" = \".\"; then
+      thisdir=\`pwd\`
+    fi
+    # remove .libs from thisdir
+    case \"\$thisdir\" in
+    *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;;
+    $objdir )   thisdir=. ;;
+    esac
+  fi
+
+  # Try to get the absolute directory name.
+  absdir=\`cd \"\$thisdir\" && pwd\`
+  test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+       if test "$fast_install" = yes; then
+         $ECHO "\
+  program=lt-'$outputname'$exeext
+  progdir=\"\$thisdir/$objdir\"
+
+  if test ! -f \"\$progdir/\$program\" ||
+     { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\
+       test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+    file=\"\$\$-\$program\"
+
+    if test ! -d \"\$progdir\"; then
+      $MKDIR \"\$progdir\"
+    else
+      $RM \"\$progdir/\$file\"
+    fi"
+
+         $ECHO "\
+
+    # relink executable if necessary
+    if test -n \"\$relink_command\"; then
+      if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+      else
+       $ECHO \"\$relink_command_output\" >&2
+       $RM \"\$progdir/\$file\"
+       exit 1
+      fi
+    fi
+
+    $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+    { $RM \"\$progdir/\$program\";
+      $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+    $RM \"\$progdir/\$file\"
+  fi"
+       else
+         $ECHO "\
+  program='$outputname'
+  progdir=\"\$thisdir/$objdir\"
+"
+       fi
+
+       $ECHO "\
+
+  if test -f \"\$progdir/\$program\"; then"
+
+       # fixup the dll searchpath if we need to.
+       #
+       # Fix the DLL searchpath if we need to.  Do this before prepending
+       # to shlibpath, because on Windows, both are PATH and uninstalled
+       # libraries must come first.
+       if test -n "$dllsearchpath"; then
+         $ECHO "\
+    # Add the dll search path components to the executable PATH
+    PATH=$dllsearchpath:\$PATH
+"
+       fi
+
+       # Export our shlibpath_var if we have one.
+       if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+         $ECHO "\
+    # Add our own library path to $shlibpath_var
+    $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+    # Some systems cannot cope with colon-terminated $shlibpath_var
+    # The second colon is a workaround for a bug in BeOS R4 sed
+    $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\`
+
+    export $shlibpath_var
+"
+       fi
+
+       $ECHO "\
+    if test \"\$libtool_execute_magic\" != \"$magic\"; then
+      # Run the actual program with our arguments.
+      func_exec_program \${1+\"\$@\"}
+    fi
+  else
+    # The program doesn't exist.
+    \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2
+    \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+    \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+    exit 1
+  fi
+fi\
+"
+}
+
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+       cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+   Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+
+   The $output program cannot be directly executed until all the libtool
+   libraries that it depends on are installed.
+
+   This wrapper executable should never be moved out of the build directory.
+   If it is, it will not operate correctly.
+*/
+EOF
+           cat <<"EOF"
+#ifdef _MSC_VER
+# define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+#  include <io.h>
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+/* declarations of non-ANSI functions */
+#if defined(__MINGW32__)
+# ifdef __STRICT_ANSI__
+int _putenv (const char *);
+# endif
+#elif defined(__CYGWIN__)
+# ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+# endif
+/* #elif defined (other platforms) ... */
+#endif
+
+/* portability defines, excluding path handling macros */
+#if defined(_MSC_VER)
+# define setmode _setmode
+# define stat    _stat
+# define chmod   _chmod
+# define getcwd  _getcwd
+# define putenv  _putenv
+# define S_IXUSR _S_IEXEC
+# ifndef _INTPTR_T_DEFINED
+#  define _INTPTR_T_DEFINED
+#  define intptr_t int
+# endif
+#elif defined(__MINGW32__)
+# define setmode _setmode
+# define stat    _stat
+# define chmod   _chmod
+# define getcwd  _getcwd
+# define putenv  _putenv
+#elif defined(__CYGWIN__)
+# define HAVE_SETENV
+# define FOPEN_WB "wb"
+/* #elif defined (other platforms) ... */
+#endif
+
+#if defined(PATH_MAX)
+# define LT_PATHMAX PATH_MAX
+#elif defined(MAXPATHLEN)
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+/* path handling portability macros */
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \
+  defined (__OS2__)
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+#  define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+#  define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+       (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num)      ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+  if (stale) { free ((void *) stale); stale = 0; } \
+} while (0)
+
+#if defined(LT_DEBUGWRAPPER)
+static int lt_debug = 1;
+#else
+static int lt_debug = 0;
+#endif
+
+const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_debugprintf (const char *file, int line, const char *fmt, ...);
+void lt_fatal (const char *file, int line, const char *message, ...);
+static const char *nonnull (const char *s);
+static const char *nonempty (const char *s);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+char **prepare_spawn (char **argv);
+void lt_dump_script (FILE *f);
+EOF
+
+           cat <<EOF
+volatile const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+           if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+              func_to_host_path "$temp_rpath"
+             cat <<EOF
+const char * LIB_PATH_VALUE   = "$func_to_host_path_result";
+EOF
+           else
+             cat <<"EOF"
+const char * LIB_PATH_VALUE   = "";
+EOF
+           fi
+
+           if test -n "$dllsearchpath"; then
+              func_to_host_path "$dllsearchpath:"
+             cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE   = "$func_to_host_path_result";
+EOF
+           else
+             cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE   = "";
+EOF
+           fi
+
+           if test "$fast_install" = yes; then
+             cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+           else
+             cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+           fi
+
+
+           cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX         "--lt-"
+
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+static const char *dumpscript_opt       = LTWRAPPER_OPTION_PREFIX "dump-script";
+static const char *debug_opt            = LTWRAPPER_OPTION_PREFIX "debug";
+
+int
+main (int argc, char *argv[])
+{
+  char **newargz;
+  int  newargc;
+  char *tmp_pathspec;
+  char *actual_cwrapper_path;
+  char *actual_cwrapper_name;
+  char *target_name;
+  char *lt_argv_zero;
+  intptr_t rval = 127;
+
+  int i;
+
+  program_name = (char *) xstrdup (base_name (argv[0]));
+  newargz = XMALLOC (char *, argc + 1);
+
+  /* very simple arg parsing; don't want to rely on getopt
+   * also, copy all non cwrapper options to newargz, except
+   * argz[0], which is handled differently
+   */
+  newargc=0;
+  for (i = 1; i < argc; i++)
+    {
+      if (strcmp (argv[i], dumpscript_opt) == 0)
+       {
+EOF
+           case "$host" in
+             *mingw* | *cygwin* )
+               # make stdout use "unix" line endings
+               echo "          setmode(1,_O_BINARY);"
+               ;;
+             esac
+
+           cat <<"EOF"
+         lt_dump_script (stdout);
+         return 0;
+       }
+      if (strcmp (argv[i], debug_opt) == 0)
+       {
+          lt_debug = 1;
+          continue;
+       }
+      if (strcmp (argv[i], ltwrapper_option_prefix) == 0)
+        {
+          /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+             namespace, but it is not one of the ones we know about and
+             have already dealt with, above (inluding dump-script), then
+             report an error. Otherwise, targets might begin to believe
+             they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+             namespace. The first time any user complains about this, we'll
+             need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+             or a configure.ac-settable value.
+           */
+          lt_fatal (__FILE__, __LINE__,
+                   "unrecognized %s option: '%s'",
+                    ltwrapper_option_prefix, argv[i]);
+        }
+      /* otherwise ... */
+      newargz[++newargc] = xstrdup (argv[i]);
+    }
+  newargz[++newargc] = NULL;
+
+EOF
+           cat <<EOF
+  /* The GNU banner must be the first non-error debug message */
+  lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\n");
+EOF
+           cat <<"EOF"
+  lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]);
+  lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name);
+
+  tmp_pathspec = find_executable (argv[0]);
+  if (tmp_pathspec == NULL)
+    lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]);
+  lt_debugprintf (__FILE__, __LINE__,
+                  "(main) found exe (before symlink chase) at: %s\n",
+                 tmp_pathspec);
+
+  actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+  lt_debugprintf (__FILE__, __LINE__,
+                  "(main) found exe (after symlink chase) at: %s\n",
+                 actual_cwrapper_path);
+  XFREE (tmp_pathspec);
+
+  actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path));
+  strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+  /* wrapper name transforms */
+  strendzap (actual_cwrapper_name, ".exe");
+  tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+  XFREE (actual_cwrapper_name);
+  actual_cwrapper_name = tmp_pathspec;
+  tmp_pathspec = 0;
+
+  /* target_name transforms -- use actual target program name; might have lt- prefix */
+  target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+  strendzap (target_name, ".exe");
+  tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+  XFREE (target_name);
+  target_name = tmp_pathspec;
+  tmp_pathspec = 0;
+
+  lt_debugprintf (__FILE__, __LINE__,
+                 "(main) libtool target name: %s\n",
+                 target_name);
+EOF
+
+           cat <<EOF
+  newargz[0] =
+    XMALLOC (char, (strlen (actual_cwrapper_path) +
+                   strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+  strcpy (newargz[0], actual_cwrapper_path);
+  strcat (newargz[0], "$objdir");
+  strcat (newargz[0], "/");
+EOF
+
+           cat <<"EOF"
+  /* stop here, and copy so we don't have to do this twice */
+  tmp_pathspec = xstrdup (newargz[0]);
+
+  /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+  strcat (newargz[0], actual_cwrapper_name);
+
+  /* DO want the lt- prefix here if it exists, so use target_name */
+  lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+  XFREE (tmp_pathspec);
+  tmp_pathspec = NULL;
+EOF
+
+           case $host_os in
+             mingw*)
+           cat <<"EOF"
+  {
+    char* p;
+    while ((p = strchr (newargz[0], '\\')) != NULL)
+      {
+       *p = '/';
+      }
+    while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+      {
+       *p = '/';
+      }
+  }
+EOF
+           ;;
+           esac
+
+           cat <<"EOF"
+  XFREE (target_name);
+  XFREE (actual_cwrapper_path);
+  XFREE (actual_cwrapper_name);
+
+  lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+  lt_setenv ("DUALCASE", "1");  /* for MSK sh */
+  /* Update the DLL searchpath.  EXE_PATH_VALUE ($dllsearchpath) must
+     be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath)
+     because on Windows, both *_VARNAMEs are PATH but uninstalled
+     libraries must come first. */
+  lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+  lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+
+  lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n",
+                 nonnull (lt_argv_zero));
+  for (i = 0; i < newargc; i++)
+    {
+      lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n",
+                     i, nonnull (newargz[i]));
+    }
+
+EOF
+
+           case $host_os in
+             mingw*)
+               cat <<"EOF"
+  /* execv doesn't actually work on mingw as expected on unix */
+  newargz = prepare_spawn (newargz);
+  rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+  if (rval == -1)
+    {
+      /* failed to start process */
+      lt_debugprintf (__FILE__, __LINE__,
+                     "(main) failed to launch target \"%s\": %s\n",
+                     lt_argv_zero, nonnull (strerror (errno)));
+      return 127;
+    }
+  return rval;
+EOF
+               ;;
+             *)
+               cat <<"EOF"
+  execv (lt_argv_zero, newargz);
+  return rval; /* =127, but avoids unused variable warning */
+EOF
+               ;;
+           esac
+
+           cat <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+  void *p = (void *) malloc (num);
+  if (!p)
+    lt_fatal (__FILE__, __LINE__, "memory exhausted");
+
+  return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+  return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+                         string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+  const char *base;
+
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  /* Skip over the disk name in MSDOS pathnames. */
+  if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+    name += 2;
+#endif
+
+  for (base = name; *name; name++)
+    if (IS_DIR_SEPARATOR (*name))
+      base = name + 1;
+  return base;
+}
+
+int
+check_executable (const char *path)
+{
+  struct stat st;
+
+  lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n",
+                  nonempty (path));
+  if ((!path) || (!*path))
+    return 0;
+
+  if ((stat (path, &st) >= 0)
+      && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+    return 1;
+  else
+    return 0;
+}
+
+int
+make_executable (const char *path)
+{
+  int rval = 0;
+  struct stat st;
+
+  lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n",
+                  nonempty (path));
+  if ((!path) || (!*path))
+    return 0;
+
+  if (stat (path, &st) >= 0)
+    {
+      rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+    }
+  return rval;
+}
+
+/* Searches for the full path of the wrapper.  Returns
+   newly allocated full path name if found, NULL otherwise
+   Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+  int has_slash = 0;
+  const char *p;
+  const char *p_next;
+  /* static buffer for getcwd */
+  char tmp[LT_PATHMAX + 1];
+  int tmp_len;
+  char *concat_name;
+
+  lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n",
+                  nonempty (wrapper));
+
+  if ((wrapper == NULL) || (*wrapper == '\0'))
+    return NULL;
+
+  /* Absolute path? */
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+    {
+      concat_name = xstrdup (wrapper);
+      if (check_executable (concat_name))
+       return concat_name;
+      XFREE (concat_name);
+    }
+  else
+    {
+#endif
+      if (IS_DIR_SEPARATOR (wrapper[0]))
+       {
+         concat_name = xstrdup (wrapper);
+         if (check_executable (concat_name))
+           return concat_name;
+         XFREE (concat_name);
+       }
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+    }
+#endif
+
+  for (p = wrapper; *p; p++)
+    if (*p == '/')
+      {
+       has_slash = 1;
+       break;
+      }
+  if (!has_slash)
+    {
+      /* no slashes; search PATH */
+      const char *path = getenv ("PATH");
+      if (path != NULL)
+       {
+         for (p = path; *p; p = p_next)
+           {
+             const char *q;
+             size_t p_len;
+             for (q = p; *q; q++)
+               if (IS_PATH_SEPARATOR (*q))
+                 break;
+             p_len = q - p;
+             p_next = (*q == '\0' ? q : q + 1);
+             if (p_len == 0)
+               {
+                 /* empty path: current directory */
+                 if (getcwd (tmp, LT_PATHMAX) == NULL)
+                   lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+                              nonnull (strerror (errno)));
+                 tmp_len = strlen (tmp);
+                 concat_name =
+                   XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+                 memcpy (concat_name, tmp, tmp_len);
+                 concat_name[tmp_len] = '/';
+                 strcpy (concat_name + tmp_len + 1, wrapper);
+               }
+             else
+               {
+                 concat_name =
+                   XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+                 memcpy (concat_name, p, p_len);
+                 concat_name[p_len] = '/';
+                 strcpy (concat_name + p_len + 1, wrapper);
+               }
+             if (check_executable (concat_name))
+               return concat_name;
+             XFREE (concat_name);
+           }
+       }
+      /* not found in PATH; assume curdir */
+    }
+  /* Relative path | not found in path: prepend cwd */
+  if (getcwd (tmp, LT_PATHMAX) == NULL)
+    lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+              nonnull (strerror (errno)));
+  tmp_len = strlen (tmp);
+  concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+  memcpy (concat_name, tmp, tmp_len);
+  concat_name[tmp_len] = '/';
+  strcpy (concat_name + tmp_len + 1, wrapper);
+
+  if (check_executable (concat_name))
+    return concat_name;
+  XFREE (concat_name);
+  return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+  return xstrdup (pathspec);
+#else
+  char buf[LT_PATHMAX];
+  struct stat s;
+  char *tmp_pathspec = xstrdup (pathspec);
+  char *p;
+  int has_symlinks = 0;
+  while (strlen (tmp_pathspec) && !has_symlinks)
+    {
+      lt_debugprintf (__FILE__, __LINE__,
+                     "checking path component for symlinks: %s\n",
+                     tmp_pathspec);
+      if (lstat (tmp_pathspec, &s) == 0)
+       {
+         if (S_ISLNK (s.st_mode) != 0)
+           {
+             has_symlinks = 1;
+             break;
+           }
+
+         /* search backwards for last DIR_SEPARATOR */
+         p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+         while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+           p--;
+         if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+           {
+             /* no more DIR_SEPARATORS left */
+             break;
+           }
+         *p = '\0';
+       }
+      else
+       {
+         lt_fatal (__FILE__, __LINE__,
+                   "error accessing file \"%s\": %s",
+                   tmp_pathspec, nonnull (strerror (errno)));
+       }
+    }
+  XFREE (tmp_pathspec);
+
+  if (!has_symlinks)
+    {
+      return xstrdup (pathspec);
+    }
+
+  tmp_pathspec = realpath (pathspec, buf);
+  if (tmp_pathspec == 0)
+    {
+      lt_fatal (__FILE__, __LINE__,
+               "could not follow symlinks for %s", pathspec);
+    }
+  return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+  size_t len, patlen;
+
+  assert (str != NULL);
+  assert (pat != NULL);
+
+  len = strlen (str);
+  patlen = strlen (pat);
+
+  if (patlen <= len)
+    {
+      str += len - patlen;
+      if (strcmp (str, pat) == 0)
+       *str = '\0';
+    }
+  return str;
+}
+
+void
+lt_debugprintf (const char *file, int line, const char *fmt, ...)
+{
+  va_list args;
+  if (lt_debug)
+    {
+      (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line);
+      va_start (args, fmt);
+      (void) vfprintf (stderr, fmt, args);
+      va_end (args);
+    }
+}
+
+static void
+lt_error_core (int exit_status, const char *file,
+              int line, const char *mode,
+              const char *message, va_list ap)
+{
+  fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode);
+  vfprintf (stderr, message, ap);
+  fprintf (stderr, ".\n");
+
+  if (exit_status >= 0)
+    exit (exit_status);
+}
+
+void
+lt_fatal (const char *file, int line, const char *message, ...)
+{
+  va_list ap;
+  va_start (ap, message);
+  lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap);
+  va_end (ap);
+}
+
+static const char *
+nonnull (const char *s)
+{
+  return s ? s : "(null)";
+}
+
+static const char *
+nonempty (const char *s)
+{
+  return (s && !*s) ? "(empty)" : nonnull (s);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+                 "(lt_setenv) setting '%s' to '%s'\n",
+                  nonnull (name), nonnull (value));
+  {
+#ifdef HAVE_SETENV
+    /* always make a copy, for consistency with !HAVE_SETENV */
+    char *str = xstrdup (value);
+    setenv (name, str, 1);
+#else
+    int len = strlen (name) + 1 + strlen (value) + 1;
+    char *str = XMALLOC (char, len);
+    sprintf (str, "%s=%s", name, value);
+    if (putenv (str) != EXIT_SUCCESS)
+      {
+        XFREE (str);
+      }
+#endif
+  }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+  char *new_value;
+  if (orig_value && *orig_value)
+    {
+      int orig_value_len = strlen (orig_value);
+      int add_len = strlen (add);
+      new_value = XMALLOC (char, add_len + orig_value_len + 1);
+      if (to_end)
+        {
+          strcpy (new_value, orig_value);
+          strcpy (new_value + orig_value_len, add);
+        }
+      else
+        {
+          strcpy (new_value, add);
+          strcpy (new_value + add_len, orig_value);
+        }
+    }
+  else
+    {
+      new_value = xstrdup (add);
+    }
+  return new_value;
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+                 "(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+                  nonnull (name), nonnull (value));
+
+  if (name && *name && value && *value)
+    {
+      char *new_value = lt_extend_str (getenv (name), value, 0);
+      /* some systems can't cope with a ':'-terminated path #' */
+      int len = strlen (new_value);
+      while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+        {
+          new_value[len-1] = '\0';
+        }
+      lt_setenv (name, new_value);
+      XFREE (new_value);
+    }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+                 "(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+                  nonnull (name), nonnull (value));
+
+  if (name && *name && value && *value)
+    {
+      char *new_value = lt_extend_str (getenv (name), value, 0);
+      lt_setenv (name, new_value);
+      XFREE (new_value);
+    }
+}
+
+EOF
+           case $host_os in
+             mingw*)
+               cat <<"EOF"
+
+/* Prepares an argument vector before calling spawn().
+   Note that spawn() does not by itself call the command interpreter
+     (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
+      ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+         GetVersionEx(&v);
+         v.dwPlatformId == VER_PLATFORM_WIN32_NT;
+      }) ? "cmd.exe" : "command.com").
+   Instead it simply concatenates the arguments, separated by ' ', and calls
+   CreateProcess().  We must quote the arguments since Win32 CreateProcess()
+   interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
+   special way:
+   - Space and tab are interpreted as delimiters. They are not treated as
+     delimiters if they are surrounded by double quotes: "...".
+   - Unescaped double quotes are removed from the input. Their only effect is
+     that within double quotes, space and tab are treated like normal
+     characters.
+   - Backslashes not followed by double quotes are not special.
+   - But 2*n+1 backslashes followed by a double quote become
+     n backslashes followed by a double quote (n >= 0):
+       \" -> "
+       \\\" -> \"
+       \\\\\" -> \\"
+ */
+#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+char **
+prepare_spawn (char **argv)
+{
+  size_t argc;
+  char **new_argv;
+  size_t i;
+
+  /* Count number of arguments.  */
+  for (argc = 0; argv[argc] != NULL; argc++)
+    ;
+
+  /* Allocate new argument vector.  */
+  new_argv = XMALLOC (char *, argc + 1);
+
+  /* Put quoted arguments into the new argument vector.  */
+  for (i = 0; i < argc; i++)
+    {
+      const char *string = argv[i];
+
+      if (string[0] == '\0')
+       new_argv[i] = xstrdup ("\"\"");
+      else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
+       {
+         int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
+         size_t length;
+         unsigned int backslashes;
+         const char *s;
+         char *quoted_string;
+         char *p;
+
+         length = 0;
+         backslashes = 0;
+         if (quote_around)
+           length++;
+         for (s = string; *s != '\0'; s++)
+           {
+             char c = *s;
+             if (c == '"')
+               length += backslashes + 1;
+             length++;
+             if (c == '\\')
+               backslashes++;
+             else
+               backslashes = 0;
+           }
+         if (quote_around)
+           length += backslashes + 1;
+
+         quoted_string = XMALLOC (char, length + 1);
+
+         p = quoted_string;
+         backslashes = 0;
+         if (quote_around)
+           *p++ = '"';
+         for (s = string; *s != '\0'; s++)
+           {
+             char c = *s;
+             if (c == '"')
+               {
+                 unsigned int j;
+                 for (j = backslashes + 1; j > 0; j--)
+                   *p++ = '\\';
+               }
+             *p++ = c;
+             if (c == '\\')
+               backslashes++;
+             else
+               backslashes = 0;
+           }
+         if (quote_around)
+           {
+             unsigned int j;
+             for (j = backslashes; j > 0; j--)
+               *p++ = '\\';
+             *p++ = '"';
+           }
+         *p = '\0';
+
+         new_argv[i] = quoted_string;
+       }
+      else
+       new_argv[i] = (char *) string;
+    }
+  new_argv[argc] = NULL;
+
+  return new_argv;
+}
+EOF
+               ;;
+           esac
+
+            cat <<"EOF"
+void lt_dump_script (FILE* f)
+{
+EOF
+           func_emit_wrapper yes |
+             $SED -n -e '
+s/^\(.\{79\}\)\(..*\)/\1\
+\2/
+h
+s/\([\\"]\)/\\\1/g
+s/$/\\n/
+s/\([^\n]*\).*/  fputs ("\1", f);/p
+g
+D'
+            cat <<"EOF"
+}
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_win32_import_lib_p ARG
+# True if ARG is an import lib, as indicated by $file_magic_cmd
+func_win32_import_lib_p ()
+{
+    $opt_debug
+    case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in
+    *import*) : ;;
+    *) false ;;
+    esac
+}
+
+# func_mode_link arg...
+func_mode_link ()
+{
+    $opt_debug
+    case $host in
+    *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+      # It is impossible to link a dll without this setting, and
+      # we shouldn't force the makefile maintainer to figure out
+      # which system we are compiling for in order to pass an extra
+      # flag for every libtool invocation.
+      # allow_undefined=no
+
+      # FIXME: Unfortunately, there are problems with the above when trying
+      # to make a dll which has undefined symbols, in which case not
+      # even a static library is built.  For now, we need to specify
+      # -no-undefined on the libtool link line when we can be certain
+      # that all symbols are satisfied, otherwise we get a static library.
+      allow_undefined=yes
+      ;;
+    *)
+      allow_undefined=yes
+      ;;
+    esac
+    libtool_args=$nonopt
+    base_compile="$nonopt $@"
+    compile_command=$nonopt
+    finalize_command=$nonopt
+
+    compile_rpath=
+    finalize_rpath=
+    compile_shlibpath=
+    finalize_shlibpath=
+    convenience=
+    old_convenience=
+    deplibs=
+    old_deplibs=
+    compiler_flags=
+    linker_flags=
+    dllsearchpath=
+    lib_search_path=`pwd`
+    inst_prefix_dir=
+    new_inherited_linker_flags=
+
+    avoid_version=no
+    bindir=
+    dlfiles=
+    dlprefiles=
+    dlself=no
+    export_dynamic=no
+    export_symbols=
+    export_symbols_regex=
+    generated=
+    libobjs=
+    ltlibs=
+    module=no
+    no_install=no
+    objs=
+    non_pic_objects=
+    precious_files_regex=
+    prefer_static_libs=no
+    preload=no
+    prev=
+    prevarg=
+    release=
+    rpath=
+    xrpath=
+    perm_rpath=
+    temp_rpath=
+    thread_safe=no
+    vinfo=
+    vinfo_number=no
+    weak_libs=
+    single_module="${wl}-single_module"
+    func_infer_tag $base_compile
+
+    # We need to know -static, to get the right output filenames.
+    for arg
+    do
+      case $arg in
+      -shared)
+       test "$build_libtool_libs" != yes && \
+         func_fatal_configuration "can not build a shared library"
+       build_old_libs=no
+       break
+       ;;
+      -all-static | -static | -static-libtool-libs)
+       case $arg in
+       -all-static)
+         if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+           func_warning "complete static linking is impossible in this configuration"
+         fi
+         if test -n "$link_static_flag"; then
+           dlopen_self=$dlopen_self_static
+         fi
+         prefer_static_libs=yes
+         ;;
+       -static)
+         if test -z "$pic_flag" && test -n "$link_static_flag"; then
+           dlopen_self=$dlopen_self_static
+         fi
+         prefer_static_libs=built
+         ;;
+       -static-libtool-libs)
+         if test -z "$pic_flag" && test -n "$link_static_flag"; then
+           dlopen_self=$dlopen_self_static
+         fi
+         prefer_static_libs=yes
+         ;;
+       esac
+       build_libtool_libs=no
+       build_old_libs=yes
+       break
+       ;;
+      esac
+    done
+
+    # See if our shared archives depend on static archives.
+    test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+    # Go through the arguments, transforming them on the way.
+    while test "$#" -gt 0; do
+      arg="$1"
+      shift
+      func_quote_for_eval "$arg"
+      qarg=$func_quote_for_eval_unquoted_result
+      func_append libtool_args " $func_quote_for_eval_result"
+
+      # If the previous option needs an argument, assign it.
+      if test -n "$prev"; then
+       case $prev in
+       output)
+         func_append compile_command " @OUTPUT@"
+         func_append finalize_command " @OUTPUT@"
+         ;;
+       esac
+
+       case $prev in
+       bindir)
+         bindir="$arg"
+         prev=
+         continue
+         ;;
+       dlfiles|dlprefiles)
+         if test "$preload" = no; then
+           # Add the symbol object into the linking commands.
+           func_append compile_command " @SYMFILE@"
+           func_append finalize_command " @SYMFILE@"
+           preload=yes
+         fi
+         case $arg in
+         *.la | *.lo) ;;  # We handle these cases below.
+         force)
+           if test "$dlself" = no; then
+             dlself=needless
+             export_dynamic=yes
+           fi
+           prev=
+           continue
+           ;;
+         self)
+           if test "$prev" = dlprefiles; then
+             dlself=yes
+           elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+             dlself=yes
+           else
+             dlself=needless
+             export_dynamic=yes
+           fi
+           prev=
+           continue
+           ;;
+         *)
+           if test "$prev" = dlfiles; then
+             func_append dlfiles " $arg"
+           else
+             func_append dlprefiles " $arg"
+           fi
+           prev=
+           continue
+           ;;
+         esac
+         ;;
+       expsyms)
+         export_symbols="$arg"
+         test -f "$arg" \
+           || func_fatal_error "symbol file \`$arg' does not exist"
+         prev=
+         continue
+         ;;
+       expsyms_regex)
+         export_symbols_regex="$arg"
+         prev=
+         continue
+         ;;
+       framework)
+         case $host in
+           *-*-darwin*)
+             case "$deplibs " in
+               *" $qarg.ltframework "*) ;;
+               *) func_append deplibs " $qarg.ltframework" # this is fixed later
+                  ;;
+             esac
+             ;;
+         esac
+         prev=
+         continue
+         ;;
+       inst_prefix)
+         inst_prefix_dir="$arg"
+         prev=
+         continue
+         ;;
+       objectlist)
+         if test -f "$arg"; then
+           save_arg=$arg
+           moreargs=
+           for fil in `cat "$save_arg"`
+           do
+#            func_append moreargs " $fil"
+             arg=$fil
+             # A libtool-controlled object.
+
+             # Check to see that this really is a libtool object.
+             if func_lalib_unsafe_p "$arg"; then
+               pic_object=
+               non_pic_object=
+
+               # Read the .lo file
+               func_source "$arg"
+
+               if test -z "$pic_object" ||
+                  test -z "$non_pic_object" ||
+                  test "$pic_object" = none &&
+                  test "$non_pic_object" = none; then
+                 func_fatal_error "cannot find name of object for \`$arg'"
+               fi
+
+               # Extract subdirectory from the argument.
+               func_dirname "$arg" "/" ""
+               xdir="$func_dirname_result"
+
+               if test "$pic_object" != none; then
+                 # Prepend the subdirectory the object is found in.
+                 pic_object="$xdir$pic_object"
+
+                 if test "$prev" = dlfiles; then
+                   if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+                     func_append dlfiles " $pic_object"
+                     prev=
+                     continue
+                   else
+                     # If libtool objects are unsupported, then we need to preload.
+                     prev=dlprefiles
+                   fi
+                 fi
+
+                 # CHECK ME:  I think I busted this.  -Ossama
+                 if test "$prev" = dlprefiles; then
+                   # Preload the old-style object.
+                   func_append dlprefiles " $pic_object"
+                   prev=
+                 fi
+
+                 # A PIC object.
+                 func_append libobjs " $pic_object"
+                 arg="$pic_object"
+               fi
+
+               # Non-PIC object.
+               if test "$non_pic_object" != none; then
+                 # Prepend the subdirectory the object is found in.
+                 non_pic_object="$xdir$non_pic_object"
+
+                 # A standard non-PIC object
+                 func_append non_pic_objects " $non_pic_object"
+                 if test -z "$pic_object" || test "$pic_object" = none ; then
+                   arg="$non_pic_object"
+                 fi
+               else
+                 # If the PIC object exists, use it instead.
+                 # $xdir was prepended to $pic_object above.
+                 non_pic_object="$pic_object"
+                 func_append non_pic_objects " $non_pic_object"
+               fi
+             else
+               # Only an error if not doing a dry-run.
+               if $opt_dry_run; then
+                 # Extract subdirectory from the argument.
+                 func_dirname "$arg" "/" ""
+                 xdir="$func_dirname_result"
+
+                 func_lo2o "$arg"
+                 pic_object=$xdir$objdir/$func_lo2o_result
+                 non_pic_object=$xdir$func_lo2o_result
+                 func_append libobjs " $pic_object"
+                 func_append non_pic_objects " $non_pic_object"
+               else
+                 func_fatal_error "\`$arg' is not a valid libtool object"
+               fi
+             fi
+           done
+         else
+           func_fatal_error "link input file \`$arg' does not exist"
+         fi
+         arg=$save_arg
+         prev=
+         continue
+         ;;
+       precious_regex)
+         precious_files_regex="$arg"
+         prev=
+         continue
+         ;;
+       release)
+         release="-$arg"
+         prev=
+         continue
+         ;;
+       rpath | xrpath)
+         # We need an absolute path.
+         case $arg in
+         [\\/]* | [A-Za-z]:[\\/]*) ;;
+         *)
+           func_fatal_error "only absolute run-paths are allowed"
+           ;;
+         esac
+         if test "$prev" = rpath; then
+           case "$rpath " in
+           *" $arg "*) ;;
+           *) func_append rpath " $arg" ;;
+           esac
+         else
+           case "$xrpath " in
+           *" $arg "*) ;;
+           *) func_append xrpath " $arg" ;;
+           esac
+         fi
+         prev=
+         continue
+         ;;
+       shrext)
+         shrext_cmds="$arg"
+         prev=
+         continue
+         ;;
+       weak)
+         func_append weak_libs " $arg"
+         prev=
+         continue
+         ;;
+       xcclinker)
+         func_append linker_flags " $qarg"
+         func_append compiler_flags " $qarg"
+         prev=
+         func_append compile_command " $qarg"
+         func_append finalize_command " $qarg"
+         continue
+         ;;
+       xcompiler)
+         func_append compiler_flags " $qarg"
+         prev=
+         func_append compile_command " $qarg"
+         func_append finalize_command " $qarg"
+         continue
+         ;;
+       xlinker)
+         func_append linker_flags " $qarg"
+         func_append compiler_flags " $wl$qarg"
+         prev=
+         func_append compile_command " $wl$qarg"
+         func_append finalize_command " $wl$qarg"
+         continue
+         ;;
+       *)
+         eval "$prev=\"\$arg\""
+         prev=
+         continue
+         ;;
+       esac
+      fi # test -n "$prev"
+
+      prevarg="$arg"
+
+      case $arg in
+      -all-static)
+       if test -n "$link_static_flag"; then
+         # See comment for -static flag below, for more details.
+         func_append compile_command " $link_static_flag"
+         func_append finalize_command " $link_static_flag"
+       fi
+       continue
+       ;;
+
+      -allow-undefined)
+       # FIXME: remove this flag sometime in the future.
+       func_fatal_error "\`-allow-undefined' must not be used because it is the default"
+       ;;
+
+      -avoid-version)
+       avoid_version=yes
+       continue
+       ;;
+
+      -bindir)
+       prev=bindir
+       continue
+       ;;
+
+      -dlopen)
+       prev=dlfiles
+       continue
+       ;;
+
+      -dlpreopen)
+       prev=dlprefiles
+       continue
+       ;;
+
+      -export-dynamic)
+       export_dynamic=yes
+       continue
+       ;;
+
+      -export-symbols | -export-symbols-regex)
+       if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+         func_fatal_error "more than one -exported-symbols argument is not allowed"
+       fi
+       if test "X$arg" = "X-export-symbols"; then
+         prev=expsyms
+       else
+         prev=expsyms_regex
+       fi
+       continue
+       ;;
+
+      -framework)
+       prev=framework
+       continue
+       ;;
+
+      -inst-prefix-dir)
+       prev=inst_prefix
+       continue
+       ;;
+
+      # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+      # so, if we see these flags be careful not to treat them like -L
+      -L[A-Z][A-Z]*:*)
+       case $with_gcc/$host in
+       no/*-*-irix* | /*-*-irix*)
+         func_append compile_command " $arg"
+         func_append finalize_command " $arg"
+         ;;
+       esac
+       continue
+       ;;
+
+      -L*)
+       func_stripname "-L" '' "$arg"
+       if test -z "$func_stripname_result"; then
+         if test "$#" -gt 0; then
+           func_fatal_error "require no space between \`-L' and \`$1'"
+         else
+           func_fatal_error "need path for \`-L' option"
+         fi
+       fi
+       func_resolve_sysroot "$func_stripname_result"
+       dir=$func_resolve_sysroot_result
+       # We need an absolute path.
+       case $dir in
+       [\\/]* | [A-Za-z]:[\\/]*) ;;
+       *)
+         absdir=`cd "$dir" && pwd`
+         test -z "$absdir" && \
+           func_fatal_error "cannot determine absolute directory name of \`$dir'"
+         dir="$absdir"
+         ;;
+       esac
+       case "$deplibs " in
+       *" -L$dir "* | *" $arg "*)
+         # Will only happen for absolute or sysroot arguments
+         ;;
+       *)
+         # Preserve sysroot, but never include relative directories
+         case $dir in
+           [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;;
+           *) func_append deplibs " -L$dir" ;;
+         esac
+         func_append lib_search_path " $dir"
+         ;;
+       esac
+       case $host in
+       *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+         testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
+         case :$dllsearchpath: in
+         *":$dir:"*) ;;
+         ::) dllsearchpath=$dir;;
+         *) func_append dllsearchpath ":$dir";;
+         esac
+         case :$dllsearchpath: in
+         *":$testbindir:"*) ;;
+         ::) dllsearchpath=$testbindir;;
+         *) func_append dllsearchpath ":$testbindir";;
+         esac
+         ;;
+       esac
+       continue
+       ;;
+
+      -l*)
+       if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then
+         case $host in
+         *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
+           # These systems don't actually have a C or math library (as such)
+           continue
+           ;;
+         *-*-os2*)
+           # These systems don't actually have a C library (as such)
+           test "X$arg" = "X-lc" && continue
+           ;;
+         *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+           # Do not include libc due to us having libc/libc_r.
+           test "X$arg" = "X-lc" && continue
+           ;;
+         *-*-rhapsody* | *-*-darwin1.[012])
+           # Rhapsody C and math libraries are in the System framework
+           func_append deplibs " System.ltframework"
+           continue
+           ;;
+         *-*-sco3.2v5* | *-*-sco5v6*)
+           # Causes problems with __ctype
+           test "X$arg" = "X-lc" && continue
+           ;;
+         *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+           # Compiler inserts libc in the correct place for threads to work
+           test "X$arg" = "X-lc" && continue
+           ;;
+         esac
+       elif test "X$arg" = "X-lc_r"; then
+        case $host in
+        *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+          # Do not include libc_r directly, use -pthread flag.
+          continue
+          ;;
+        esac
+       fi
+       func_append deplibs " $arg"
+       continue
+       ;;
+
+      -module)
+       module=yes
+       continue
+       ;;
+
+      # Tru64 UNIX uses -model [arg] to determine the layout of C++
+      # classes, name mangling, and exception handling.
+      # Darwin uses the -arch flag to determine output architecture.
+      -model|-arch|-isysroot|--sysroot)
+       func_append compiler_flags " $arg"
+       func_append compile_command " $arg"
+       func_append finalize_command " $arg"
+       prev=xcompiler
+       continue
+       ;;
+
+      -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+      |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+       func_append compiler_flags " $arg"
+       func_append compile_command " $arg"
+       func_append finalize_command " $arg"
+       case "$new_inherited_linker_flags " in
+           *" $arg "*) ;;
+           * ) func_append new_inherited_linker_flags " $arg" ;;
+       esac
+       continue
+       ;;
+
+      -multi_module)
+       single_module="${wl}-multi_module"
+       continue
+       ;;
+
+      -no-fast-install)
+       fast_install=no
+       continue
+       ;;
+
+      -no-install)
+       case $host in
+       *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+         # The PATH hackery in wrapper scripts is required on Windows
+         # and Darwin in order for the loader to find any dlls it needs.
+         func_warning "\`-no-install' is ignored for $host"
+         func_warning "assuming \`-no-fast-install' instead"
+         fast_install=no
+         ;;
+       *) no_install=yes ;;
+       esac
+       continue
+       ;;
+
+      -no-undefined)
+       allow_undefined=no
+       continue
+       ;;
+
+      -objectlist)
+       prev=objectlist
+       continue
+       ;;
+
+      -o) prev=output ;;
+
+      -precious-files-regex)
+       prev=precious_regex
+       continue
+       ;;
+
+      -release)
+       prev=release
+       continue
+       ;;
+
+      -rpath)
+       prev=rpath
+       continue
+       ;;
+
+      -R)
+       prev=xrpath
+       continue
+       ;;
+
+      -R*)
+       func_stripname '-R' '' "$arg"
+       dir=$func_stripname_result
+       # We need an absolute path.
+       case $dir in
+       [\\/]* | [A-Za-z]:[\\/]*) ;;
+       =*)
+         func_stripname '=' '' "$dir"
+         dir=$lt_sysroot$func_stripname_result
+         ;;
+       *)
+         func_fatal_error "only absolute run-paths are allowed"
+         ;;
+       esac
+       case "$xrpath " in
+       *" $dir "*) ;;
+       *) func_append xrpath " $dir" ;;
+       esac
+       continue
+       ;;
+
+      -shared)
+       # The effects of -shared are defined in a previous loop.
+       continue
+       ;;
+
+      -shrext)
+       prev=shrext
+       continue
+       ;;
+
+      -static | -static-libtool-libs)
+       # The effects of -static are defined in a previous loop.
+       # We used to do the same as -all-static on platforms that
+       # didn't have a PIC flag, but the assumption that the effects
+       # would be equivalent was wrong.  It would break on at least
+       # Digital Unix and AIX.
+       continue
+       ;;
+
+      -thread-safe)
+       thread_safe=yes
+       continue
+       ;;
+
+      -version-info)
+       prev=vinfo
+       continue
+       ;;
+
+      -version-number)
+       prev=vinfo
+       vinfo_number=yes
+       continue
+       ;;
+
+      -weak)
+        prev=weak
+       continue
+       ;;
+
+      -Wc,*)
+       func_stripname '-Wc,' '' "$arg"
+       args=$func_stripname_result
+       arg=
+       save_ifs="$IFS"; IFS=','
+       for flag in $args; do
+         IFS="$save_ifs"
+          func_quote_for_eval "$flag"
+         func_append arg " $func_quote_for_eval_result"
+         func_append compiler_flags " $func_quote_for_eval_result"
+       done
+       IFS="$save_ifs"
+       func_stripname ' ' '' "$arg"
+       arg=$func_stripname_result
+       ;;
+
+      -Wl,*)
+       func_stripname '-Wl,' '' "$arg"
+       args=$func_stripname_result
+       arg=
+       save_ifs="$IFS"; IFS=','
+       for flag in $args; do
+         IFS="$save_ifs"
+          func_quote_for_eval "$flag"
+         func_append arg " $wl$func_quote_for_eval_result"
+         func_append compiler_flags " $wl$func_quote_for_eval_result"
+         func_append linker_flags " $func_quote_for_eval_result"
+       done
+       IFS="$save_ifs"
+       func_stripname ' ' '' "$arg"
+       arg=$func_stripname_result
+       ;;
+
+      -Xcompiler)
+       prev=xcompiler
+       continue
+       ;;
+
+      -Xlinker)
+       prev=xlinker
+       continue
+       ;;
+
+      -XCClinker)
+       prev=xcclinker
+       continue
+       ;;
+
+      # -msg_* for osf cc
+      -msg_*)
+       func_quote_for_eval "$arg"
+       arg="$func_quote_for_eval_result"
+       ;;
+
+      # Flags to be passed through unchanged, with rationale:
+      # -64, -mips[0-9]      enable 64-bit mode for the SGI compiler
+      # -r[0-9][0-9]*        specify processor for the SGI compiler
+      # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler
+      # +DA*, +DD*           enable 64-bit mode for the HP compiler
+      # -q*                  compiler args for the IBM compiler
+      # -m*, -t[45]*, -txscale* architecture-specific flags for GCC
+      # -F/path              path to uninstalled frameworks, gcc on darwin
+      # -p, -pg, --coverage, -fprofile-*  profiling flags for GCC
+      # @file                GCC response files
+      # -tp=*                Portland pgcc target processor selection
+      # --sysroot=*          for sysroot support
+      # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+      -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+      -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
+      -O*|-flto*|-fwhopr*|-fuse-linker-plugin)
+        func_quote_for_eval "$arg"
+       arg="$func_quote_for_eval_result"
+        func_append compile_command " $arg"
+        func_append finalize_command " $arg"
+        func_append compiler_flags " $arg"
+        continue
+        ;;
+
+      # Some other compiler flag.
+      -* | +*)
+        func_quote_for_eval "$arg"
+       arg="$func_quote_for_eval_result"
+       ;;
+
+      *.$objext)
+       # A standard object.
+       func_append objs " $arg"
+       ;;
+
+      *.lo)
+       # A libtool-controlled object.
+
+       # Check to see that this really is a libtool object.
+       if func_lalib_unsafe_p "$arg"; then
+         pic_object=
+         non_pic_object=
+
+         # Read the .lo file
+         func_source "$arg"
+
+         if test -z "$pic_object" ||
+            test -z "$non_pic_object" ||
+            test "$pic_object" = none &&
+            test "$non_pic_object" = none; then
+           func_fatal_error "cannot find name of object for \`$arg'"
+         fi
+
+         # Extract subdirectory from the argument.
+         func_dirname "$arg" "/" ""
+         xdir="$func_dirname_result"
+
+         if test "$pic_object" != none; then
+           # Prepend the subdirectory the object is found in.
+           pic_object="$xdir$pic_object"
+
+           if test "$prev" = dlfiles; then
+             if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+               func_append dlfiles " $pic_object"
+               prev=
+               continue
+             else
+               # If libtool objects are unsupported, then we need to preload.
+               prev=dlprefiles
+             fi
+           fi
+
+           # CHECK ME:  I think I busted this.  -Ossama
+           if test "$prev" = dlprefiles; then
+             # Preload the old-style object.
+             func_append dlprefiles " $pic_object"
+             prev=
+           fi
+
+           # A PIC object.
+           func_append libobjs " $pic_object"
+           arg="$pic_object"
+         fi
+
+         # Non-PIC object.
+         if test "$non_pic_object" != none; then
+           # Prepend the subdirectory the object is found in.
+           non_pic_object="$xdir$non_pic_object"
+
+           # A standard non-PIC object
+           func_append non_pic_objects " $non_pic_object"
+           if test -z "$pic_object" || test "$pic_object" = none ; then
+             arg="$non_pic_object"
+           fi
+         else
+           # If the PIC object exists, use it instead.
+           # $xdir was prepended to $pic_object above.
+           non_pic_object="$pic_object"
+           func_append non_pic_objects " $non_pic_object"
+         fi
+       else
+         # Only an error if not doing a dry-run.
+         if $opt_dry_run; then
+           # Extract subdirectory from the argument.
+           func_dirname "$arg" "/" ""
+           xdir="$func_dirname_result"
+
+           func_lo2o "$arg"
+           pic_object=$xdir$objdir/$func_lo2o_result
+           non_pic_object=$xdir$func_lo2o_result
+           func_append libobjs " $pic_object"
+           func_append non_pic_objects " $non_pic_object"
+         else
+           func_fatal_error "\`$arg' is not a valid libtool object"
+         fi
+       fi
+       ;;
+
+      *.$libext)
+       # An archive.
+       func_append deplibs " $arg"
+       func_append old_deplibs " $arg"
+       continue
+       ;;
+
+      *.la)
+       # A libtool-controlled library.
+
+       func_resolve_sysroot "$arg"
+       if test "$prev" = dlfiles; then
+         # This library was specified with -dlopen.
+         func_append dlfiles " $func_resolve_sysroot_result"
+         prev=
+       elif test "$prev" = dlprefiles; then
+         # The library was specified with -dlpreopen.
+         func_append dlprefiles " $func_resolve_sysroot_result"
+         prev=
+       else
+         func_append deplibs " $func_resolve_sysroot_result"
+       fi
+       continue
+       ;;
+
+      # Some other compiler argument.
+      *)
+       # Unknown arguments in both finalize_command and compile_command need
+       # to be aesthetically quoted because they are evaled later.
+       func_quote_for_eval "$arg"
+       arg="$func_quote_for_eval_result"
+       ;;
+      esac # arg
+
+      # Now actually substitute the argument into the commands.
+      if test -n "$arg"; then
+       func_append compile_command " $arg"
+       func_append finalize_command " $arg"
+      fi
+    done # argument parsing loop
+
+    test -n "$prev" && \
+      func_fatal_help "the \`$prevarg' option requires an argument"
+
+    if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+      eval arg=\"$export_dynamic_flag_spec\"
+      func_append compile_command " $arg"
+      func_append finalize_command " $arg"
+    fi
+
+    oldlibs=
+    # calculate the name of the file, without its directory
+    func_basename "$output"
+    outputname="$func_basename_result"
+    libobjs_save="$libobjs"
+
+    if test -n "$shlibpath_var"; then
+      # get the directories listed in $shlibpath_var
+      eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\`
+    else
+      shlib_search_path=
+    fi
+    eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+    eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+    func_dirname "$output" "/" ""
+    output_objdir="$func_dirname_result$objdir"
+    func_to_tool_file "$output_objdir/"
+    tool_output_objdir=$func_to_tool_file_result
+    # Create the object directory.
+    func_mkdir_p "$output_objdir"
+
+    # Determine the type of output
+    case $output in
+    "")
+      func_fatal_help "you must specify an output file"
+      ;;
+    *.$libext) linkmode=oldlib ;;
+    *.lo | *.$objext) linkmode=obj ;;
+    *.la) linkmode=lib ;;
+    *) linkmode=prog ;; # Anything else should be a program.
+    esac
+
+    specialdeplibs=
+
+    libs=
+    # Find all interdependent deplibs by searching for libraries
+    # that are linked more than once (e.g. -la -lb -la)
+    for deplib in $deplibs; do
+      if $opt_preserve_dup_deps ; then
+       case "$libs " in
+       *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+       esac
+      fi
+      func_append libs " $deplib"
+    done
+
+    if test "$linkmode" = lib; then
+      libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+      # Compute libraries that are listed more than once in $predeps
+      # $postdeps and mark them as special (i.e., whose duplicates are
+      # not to be eliminated).
+      pre_post_deps=
+      if $opt_duplicate_compiler_generated_deps; then
+       for pre_post_dep in $predeps $postdeps; do
+         case "$pre_post_deps " in
+         *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;;
+         esac
+         func_append pre_post_deps " $pre_post_dep"
+       done
+      fi
+      pre_post_deps=
+    fi
+
+    deplibs=
+    newdependency_libs=
+    newlib_search_path=
+    need_relink=no # whether we're linking any uninstalled libtool libraries
+    notinst_deplibs= # not-installed libtool libraries
+    notinst_path= # paths that contain not-installed libtool libraries
+
+    case $linkmode in
+    lib)
+       passes="conv dlpreopen link"
+       for file in $dlfiles $dlprefiles; do
+         case $file in
+         *.la) ;;
+         *)
+           func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file"
+           ;;
+         esac
+       done
+       ;;
+    prog)
+       compile_deplibs=
+       finalize_deplibs=
+       alldeplibs=no
+       newdlfiles=
+       newdlprefiles=
+       passes="conv scan dlopen dlpreopen link"
+       ;;
+    *)  passes="conv"
+       ;;
+    esac
+
+    for pass in $passes; do
+      # The preopen pass in lib mode reverses $deplibs; put it back here
+      # so that -L comes before libs that need it for instance...
+      if test "$linkmode,$pass" = "lib,link"; then
+       ## FIXME: Find the place where the list is rebuilt in the wrong
+       ##        order, and fix it there properly
+        tmp_deplibs=
+       for deplib in $deplibs; do
+         tmp_deplibs="$deplib $tmp_deplibs"
+       done
+       deplibs="$tmp_deplibs"
+      fi
+
+      if test "$linkmode,$pass" = "lib,link" ||
+        test "$linkmode,$pass" = "prog,scan"; then
+       libs="$deplibs"
+       deplibs=
+      fi
+      if test "$linkmode" = prog; then
+       case $pass in
+       dlopen) libs="$dlfiles" ;;
+       dlpreopen) libs="$dlprefiles" ;;
+       link)
+         libs="$deplibs %DEPLIBS%"
+         test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs"
+         ;;
+       esac
+      fi
+      if test "$linkmode,$pass" = "lib,dlpreopen"; then
+       # Collect and forward deplibs of preopened libtool libs
+       for lib in $dlprefiles; do
+         # Ignore non-libtool-libs
+         dependency_libs=
+         func_resolve_sysroot "$lib"
+         case $lib in
+         *.la) func_source "$func_resolve_sysroot_result" ;;
+         esac
+
+         # Collect preopened libtool deplibs, except any this library
+         # has declared as weak libs
+         for deplib in $dependency_libs; do
+           func_basename "$deplib"
+            deplib_base=$func_basename_result
+           case " $weak_libs " in
+           *" $deplib_base "*) ;;
+           *) func_append deplibs " $deplib" ;;
+           esac
+         done
+       done
+       libs="$dlprefiles"
+      fi
+      if test "$pass" = dlopen; then
+       # Collect dlpreopened libraries
+       save_deplibs="$deplibs"
+       deplibs=
+      fi
+
+      for deplib in $libs; do
+       lib=
+       found=no
+       case $deplib in
+       -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+        |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+         if test "$linkmode,$pass" = "prog,link"; then
+           compile_deplibs="$deplib $compile_deplibs"
+           finalize_deplibs="$deplib $finalize_deplibs"
+         else
+           func_append compiler_flags " $deplib"
+           if test "$linkmode" = lib ; then
+               case "$new_inherited_linker_flags " in
+                   *" $deplib "*) ;;
+                   * ) func_append new_inherited_linker_flags " $deplib" ;;
+               esac
+           fi
+         fi
+         continue
+         ;;
+       -l*)
+         if test "$linkmode" != lib && test "$linkmode" != prog; then
+           func_warning "\`-l' is ignored for archives/objects"
+           continue
+         fi
+         func_stripname '-l' '' "$deplib"
+         name=$func_stripname_result
+         if test "$linkmode" = lib; then
+           searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+         else
+           searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+         fi
+         for searchdir in $searchdirs; do
+           for search_ext in .la $std_shrext .so .a; do
+             # Search the libtool library
+             lib="$searchdir/lib${name}${search_ext}"
+             if test -f "$lib"; then
+               if test "$search_ext" = ".la"; then
+                 found=yes
+               else
+                 found=no
+               fi
+               break 2
+             fi
+           done
+         done
+         if test "$found" != yes; then
+           # deplib doesn't seem to be a libtool library
+           if test "$linkmode,$pass" = "prog,link"; then
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           else
+             deplibs="$deplib $deplibs"
+             test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+           fi
+           continue
+         else # deplib is a libtool library
+           # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+           # We need to do some special things here, and not later.
+           if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+             case " $predeps $postdeps " in
+             *" $deplib "*)
+               if func_lalib_p "$lib"; then
+                 library_names=
+                 old_library=
+                 func_source "$lib"
+                 for l in $old_library $library_names; do
+                   ll="$l"
+                 done
+                 if test "X$ll" = "X$old_library" ; then # only static version available
+                   found=no
+                   func_dirname "$lib" "" "."
+                   ladir="$func_dirname_result"
+                   lib=$ladir/$old_library
+                   if test "$linkmode,$pass" = "prog,link"; then
+                     compile_deplibs="$deplib $compile_deplibs"
+                     finalize_deplibs="$deplib $finalize_deplibs"
+                   else
+                     deplibs="$deplib $deplibs"
+                     test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+                   fi
+                   continue
+                 fi
+               fi
+               ;;
+             *) ;;
+             esac
+           fi
+         fi
+         ;; # -l
+       *.ltframework)
+         if test "$linkmode,$pass" = "prog,link"; then
+           compile_deplibs="$deplib $compile_deplibs"
+           finalize_deplibs="$deplib $finalize_deplibs"
+         else
+           deplibs="$deplib $deplibs"
+           if test "$linkmode" = lib ; then
+               case "$new_inherited_linker_flags " in
+                   *" $deplib "*) ;;
+                   * ) func_append new_inherited_linker_flags " $deplib" ;;
+               esac
+           fi
+         fi
+         continue
+         ;;
+       -L*)
+         case $linkmode in
+         lib)
+           deplibs="$deplib $deplibs"
+           test "$pass" = conv && continue
+           newdependency_libs="$deplib $newdependency_libs"
+           func_stripname '-L' '' "$deplib"
+           func_resolve_sysroot "$func_stripname_result"
+           func_append newlib_search_path " $func_resolve_sysroot_result"
+           ;;
+         prog)
+           if test "$pass" = conv; then
+             deplibs="$deplib $deplibs"
+             continue
+           fi
+           if test "$pass" = scan; then
+             deplibs="$deplib $deplibs"
+           else
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           fi
+           func_stripname '-L' '' "$deplib"
+           func_resolve_sysroot "$func_stripname_result"
+           func_append newlib_search_path " $func_resolve_sysroot_result"
+           ;;
+         *)
+           func_warning "\`-L' is ignored for archives/objects"
+           ;;
+         esac # linkmode
+         continue
+         ;; # -L
+       -R*)
+         if test "$pass" = link; then
+           func_stripname '-R' '' "$deplib"
+           func_resolve_sysroot "$func_stripname_result"
+           dir=$func_resolve_sysroot_result
+           # Make sure the xrpath contains only unique directories.
+           case "$xrpath " in
+           *" $dir "*) ;;
+           *) func_append xrpath " $dir" ;;
+           esac
+         fi
+         deplibs="$deplib $deplibs"
+         continue
+         ;;
+       *.la)
+         func_resolve_sysroot "$deplib"
+         lib=$func_resolve_sysroot_result
+         ;;
+       *.$libext)
+         if test "$pass" = conv; then
+           deplibs="$deplib $deplibs"
+           continue
+         fi
+         case $linkmode in
+         lib)
+           # Linking convenience modules into shared libraries is allowed,
+           # but linking other static libraries is non-portable.
+           case " $dlpreconveniencelibs " in
+           *" $deplib "*) ;;
+           *)
+             valid_a_lib=no
+             case $deplibs_check_method in
+               match_pattern*)
+                 set dummy $deplibs_check_method; shift
+                 match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+                 if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \
+                   | $EGREP "$match_pattern_regex" > /dev/null; then
+                   valid_a_lib=yes
+                 fi
+               ;;
+               pass_all)
+                 valid_a_lib=yes
+               ;;
+             esac
+             if test "$valid_a_lib" != yes; then
+               echo
+               $ECHO "*** Warning: Trying to link with static lib archive $deplib."
+               echo "*** I have the capability to make that library automatically link in when"
+               echo "*** you link to this library.  But I can only do this if you have a"
+               echo "*** shared version of the library, which you do not appear to have"
+               echo "*** because the file extensions .$libext of this argument makes me believe"
+               echo "*** that it is just a static archive that I should not use here."
+             else
+               echo
+               $ECHO "*** Warning: Linking the shared library $output against the"
+               $ECHO "*** static library $deplib is not portable!"
+               deplibs="$deplib $deplibs"
+             fi
+             ;;
+           esac
+           continue
+           ;;
+         prog)
+           if test "$pass" != link; then
+             deplibs="$deplib $deplibs"
+           else
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           fi
+           continue
+           ;;
+         esac # linkmode
+         ;; # *.$libext
+       *.lo | *.$objext)
+         if test "$pass" = conv; then
+           deplibs="$deplib $deplibs"
+         elif test "$linkmode" = prog; then
+           if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+             # If there is no dlopen support or we're linking statically,
+             # we need to preload.
+             func_append newdlprefiles " $deplib"
+             compile_deplibs="$deplib $compile_deplibs"
+             finalize_deplibs="$deplib $finalize_deplibs"
+           else
+             func_append newdlfiles " $deplib"
+           fi
+         fi
+         continue
+         ;;
+       %DEPLIBS%)
+         alldeplibs=yes
+         continue
+         ;;
+       esac # case $deplib
+
+       if test "$found" = yes || test -f "$lib"; then :
+       else
+         func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'"
+       fi
+
+       # Check to see that this really is a libtool archive.
+       func_lalib_unsafe_p "$lib" \
+         || func_fatal_error "\`$lib' is not a valid libtool archive"
+
+       func_dirname "$lib" "" "."
+       ladir="$func_dirname_result"
+
+       dlname=
+       dlopen=
+       dlpreopen=
+       libdir=
+       library_names=
+       old_library=
+       inherited_linker_flags=
+       # If the library was installed with an old release of libtool,
+       # it will not redefine variables installed, or shouldnotlink
+       installed=yes
+       shouldnotlink=no
+       avoidtemprpath=
+
+
+       # Read the .la file
+       func_source "$lib"
+
+       # Convert "-framework foo" to "foo.ltframework"
+       if test -n "$inherited_linker_flags"; then
+         tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'`
+         for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+           case " $new_inherited_linker_flags " in
+             *" $tmp_inherited_linker_flag "*) ;;
+             *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";;
+           esac
+         done
+       fi
+       dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+       if test "$linkmode,$pass" = "lib,link" ||
+          test "$linkmode,$pass" = "prog,scan" ||
+          { test "$linkmode" != prog && test "$linkmode" != lib; }; then
+         test -n "$dlopen" && func_append dlfiles " $dlopen"
+         test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen"
+       fi
+
+       if test "$pass" = conv; then
+         # Only check for convenience libraries
+         deplibs="$lib $deplibs"
+         if test -z "$libdir"; then
+           if test -z "$old_library"; then
+             func_fatal_error "cannot find name of link library for \`$lib'"
+           fi
+           # It is a libtool convenience library, so add in its objects.
+           func_append convenience " $ladir/$objdir/$old_library"
+           func_append old_convenience " $ladir/$objdir/$old_library"
+           tmp_libs=
+           for deplib in $dependency_libs; do
+             deplibs="$deplib $deplibs"
+             if $opt_preserve_dup_deps ; then
+               case "$tmp_libs " in
+               *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+               esac
+             fi
+             func_append tmp_libs " $deplib"
+           done
+         elif test "$linkmode" != prog && test "$linkmode" != lib; then
+           func_fatal_error "\`$lib' is not a convenience library"
+         fi
+         continue
+       fi # $pass = conv
+
+
+       # Get the name of the library we link against.
+       linklib=
+       if test -n "$old_library" &&
+          { test "$prefer_static_libs" = yes ||
+            test "$prefer_static_libs,$installed" = "built,no"; }; then
+         linklib=$old_library
+       else
+         for l in $old_library $library_names; do
+           linklib="$l"
+         done
+       fi
+       if test -z "$linklib"; then
+         func_fatal_error "cannot find name of link library for \`$lib'"
+       fi
+
+       # This library was specified with -dlopen.
+       if test "$pass" = dlopen; then
+         if test -z "$libdir"; then
+           func_fatal_error "cannot -dlopen a convenience library: \`$lib'"
+         fi
+         if test -z "$dlname" ||
+            test "$dlopen_support" != yes ||
+            test "$build_libtool_libs" = no; then
+           # If there is no dlname, no dlopen support or we're linking
+           # statically, we need to preload.  We also need to preload any
+           # dependent libraries so libltdl's deplib preloader doesn't
+           # bomb out in the load deplibs phase.
+           func_append dlprefiles " $lib $dependency_libs"
+         else
+           func_append newdlfiles " $lib"
+         fi
+         continue
+       fi # $pass = dlopen
+
+       # We need an absolute path.
+       case $ladir in
+       [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;;
+       *)
+         abs_ladir=`cd "$ladir" && pwd`
+         if test -z "$abs_ladir"; then
+           func_warning "cannot determine absolute directory name of \`$ladir'"
+           func_warning "passing it literally to the linker, although it might fail"
+           abs_ladir="$ladir"
+         fi
+         ;;
+       esac
+       func_basename "$lib"
+       laname="$func_basename_result"
+
+       # Find the relevant object directory and library name.
+       if test "X$installed" = Xyes; then
+         if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+           func_warning "library \`$lib' was moved."
+           dir="$ladir"
+           absdir="$abs_ladir"
+           libdir="$abs_ladir"
+         else
+           dir="$lt_sysroot$libdir"
+           absdir="$lt_sysroot$libdir"
+         fi
+         test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes
+       else
+         if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+           dir="$ladir"
+           absdir="$abs_ladir"
+           # Remove this search path later
+           func_append notinst_path " $abs_ladir"
+         else
+           dir="$ladir/$objdir"
+           absdir="$abs_ladir/$objdir"
+           # Remove this search path later
+           func_append notinst_path " $abs_ladir"
+         fi
+       fi # $installed = yes
+       func_stripname 'lib' '.la' "$laname"
+       name=$func_stripname_result
+
+       # This library was specified with -dlpreopen.
+       if test "$pass" = dlpreopen; then
+         if test -z "$libdir" && test "$linkmode" = prog; then
+           func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'"
+         fi
+         case "$host" in
+           # special handling for platforms with PE-DLLs.
+           *cygwin* | *mingw* | *cegcc* )
+             # Linker will automatically link against shared library if both
+             # static and shared are present.  Therefore, ensure we extract
+             # symbols from the import library if a shared library is present
+             # (otherwise, the dlopen module name will be incorrect).  We do
+             # this by putting the import library name into $newdlprefiles.
+             # We recover the dlopen module name by 'saving' the la file
+             # name in a special purpose variable, and (later) extracting the
+             # dlname from the la file.
+             if test -n "$dlname"; then
+               func_tr_sh "$dir/$linklib"
+               eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname"
+               func_append newdlprefiles " $dir/$linklib"
+             else
+               func_append newdlprefiles " $dir/$old_library"
+               # Keep a list of preopened convenience libraries to check
+               # that they are being used correctly in the link pass.
+               test -z "$libdir" && \
+                 func_append dlpreconveniencelibs " $dir/$old_library"
+             fi
+           ;;
+           * )
+             # Prefer using a static library (so that no silly _DYNAMIC symbols
+             # are required to link).
+             if test -n "$old_library"; then
+               func_append newdlprefiles " $dir/$old_library"
+               # Keep a list of preopened convenience libraries to check
+               # that they are being used correctly in the link pass.
+               test -z "$libdir" && \
+                 func_append dlpreconveniencelibs " $dir/$old_library"
+             # Otherwise, use the dlname, so that lt_dlopen finds it.
+             elif test -n "$dlname"; then
+               func_append newdlprefiles " $dir/$dlname"
+             else
+               func_append newdlprefiles " $dir/$linklib"
+             fi
+           ;;
+         esac
+       fi # $pass = dlpreopen
+
+       if test -z "$libdir"; then
+         # Link the convenience library
+         if test "$linkmode" = lib; then
+           deplibs="$dir/$old_library $deplibs"
+         elif test "$linkmode,$pass" = "prog,link"; then
+           compile_deplibs="$dir/$old_library $compile_deplibs"
+           finalize_deplibs="$dir/$old_library $finalize_deplibs"
+         else
+           deplibs="$lib $deplibs" # used for prog,scan pass
+         fi
+         continue
+       fi
+
+
+       if test "$linkmode" = prog && test "$pass" != link; then
+         func_append newlib_search_path " $ladir"
+         deplibs="$lib $deplibs"
+
+         linkalldeplibs=no
+         if test "$link_all_deplibs" != no || test -z "$library_names" ||
+            test "$build_libtool_libs" = no; then
+           linkalldeplibs=yes
+         fi
+
+         tmp_libs=
+         for deplib in $dependency_libs; do
+           case $deplib in
+           -L*) func_stripname '-L' '' "$deplib"
+                func_resolve_sysroot "$func_stripname_result"
+                func_append newlib_search_path " $func_resolve_sysroot_result"
+                ;;
+           esac
+           # Need to link against all dependency_libs?
+           if test "$linkalldeplibs" = yes; then
+             deplibs="$deplib $deplibs"
+           else
+             # Need to hardcode shared library paths
+             # or/and link against static libraries
+             newdependency_libs="$deplib $newdependency_libs"
+           fi
+           if $opt_preserve_dup_deps ; then
+             case "$tmp_libs " in
+             *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+             esac
+           fi
+           func_append tmp_libs " $deplib"
+         done # for deplib
+         continue
+       fi # $linkmode = prog...
+
+       if test "$linkmode,$pass" = "prog,link"; then
+         if test -n "$library_names" &&
+            { { test "$prefer_static_libs" = no ||
+                test "$prefer_static_libs,$installed" = "built,yes"; } ||
+              test -z "$old_library"; }; then
+           # We need to hardcode the library path
+           if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then
+             # Make sure the rpath contains only unique directories.
+             case "$temp_rpath:" in
+             *"$absdir:"*) ;;
+             *) func_append temp_rpath "$absdir:" ;;
+             esac
+           fi
+
+           # Hardcode the library path.
+           # Skip directories that are in the system default run-time
+           # search path.
+           case " $sys_lib_dlsearch_path " in
+           *" $absdir "*) ;;
+           *)
+             case "$compile_rpath " in
+             *" $absdir "*) ;;
+             *) func_append compile_rpath " $absdir" ;;
+             esac
+             ;;
+           esac
+           case " $sys_lib_dlsearch_path " in
+           *" $libdir "*) ;;
+           *)
+             case "$finalize_rpath " in
+             *" $libdir "*) ;;
+             *) func_append finalize_rpath " $libdir" ;;
+             esac
+             ;;
+           esac
+         fi # $linkmode,$pass = prog,link...
+
+         if test "$alldeplibs" = yes &&
+            { test "$deplibs_check_method" = pass_all ||
+              { test "$build_libtool_libs" = yes &&
+                test -n "$library_names"; }; }; then
+           # We only need to search for static libraries
+           continue
+         fi
+       fi
+
+       link_static=no # Whether the deplib will be linked statically
+       use_static_libs=$prefer_static_libs
+       if test "$use_static_libs" = built && test "$installed" = yes; then
+         use_static_libs=no
+       fi
+       if test -n "$library_names" &&
+          { test "$use_static_libs" = no || test -z "$old_library"; }; then
+         case $host in
+         *cygwin* | *mingw* | *cegcc*)
+             # No point in relinking DLLs because paths are not encoded
+             func_append notinst_deplibs " $lib"
+             need_relink=no
+           ;;
+         *)
+           if test "$installed" = no; then
+             func_append notinst_deplibs " $lib"
+             need_relink=yes
+           fi
+           ;;
+         esac
+         # This is a shared library
+
+         # Warn about portability, can't link against -module's on some
+         # systems (darwin).  Don't bleat about dlopened modules though!
+         dlopenmodule=""
+         for dlpremoduletest in $dlprefiles; do
+           if test "X$dlpremoduletest" = "X$lib"; then
+             dlopenmodule="$dlpremoduletest"
+             break
+           fi
+         done
+         if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then
+           echo
+           if test "$linkmode" = prog; then
+             $ECHO "*** Warning: Linking the executable $output against the loadable module"
+           else
+             $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+           fi
+           $ECHO "*** $linklib is not portable!"
+         fi
+         if test "$linkmode" = lib &&
+            test "$hardcode_into_libs" = yes; then
+           # Hardcode the library path.
+           # Skip directories that are in the system default run-time
+           # search path.
+           case " $sys_lib_dlsearch_path " in
+           *" $absdir "*) ;;
+           *)
+             case "$compile_rpath " in
+             *" $absdir "*) ;;
+             *) func_append compile_rpath " $absdir" ;;
+             esac
+             ;;
+           esac
+           case " $sys_lib_dlsearch_path " in
+           *" $libdir "*) ;;
+           *)
+             case "$finalize_rpath " in
+             *" $libdir "*) ;;
+             *) func_append finalize_rpath " $libdir" ;;
+             esac
+             ;;
+           esac
+         fi
+
+         if test -n "$old_archive_from_expsyms_cmds"; then
+           # figure out the soname
+           set dummy $library_names
+           shift
+           realname="$1"
+           shift
+           libname=`eval "\\$ECHO \"$libname_spec\""`
+           # use dlname if we got it. it's perfectly good, no?
+           if test -n "$dlname"; then
+             soname="$dlname"
+           elif test -n "$soname_spec"; then
+             # bleh windows
+             case $host in
+             *cygwin* | mingw* | *cegcc*)
+               func_arith $current - $age
+               major=$func_arith_result
+               versuffix="-$major"
+               ;;
+             esac
+             eval soname=\"$soname_spec\"
+           else
+             soname="$realname"
+           fi
+
+           # Make a new name for the extract_expsyms_cmds to use
+           soroot="$soname"
+           func_basename "$soroot"
+           soname="$func_basename_result"
+           func_stripname 'lib' '.dll' "$soname"
+           newlib=libimp-$func_stripname_result.a
+
+           # If the library has no export list, then create one now
+           if test -f "$output_objdir/$soname-def"; then :
+           else
+             func_verbose "extracting exported symbol list from \`$soname'"
+             func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+           fi
+
+           # Create $newlib
+           if test -f "$output_objdir/$newlib"; then :; else
+             func_verbose "generating import library for \`$soname'"
+             func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+           fi
+           # make sure the library variables are pointing to the new library
+           dir=$output_objdir
+           linklib=$newlib
+         fi # test -n "$old_archive_from_expsyms_cmds"
+
+         if test "$linkmode" = prog || test "$opt_mode" != relink; then
+           add_shlibpath=
+           add_dir=
+           add=
+           lib_linked=yes
+           case $hardcode_action in
+           immediate | unsupported)
+             if test "$hardcode_direct" = no; then
+               add="$dir/$linklib"
+               case $host in
+                 *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;;
+                 *-*-sysv4*uw2*) add_dir="-L$dir" ;;
+                 *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+                   *-*-unixware7*) add_dir="-L$dir" ;;
+                 *-*-darwin* )
+                   # if the lib is a (non-dlopened) module then we can not
+                   # link against it, someone is ignoring the earlier warnings
+                   if /usr/bin/file -L $add 2> /dev/null |
+                        $GREP ": [^:]* bundle" >/dev/null ; then
+                     if test "X$dlopenmodule" != "X$lib"; then
+                       $ECHO "*** Warning: lib $linklib is a module, not a shared library"
+                       if test -z "$old_library" ; then
+                         echo
+                         echo "*** And there doesn't seem to be a static archive available"
+                         echo "*** The link will probably fail, sorry"
+                       else
+                         add="$dir/$old_library"
+                       fi
+                     elif test -n "$old_library"; then
+                       add="$dir/$old_library"
+                     fi
+                   fi
+               esac
+             elif test "$hardcode_minus_L" = no; then
+               case $host in
+               *-*-sunos*) add_shlibpath="$dir" ;;
+               esac
+               add_dir="-L$dir"
+               add="-l$name"
+             elif test "$hardcode_shlibpath_var" = no; then
+               add_shlibpath="$dir"
+               add="-l$name"
+             else
+               lib_linked=no
+             fi
+             ;;
+           relink)
+             if test "$hardcode_direct" = yes &&
+                test "$hardcode_direct_absolute" = no; then
+               add="$dir/$linklib"
+             elif test "$hardcode_minus_L" = yes; then
+               add_dir="-L$absdir"
+               # Try looking first in the location we're being installed to.
+               if test -n "$inst_prefix_dir"; then
+                 case $libdir in
+                   [\\/]*)
+                     func_append add_dir " -L$inst_prefix_dir$libdir"
+                     ;;
+                 esac
+               fi
+               add="-l$name"
+             elif test "$hardcode_shlibpath_var" = yes; then
+               add_shlibpath="$dir"
+               add="-l$name"
+             else
+               lib_linked=no
+             fi
+             ;;
+           *) lib_linked=no ;;
+           esac
+
+           if test "$lib_linked" != yes; then
+             func_fatal_configuration "unsupported hardcode properties"
+           fi
+
+           if test -n "$add_shlibpath"; then
+             case :$compile_shlibpath: in
+             *":$add_shlibpath:"*) ;;
+             *) func_append compile_shlibpath "$add_shlibpath:" ;;
+             esac
+           fi
+           if test "$linkmode" = prog; then
+             test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+             test -n "$add" && compile_deplibs="$add $compile_deplibs"
+           else
+             test -n "$add_dir" && deplibs="$add_dir $deplibs"
+             test -n "$add" && deplibs="$add $deplibs"
+             if test "$hardcode_direct" != yes &&
+                test "$hardcode_minus_L" != yes &&
+                test "$hardcode_shlibpath_var" = yes; then
+               case :$finalize_shlibpath: in
+               *":$libdir:"*) ;;
+               *) func_append finalize_shlibpath "$libdir:" ;;
+               esac
+             fi
+           fi
+         fi
+
+         if test "$linkmode" = prog || test "$opt_mode" = relink; then
+           add_shlibpath=
+           add_dir=
+           add=
+           # Finalize command for both is simple: just hardcode it.
+           if test "$hardcode_direct" = yes &&
+              test "$hardcode_direct_absolute" = no; then
+             add="$libdir/$linklib"
+           elif test "$hardcode_minus_L" = yes; then
+             add_dir="-L$libdir"
+             add="-l$name"
+           elif test "$hardcode_shlibpath_var" = yes; then
+             case :$finalize_shlibpath: in
+             *":$libdir:"*) ;;
+             *) func_append finalize_shlibpath "$libdir:" ;;
+             esac
+             add="-l$name"
+           elif test "$hardcode_automatic" = yes; then
+             if test -n "$inst_prefix_dir" &&
+                test -f "$inst_prefix_dir$libdir/$linklib" ; then
+               add="$inst_prefix_dir$libdir/$linklib"
+             else
+               add="$libdir/$linklib"
+             fi
+           else
+             # We cannot seem to hardcode it, guess we'll fake it.
+             add_dir="-L$libdir"
+             # Try looking first in the location we're being installed to.
+             if test -n "$inst_prefix_dir"; then
+               case $libdir in
+                 [\\/]*)
+                   func_append add_dir " -L$inst_prefix_dir$libdir"
+                   ;;
+               esac
+             fi
+             add="-l$name"
+           fi
+
+           if test "$linkmode" = prog; then
+             test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+             test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+           else
+             test -n "$add_dir" && deplibs="$add_dir $deplibs"
+             test -n "$add" && deplibs="$add $deplibs"
+           fi
+         fi
+       elif test "$linkmode" = prog; then
+         # Here we assume that one of hardcode_direct or hardcode_minus_L
+         # is not unsupported.  This is valid on all known static and
+         # shared platforms.
+         if test "$hardcode_direct" != unsupported; then
+           test -n "$old_library" && linklib="$old_library"
+           compile_deplibs="$dir/$linklib $compile_deplibs"
+           finalize_deplibs="$dir/$linklib $finalize_deplibs"
+         else
+           compile_deplibs="-l$name -L$dir $compile_deplibs"
+           finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+         fi
+       elif test "$build_libtool_libs" = yes; then
+         # Not a shared library
+         if test "$deplibs_check_method" != pass_all; then
+           # We're trying link a shared library against a static one
+           # but the system doesn't support it.
+
+           # Just print a warning and add the library to dependency_libs so
+           # that the program can be linked against the static library.
+           echo
+           $ECHO "*** Warning: This system can not link to static lib archive $lib."
+           echo "*** I have the capability to make that library automatically link in when"
+           echo "*** you link to this library.  But I can only do this if you have a"
+           echo "*** shared version of the library, which you do not appear to have."
+           if test "$module" = yes; then
+             echo "*** But as you try to build a module library, libtool will still create "
+             echo "*** a static module, that should work as long as the dlopening application"
+             echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
+             if test -z "$global_symbol_pipe"; then
+               echo
+               echo "*** However, this would only work if libtool was able to extract symbol"
+               echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+               echo "*** not find such a program.  So, this module is probably useless."
+               echo "*** \`nm' from GNU binutils and a full rebuild may help."
+             fi
+             if test "$build_old_libs" = no; then
+               build_libtool_libs=module
+               build_old_libs=yes
+             else
+               build_libtool_libs=no
+             fi
+           fi
+         else
+           deplibs="$dir/$old_library $deplibs"
+           link_static=yes
+         fi
+       fi # link shared/static library?
+
+       if test "$linkmode" = lib; then
+         if test -n "$dependency_libs" &&
+            { test "$hardcode_into_libs" != yes ||
+              test "$build_old_libs" = yes ||
+              test "$link_static" = yes; }; then
+           # Extract -R from dependency_libs
+           temp_deplibs=
+           for libdir in $dependency_libs; do
+             case $libdir in
+             -R*) func_stripname '-R' '' "$libdir"
+                  temp_xrpath=$func_stripname_result
+                  case " $xrpath " in
+                  *" $temp_xrpath "*) ;;
+                  *) func_append xrpath " $temp_xrpath";;
+                  esac;;
+             *) func_append temp_deplibs " $libdir";;
+             esac
+           done
+           dependency_libs="$temp_deplibs"
+         fi
+
+         func_append newlib_search_path " $absdir"
+         # Link against this library
+         test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+         # ... and its dependency_libs
+         tmp_libs=
+         for deplib in $dependency_libs; do
+           newdependency_libs="$deplib $newdependency_libs"
+           case $deplib in
+              -L*) func_stripname '-L' '' "$deplib"
+                   func_resolve_sysroot "$func_stripname_result";;
+              *) func_resolve_sysroot "$deplib" ;;
+            esac
+           if $opt_preserve_dup_deps ; then
+             case "$tmp_libs " in
+             *" $func_resolve_sysroot_result "*)
+                func_append specialdeplibs " $func_resolve_sysroot_result" ;;
+             esac
+           fi
+           func_append tmp_libs " $func_resolve_sysroot_result"
+         done
+
+         if test "$link_all_deplibs" != no; then
+           # Add the search paths of all dependency libraries
+           for deplib in $dependency_libs; do
+             path=
+             case $deplib in
+             -L*) path="$deplib" ;;
+             *.la)
+               func_resolve_sysroot "$deplib"
+               deplib=$func_resolve_sysroot_result
+               func_dirname "$deplib" "" "."
+               dir=$func_dirname_result
+               # We need an absolute path.
+               case $dir in
+               [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+               *)
+                 absdir=`cd "$dir" && pwd`
+                 if test -z "$absdir"; then
+                   func_warning "cannot determine absolute directory name of \`$dir'"
+                   absdir="$dir"
+                 fi
+                 ;;
+               esac
+               if $GREP "^installed=no" $deplib > /dev/null; then
+               case $host in
+               *-*-darwin*)
+                 depdepl=
+                 eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+                 if test -n "$deplibrary_names" ; then
+                   for tmp in $deplibrary_names ; do
+                     depdepl=$tmp
+                   done
+                   if test -f "$absdir/$objdir/$depdepl" ; then
+                     depdepl="$absdir/$objdir/$depdepl"
+                     darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+                      if test -z "$darwin_install_name"; then
+                          darwin_install_name=`${OTOOL64} -L $depdepl  | awk '{if (NR == 2) {print $1;exit}}'`
+                      fi
+                     func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}"
+                     func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}"
+                     path=
+                   fi
+                 fi
+                 ;;
+               *)
+                 path="-L$absdir/$objdir"
+                 ;;
+               esac
+               else
+                 eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+                 test -z "$libdir" && \
+                   func_fatal_error "\`$deplib' is not a valid libtool archive"
+                 test "$absdir" != "$libdir" && \
+                   func_warning "\`$deplib' seems to be moved"
+
+                 path="-L$absdir"
+               fi
+               ;;
+             esac
+             case " $deplibs " in
+             *" $path "*) ;;
+             *) deplibs="$path $deplibs" ;;
+             esac
+           done
+         fi # link_all_deplibs != no
+       fi # linkmode = lib
+      done # for deplib in $libs
+      if test "$pass" = link; then
+       if test "$linkmode" = "prog"; then
+         compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+         finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+       else
+         compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+       fi
+      fi
+      dependency_libs="$newdependency_libs"
+      if test "$pass" = dlpreopen; then
+       # Link the dlpreopened libraries before other libraries
+       for deplib in $save_deplibs; do
+         deplibs="$deplib $deplibs"
+       done
+      fi
+      if test "$pass" != dlopen; then
+       if test "$pass" != conv; then
+         # Make sure lib_search_path contains only unique directories.
+         lib_search_path=
+         for dir in $newlib_search_path; do
+           case "$lib_search_path " in
+           *" $dir "*) ;;
+           *) func_append lib_search_path " $dir" ;;
+           esac
+         done
+         newlib_search_path=
+       fi
+
+       if test "$linkmode,$pass" != "prog,link"; then
+         vars="deplibs"
+       else
+         vars="compile_deplibs finalize_deplibs"
+       fi
+       for var in $vars dependency_libs; do
+         # Add libraries to $var in reverse order
+         eval tmp_libs=\"\$$var\"
+         new_libs=
+         for deplib in $tmp_libs; do
+           # FIXME: Pedantically, this is the right thing to do, so
+           #        that some nasty dependency loop isn't accidentally
+           #        broken:
+           #new_libs="$deplib $new_libs"
+           # Pragmatically, this seems to cause very few problems in
+           # practice:
+           case $deplib in
+           -L*) new_libs="$deplib $new_libs" ;;
+           -R*) ;;
+           *)
+             # And here is the reason: when a library appears more
+             # than once as an explicit dependence of a library, or
+             # is implicitly linked in more than once by the
+             # compiler, it is considered special, and multiple
+             # occurrences thereof are not removed.  Compare this
+             # with having the same library being listed as a
+             # dependency of multiple other libraries: in this case,
+             # we know (pedantically, we assume) the library does not
+             # need to be listed more than once, so we keep only the
+             # last copy.  This is not always right, but it is rare
+             # enough that we require users that really mean to play
+             # such unportable linking tricks to link the library
+             # using -Wl,-lname, so that libtool does not consider it
+             # for duplicate removal.
+             case " $specialdeplibs " in
+             *" $deplib "*) new_libs="$deplib $new_libs" ;;
+             *)
+               case " $new_libs " in
+               *" $deplib "*) ;;
+               *) new_libs="$deplib $new_libs" ;;
+               esac
+               ;;
+             esac
+             ;;
+           esac
+         done
+         tmp_libs=
+         for deplib in $new_libs; do
+           case $deplib in
+           -L*)
+             case " $tmp_libs " in
+             *" $deplib "*) ;;
+             *) func_append tmp_libs " $deplib" ;;
+             esac
+             ;;
+           *) func_append tmp_libs " $deplib" ;;
+           esac
+         done
+         eval $var=\"$tmp_libs\"
+       done # for var
+      fi
+      # Last step: remove runtime libs from dependency_libs
+      # (they stay in deplibs)
+      tmp_libs=
+      for i in $dependency_libs ; do
+       case " $predeps $postdeps $compiler_lib_search_path " in
+       *" $i "*)
+         i=""
+         ;;
+       esac
+       if test -n "$i" ; then
+         func_append tmp_libs " $i"
+       fi
+      done
+      dependency_libs=$tmp_libs
+    done # for pass
+    if test "$linkmode" = prog; then
+      dlfiles="$newdlfiles"
+    fi
+    if test "$linkmode" = prog || test "$linkmode" = lib; then
+      dlprefiles="$newdlprefiles"
+    fi
+
+    case $linkmode in
+    oldlib)
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+       func_warning "\`-dlopen' is ignored for archives"
+      fi
+
+      case " $deplibs" in
+      *\ -l* | *\ -L*)
+       func_warning "\`-l' and \`-L' are ignored for archives" ;;
+      esac
+
+      test -n "$rpath" && \
+       func_warning "\`-rpath' is ignored for archives"
+
+      test -n "$xrpath" && \
+       func_warning "\`-R' is ignored for archives"
+
+      test -n "$vinfo" && \
+       func_warning "\`-version-info/-version-number' is ignored for archives"
+
+      test -n "$release" && \
+       func_warning "\`-release' is ignored for archives"
+
+      test -n "$export_symbols$export_symbols_regex" && \
+       func_warning "\`-export-symbols' is ignored for archives"
+
+      # Now set the variables for building old libraries.
+      build_libtool_libs=no
+      oldlibs="$output"
+      func_append objs "$old_deplibs"
+      ;;
+
+    lib)
+      # Make sure we only generate libraries of the form `libNAME.la'.
+      case $outputname in
+      lib*)
+       func_stripname 'lib' '.la' "$outputname"
+       name=$func_stripname_result
+       eval shared_ext=\"$shrext_cmds\"
+       eval libname=\"$libname_spec\"
+       ;;
+      *)
+       test "$module" = no && \
+         func_fatal_help "libtool library \`$output' must begin with \`lib'"
+
+       if test "$need_lib_prefix" != no; then
+         # Add the "lib" prefix for modules if required
+         func_stripname '' '.la' "$outputname"
+         name=$func_stripname_result
+         eval shared_ext=\"$shrext_cmds\"
+         eval libname=\"$libname_spec\"
+       else
+         func_stripname '' '.la' "$outputname"
+         libname=$func_stripname_result
+       fi
+       ;;
+      esac
+
+      if test -n "$objs"; then
+       if test "$deplibs_check_method" != pass_all; then
+         func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs"
+       else
+         echo
+         $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+         $ECHO "*** objects $objs is not portable!"
+         func_append libobjs " $objs"
+       fi
+      fi
+
+      test "$dlself" != no && \
+       func_warning "\`-dlopen self' is ignored for libtool libraries"
+
+      set dummy $rpath
+      shift
+      test "$#" -gt 1 && \
+       func_warning "ignoring multiple \`-rpath's for a libtool library"
+
+      install_libdir="$1"
+
+      oldlibs=
+      if test -z "$rpath"; then
+       if test "$build_libtool_libs" = yes; then
+         # Building a libtool convenience library.
+         # Some compilers have problems with a `.al' extension so
+         # convenience libraries should have the same extension an
+         # archive normally would.
+         oldlibs="$output_objdir/$libname.$libext $oldlibs"
+         build_libtool_libs=convenience
+         build_old_libs=yes
+       fi
+
+       test -n "$vinfo" && \
+         func_warning "\`-version-info/-version-number' is ignored for convenience libraries"
+
+       test -n "$release" && \
+         func_warning "\`-release' is ignored for convenience libraries"
+      else
+
+       # Parse the version information argument.
+       save_ifs="$IFS"; IFS=':'
+       set dummy $vinfo 0 0 0
+       shift
+       IFS="$save_ifs"
+
+       test -n "$7" && \
+         func_fatal_help "too many parameters to \`-version-info'"
+
+       # convert absolute version numbers to libtool ages
+       # this retains compatibility with .la files and attempts
+       # to make the code below a bit more comprehensible
+
+       case $vinfo_number in
+       yes)
+         number_major="$1"
+         number_minor="$2"
+         number_revision="$3"
+         #
+         # There are really only two kinds -- those that
+         # use the current revision as the major version
+         # and those that subtract age and use age as
+         # a minor version.  But, then there is irix
+         # which has an extra 1 added just for fun
+         #
+         case $version_type in
+         # correct linux to gnu/linux during the next big refactor
+         darwin|linux|osf|windows|none)
+           func_arith $number_major + $number_minor
+           current=$func_arith_result
+           age="$number_minor"
+           revision="$number_revision"
+           ;;
+         freebsd-aout|freebsd-elf|qnx|sunos)
+           current="$number_major"
+           revision="$number_minor"
+           age="0"
+           ;;
+         irix|nonstopux)
+           func_arith $number_major + $number_minor
+           current=$func_arith_result
+           age="$number_minor"
+           revision="$number_minor"
+           lt_irix_increment=no
+           ;;
+         *)
+           func_fatal_configuration "$modename: unknown library version type \`$version_type'"
+           ;;
+         esac
+         ;;
+       no)
+         current="$1"
+         revision="$2"
+         age="$3"
+         ;;
+       esac
+
+       # Check that each of the things are valid numbers.
+       case $current in
+       0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+       *)
+         func_error "CURRENT \`$current' must be a nonnegative integer"
+         func_fatal_error "\`$vinfo' is not valid version information"
+         ;;
+       esac
+
+       case $revision in
+       0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+       *)
+         func_error "REVISION \`$revision' must be a nonnegative integer"
+         func_fatal_error "\`$vinfo' is not valid version information"
+         ;;
+       esac
+
+       case $age in
+       0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+       *)
+         func_error "AGE \`$age' must be a nonnegative integer"
+         func_fatal_error "\`$vinfo' is not valid version information"
+         ;;
+       esac
+
+       if test "$age" -gt "$current"; then
+         func_error "AGE \`$age' is greater than the current interface number \`$current'"
+         func_fatal_error "\`$vinfo' is not valid version information"
+       fi
+
+       # Calculate the version variables.
+       major=
+       versuffix=
+       verstring=
+       case $version_type in
+       none) ;;
+
+       darwin)
+         # Like Linux, but with the current version available in
+         # verstring for coding it into the library header
+         func_arith $current - $age
+         major=.$func_arith_result
+         versuffix="$major.$age.$revision"
+         # Darwin ld doesn't like 0 for these options...
+         func_arith $current + 1
+         minor_current=$func_arith_result
+         xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
+         verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+         ;;
+
+       freebsd-aout)
+         major=".$current"
+         versuffix=".$current.$revision";
+         ;;
+
+       freebsd-elf)
+         major=".$current"
+         versuffix=".$current"
+         ;;
+
+       irix | nonstopux)
+         if test "X$lt_irix_increment" = "Xno"; then
+           func_arith $current - $age
+         else
+           func_arith $current - $age + 1
+         fi
+         major=$func_arith_result
+
+         case $version_type in
+           nonstopux) verstring_prefix=nonstopux ;;
+           *)         verstring_prefix=sgi ;;
+         esac
+         verstring="$verstring_prefix$major.$revision"
+
+         # Add in all the interfaces that we are compatible with.
+         loop=$revision
+         while test "$loop" -ne 0; do
+           func_arith $revision - $loop
+           iface=$func_arith_result
+           func_arith $loop - 1
+           loop=$func_arith_result
+           verstring="$verstring_prefix$major.$iface:$verstring"
+         done
+
+         # Before this point, $major must not contain `.'.
+         major=.$major
+         versuffix="$major.$revision"
+         ;;
+
+       linux) # correct to gnu/linux during the next big refactor
+         func_arith $current - $age
+         major=.$func_arith_result
+         versuffix="$major.$age.$revision"
+         ;;
+
+       osf)
+         func_arith $current - $age
+         major=.$func_arith_result
+         versuffix=".$current.$age.$revision"
+         verstring="$current.$age.$revision"
+
+         # Add in all the interfaces that we are compatible with.
+         loop=$age
+         while test "$loop" -ne 0; do
+           func_arith $current - $loop
+           iface=$func_arith_result
+           func_arith $loop - 1
+           loop=$func_arith_result
+           verstring="$verstring:${iface}.0"
+         done
+
+         # Make executables depend on our current version.
+         func_append verstring ":${current}.0"
+         ;;
+
+       qnx)
+         major=".$current"
+         versuffix=".$current"
+         ;;
+
+       sunos)
+         major=".$current"
+         versuffix=".$current.$revision"
+         ;;
+
+       windows)
+         # Use '-' rather than '.', since we only want one
+         # extension on DOS 8.3 filesystems.
+         func_arith $current - $age
+         major=$func_arith_result
+         versuffix="-$major"
+         ;;
+
+       *)
+         func_fatal_configuration "unknown library version type \`$version_type'"
+         ;;
+       esac
+
+       # Clear the version info if we defaulted, and they specified a release.
+       if test -z "$vinfo" && test -n "$release"; then
+         major=
+         case $version_type in
+         darwin)
+           # we can't check for "0.0" in archive_cmds due to quoting
+           # problems, so we reset it completely
+           verstring=
+           ;;
+         *)
+           verstring="0.0"
+           ;;
+         esac
+         if test "$need_version" = no; then
+           versuffix=
+         else
+           versuffix=".0.0"
+         fi
+       fi
+
+       # Remove version info from name if versioning should be avoided
+       if test "$avoid_version" = yes && test "$need_version" = no; then
+         major=
+         versuffix=
+         verstring=""
+       fi
+
+       # Check to see if the archive will have undefined symbols.
+       if test "$allow_undefined" = yes; then
+         if test "$allow_undefined_flag" = unsupported; then
+           func_warning "undefined symbols not allowed in $host shared libraries"
+           build_libtool_libs=no
+           build_old_libs=yes
+         fi
+       else
+         # Don't allow undefined symbols.
+         allow_undefined_flag="$no_undefined_flag"
+       fi
+
+      fi
+
+      func_generate_dlsyms "$libname" "$libname" "yes"
+      func_append libobjs " $symfileobj"
+      test "X$libobjs" = "X " && libobjs=
+
+      if test "$opt_mode" != relink; then
+       # Remove our outputs, but don't remove object files since they
+       # may have been created when compiling PIC objects.
+       removelist=
+       tempremovelist=`$ECHO "$output_objdir/*"`
+       for p in $tempremovelist; do
+         case $p in
+           *.$objext | *.gcno)
+              ;;
+           $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*)
+              if test "X$precious_files_regex" != "X"; then
+                if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+                then
+                  continue
+                fi
+              fi
+              func_append removelist " $p"
+              ;;
+           *) ;;
+         esac
+       done
+       test -n "$removelist" && \
+         func_show_eval "${RM}r \$removelist"
+      fi
+
+      # Now set the variables for building old libraries.
+      if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+       func_append oldlibs " $output_objdir/$libname.$libext"
+
+       # Transform .lo files to .o files.
+       oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP`
+      fi
+
+      # Eliminate all temporary directories.
+      #for path in $notinst_path; do
+      #        lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"`
+      #        deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"`
+      #        dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"`
+      #done
+
+      if test -n "$xrpath"; then
+       # If the user specified any rpath flags, then add them.
+       temp_xrpath=
+       for libdir in $xrpath; do
+         func_replace_sysroot "$libdir"
+         func_append temp_xrpath " -R$func_replace_sysroot_result"
+         case "$finalize_rpath " in
+         *" $libdir "*) ;;
+         *) func_append finalize_rpath " $libdir" ;;
+         esac
+       done
+       if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then
+         dependency_libs="$temp_xrpath $dependency_libs"
+       fi
+      fi
+
+      # Make sure dlfiles contains only unique files that won't be dlpreopened
+      old_dlfiles="$dlfiles"
+      dlfiles=
+      for lib in $old_dlfiles; do
+       case " $dlprefiles $dlfiles " in
+       *" $lib "*) ;;
+       *) func_append dlfiles " $lib" ;;
+       esac
+      done
+
+      # Make sure dlprefiles contains only unique files
+      old_dlprefiles="$dlprefiles"
+      dlprefiles=
+      for lib in $old_dlprefiles; do
+       case "$dlprefiles " in
+       *" $lib "*) ;;
+       *) func_append dlprefiles " $lib" ;;
+       esac
+      done
+
+      if test "$build_libtool_libs" = yes; then
+       if test -n "$rpath"; then
+         case $host in
+         *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
+           # these systems don't actually have a c library (as such)!
+           ;;
+         *-*-rhapsody* | *-*-darwin1.[012])
+           # Rhapsody C library is in the System framework
+           func_append deplibs " System.ltframework"
+           ;;
+         *-*-netbsd*)
+           # Don't link with libc until the a.out ld.so is fixed.
+           ;;
+         *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+           # Do not include libc due to us having libc/libc_r.
+           ;;
+         *-*-sco3.2v5* | *-*-sco5v6*)
+           # Causes problems with __ctype
+           ;;
+         *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+           # Compiler inserts libc in the correct place for threads to work
+           ;;
+         *)
+           # Add libc to deplibs on all other systems if necessary.
+           if test "$build_libtool_need_lc" = "yes"; then
+             func_append deplibs " -lc"
+           fi
+           ;;
+         esac
+       fi
+
+       # Transform deplibs into only deplibs that can be linked in shared.
+       name_save=$name
+       libname_save=$libname
+       release_save=$release
+       versuffix_save=$versuffix
+       major_save=$major
+       # I'm not sure if I'm treating the release correctly.  I think
+       # release should show up in the -l (ie -lgmp5) so we don't want to
+       # add it in twice.  Is that correct?
+       release=""
+       versuffix=""
+       major=""
+       newdeplibs=
+       droppeddeps=no
+       case $deplibs_check_method in
+       pass_all)
+         # Don't check for shared/static.  Everything works.
+         # This might be a little naive.  We might want to check
+         # whether the library exists or not.  But this is on
+         # osf3 & osf4 and I'm not really sure... Just
+         # implementing what was already the behavior.
+         newdeplibs=$deplibs
+         ;;
+       test_compile)
+         # This code stresses the "libraries are programs" paradigm to its
+         # limits. Maybe even breaks it.  We compile a program, linking it
+         # against the deplibs as a proxy for the library.  Then we can check
+         # whether they linked in statically or dynamically with ldd.
+         $opt_dry_run || $RM conftest.c
+         cat > conftest.c <<EOF
+         int main() { return 0; }
+EOF
+         $opt_dry_run || $RM conftest
+         if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+           ldd_output=`ldd conftest`
+           for i in $deplibs; do
+             case $i in
+             -l*)
+               func_stripname -l '' "$i"
+               name=$func_stripname_result
+               if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+                 case " $predeps $postdeps " in
+                 *" $i "*)
+                   func_append newdeplibs " $i"
+                   i=""
+                   ;;
+                 esac
+               fi
+               if test -n "$i" ; then
+                 libname=`eval "\\$ECHO \"$libname_spec\""`
+                 deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+                 set dummy $deplib_matches; shift
+                 deplib_match=$1
+                 if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+                   func_append newdeplibs " $i"
+                 else
+                   droppeddeps=yes
+                   echo
+                   $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+                   echo "*** I have the capability to make that library automatically link in when"
+                   echo "*** you link to this library.  But I can only do this if you have a"
+                   echo "*** shared version of the library, which I believe you do not have"
+                   echo "*** because a test_compile did reveal that the linker did not use it for"
+                   echo "*** its dynamic dependency list that programs get resolved with at runtime."
+                 fi
+               fi
+               ;;
+             *)
+               func_append newdeplibs " $i"
+               ;;
+             esac
+           done
+         else
+           # Error occurred in the first compile.  Let's try to salvage
+           # the situation: Compile a separate program for each library.
+           for i in $deplibs; do
+             case $i in
+             -l*)
+               func_stripname -l '' "$i"
+               name=$func_stripname_result
+               $opt_dry_run || $RM conftest
+               if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+                 ldd_output=`ldd conftest`
+                 if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+                   case " $predeps $postdeps " in
+                   *" $i "*)
+                     func_append newdeplibs " $i"
+                     i=""
+                     ;;
+                   esac
+                 fi
+                 if test -n "$i" ; then
+                   libname=`eval "\\$ECHO \"$libname_spec\""`
+                   deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+                   set dummy $deplib_matches; shift
+                   deplib_match=$1
+                   if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+                     func_append newdeplibs " $i"
+                   else
+                     droppeddeps=yes
+                     echo
+                     $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+                     echo "*** I have the capability to make that library automatically link in when"
+                     echo "*** you link to this library.  But I can only do this if you have a"
+                     echo "*** shared version of the library, which you do not appear to have"
+                     echo "*** because a test_compile did reveal that the linker did not use this one"
+                     echo "*** as a dynamic dependency that programs can get resolved with at runtime."
+                   fi
+                 fi
+               else
+                 droppeddeps=yes
+                 echo
+                 $ECHO "*** Warning!  Library $i is needed by this library but I was not able to"
+                 echo "*** make it link in!  You will probably need to install it or some"
+                 echo "*** library that it depends on before this library will be fully"
+                 echo "*** functional.  Installing it before continuing would be even better."
+               fi
+               ;;
+             *)
+               func_append newdeplibs " $i"
+               ;;
+             esac
+           done
+         fi
+         ;;
+       file_magic*)
+         set dummy $deplibs_check_method; shift
+         file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+         for a_deplib in $deplibs; do
+           case $a_deplib in
+           -l*)
+             func_stripname -l '' "$a_deplib"
+             name=$func_stripname_result
+             if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+               case " $predeps $postdeps " in
+               *" $a_deplib "*)
+                 func_append newdeplibs " $a_deplib"
+                 a_deplib=""
+                 ;;
+               esac
+             fi
+             if test -n "$a_deplib" ; then
+               libname=`eval "\\$ECHO \"$libname_spec\""`
+               if test -n "$file_magic_glob"; then
+                 libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob`
+               else
+                 libnameglob=$libname
+               fi
+               test "$want_nocaseglob" = yes && nocaseglob=`shopt -p nocaseglob`
+               for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+                 if test "$want_nocaseglob" = yes; then
+                   shopt -s nocaseglob
+                   potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+                   $nocaseglob
+                 else
+                   potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+                 fi
+                 for potent_lib in $potential_libs; do
+                     # Follow soft links.
+                     if ls -lLd "$potent_lib" 2>/dev/null |
+                        $GREP " -> " >/dev/null; then
+                       continue
+                     fi
+                     # The statement above tries to avoid entering an
+                     # endless loop below, in case of cyclic links.
+                     # We might still enter an endless loop, since a link
+                     # loop can be closed while we follow links,
+                     # but so what?
+                     potlib="$potent_lib"
+                     while test -h "$potlib" 2>/dev/null; do
+                       potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'`
+                       case $potliblink in
+                       [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+                       *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";;
+                       esac
+                     done
+                     if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+                        $SED -e 10q |
+                        $EGREP "$file_magic_regex" > /dev/null; then
+                       func_append newdeplibs " $a_deplib"
+                       a_deplib=""
+                       break 2
+                     fi
+                 done
+               done
+             fi
+             if test -n "$a_deplib" ; then
+               droppeddeps=yes
+               echo
+               $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+               echo "*** I have the capability to make that library automatically link in when"
+               echo "*** you link to this library.  But I can only do this if you have a"
+               echo "*** shared version of the library, which you do not appear to have"
+               echo "*** because I did check the linker path looking for a file starting"
+               if test -z "$potlib" ; then
+                 $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+               else
+                 $ECHO "*** with $libname and none of the candidates passed a file format test"
+                 $ECHO "*** using a file magic. Last file checked: $potlib"
+               fi
+             fi
+             ;;
+           *)
+             # Add a -L argument.
+             func_append newdeplibs " $a_deplib"
+             ;;
+           esac
+         done # Gone through all deplibs.
+         ;;
+       match_pattern*)
+         set dummy $deplibs_check_method; shift
+         match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+         for a_deplib in $deplibs; do
+           case $a_deplib in
+           -l*)
+             func_stripname -l '' "$a_deplib"
+             name=$func_stripname_result
+             if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+               case " $predeps $postdeps " in
+               *" $a_deplib "*)
+                 func_append newdeplibs " $a_deplib"
+                 a_deplib=""
+                 ;;
+               esac
+             fi
+             if test -n "$a_deplib" ; then
+               libname=`eval "\\$ECHO \"$libname_spec\""`
+               for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+                 potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+                 for potent_lib in $potential_libs; do
+                   potlib="$potent_lib" # see symlink-check above in file_magic test
+                   if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \
+                      $EGREP "$match_pattern_regex" > /dev/null; then
+                     func_append newdeplibs " $a_deplib"
+                     a_deplib=""
+                     break 2
+                   fi
+                 done
+               done
+             fi
+             if test -n "$a_deplib" ; then
+               droppeddeps=yes
+               echo
+               $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+               echo "*** I have the capability to make that library automatically link in when"
+               echo "*** you link to this library.  But I can only do this if you have a"
+               echo "*** shared version of the library, which you do not appear to have"
+               echo "*** because I did check the linker path looking for a file starting"
+               if test -z "$potlib" ; then
+                 $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+               else
+                 $ECHO "*** with $libname and none of the candidates passed a file format test"
+                 $ECHO "*** using a regex pattern. Last file checked: $potlib"
+               fi
+             fi
+             ;;
+           *)
+             # Add a -L argument.
+             func_append newdeplibs " $a_deplib"
+             ;;
+           esac
+         done # Gone through all deplibs.
+         ;;
+       none | unknown | *)
+         newdeplibs=""
+         tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'`
+         if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+           for i in $predeps $postdeps ; do
+             # can't use Xsed below, because $i might contain '/'
+             tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"`
+           done
+         fi
+         case $tmp_deplibs in
+         *[!\  \ ]*)
+           echo
+           if test "X$deplibs_check_method" = "Xnone"; then
+             echo "*** Warning: inter-library dependencies are not supported in this platform."
+           else
+             echo "*** Warning: inter-library dependencies are not known to be supported."
+           fi
+           echo "*** All declared inter-library dependencies are being dropped."
+           droppeddeps=yes
+           ;;
+         esac
+         ;;
+       esac
+       versuffix=$versuffix_save
+       major=$major_save
+       release=$release_save
+       libname=$libname_save
+       name=$name_save
+
+       case $host in
+       *-*-rhapsody* | *-*-darwin1.[012])
+         # On Rhapsody replace the C library with the System framework
+         newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'`
+         ;;
+       esac
+
+       if test "$droppeddeps" = yes; then
+         if test "$module" = yes; then
+           echo
+           echo "*** Warning: libtool could not satisfy all declared inter-library"
+           $ECHO "*** dependencies of module $libname.  Therefore, libtool will create"
+           echo "*** a static module, that should work as long as the dlopening"
+           echo "*** application is linked with the -dlopen flag."
+           if test -z "$global_symbol_pipe"; then
+             echo
+             echo "*** However, this would only work if libtool was able to extract symbol"
+             echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+             echo "*** not find such a program.  So, this module is probably useless."
+             echo "*** \`nm' from GNU binutils and a full rebuild may help."
+           fi
+           if test "$build_old_libs" = no; then
+             oldlibs="$output_objdir/$libname.$libext"
+             build_libtool_libs=module
+             build_old_libs=yes
+           else
+             build_libtool_libs=no
+           fi
+         else
+           echo "*** The inter-library dependencies that have been dropped here will be"
+           echo "*** automatically added whenever a program is linked with this library"
+           echo "*** or is declared to -dlopen it."
+
+           if test "$allow_undefined" = no; then
+             echo
+             echo "*** Since this library must not contain undefined symbols,"
+             echo "*** because either the platform does not support them or"
+             echo "*** it was explicitly requested with -no-undefined,"
+             echo "*** libtool will only create a static version of it."
+             if test "$build_old_libs" = no; then
+               oldlibs="$output_objdir/$libname.$libext"
+               build_libtool_libs=module
+               build_old_libs=yes
+             else
+               build_libtool_libs=no
+             fi
+           fi
+         fi
+       fi
+       # Done checking deplibs!
+       deplibs=$newdeplibs
+      fi
+      # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+      case $host in
+       *-*-darwin*)
+         newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+         new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+         deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+         ;;
+      esac
+
+      # move library search paths that coincide with paths to not yet
+      # installed libraries to the beginning of the library search list
+      new_libs=
+      for path in $notinst_path; do
+       case " $new_libs " in
+       *" -L$path/$objdir "*) ;;
+       *)
+         case " $deplibs " in
+         *" -L$path/$objdir "*)
+           func_append new_libs " -L$path/$objdir" ;;
+         esac
+         ;;
+       esac
+      done
+      for deplib in $deplibs; do
+       case $deplib in
+       -L*)
+         case " $new_libs " in
+         *" $deplib "*) ;;
+         *) func_append new_libs " $deplib" ;;
+         esac
+         ;;
+       *) func_append new_libs " $deplib" ;;
+       esac
+      done
+      deplibs="$new_libs"
+
+      # All the library-specific variables (install_libdir is set above).
+      library_names=
+      old_library=
+      dlname=
+
+      # Test again, we may have decided not to build it any more
+      if test "$build_libtool_libs" = yes; then
+       # Remove ${wl} instances when linking with ld.
+       # FIXME: should test the right _cmds variable.
+       case $archive_cmds in
+         *\$LD\ *) wl= ;;
+        esac
+       if test "$hardcode_into_libs" = yes; then
+         # Hardcode the library paths
+         hardcode_libdirs=
+         dep_rpath=
+         rpath="$finalize_rpath"
+         test "$opt_mode" != relink && rpath="$compile_rpath$rpath"
+         for libdir in $rpath; do
+           if test -n "$hardcode_libdir_flag_spec"; then
+             if test -n "$hardcode_libdir_separator"; then
+               func_replace_sysroot "$libdir"
+               libdir=$func_replace_sysroot_result
+               if test -z "$hardcode_libdirs"; then
+                 hardcode_libdirs="$libdir"
+               else
+                 # Just accumulate the unique libdirs.
+                 case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+                 *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+                   ;;
+                 *)
+                   func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+                   ;;
+                 esac
+               fi
+             else
+               eval flag=\"$hardcode_libdir_flag_spec\"
+               func_append dep_rpath " $flag"
+             fi
+           elif test -n "$runpath_var"; then
+             case "$perm_rpath " in
+             *" $libdir "*) ;;
+             *) func_append perm_rpath " $libdir" ;;
+             esac
+           fi
+         done
+         # Substitute the hardcoded libdirs into the rpath.
+         if test -n "$hardcode_libdir_separator" &&
+            test -n "$hardcode_libdirs"; then
+           libdir="$hardcode_libdirs"
+           eval "dep_rpath=\"$hardcode_libdir_flag_spec\""
+         fi
+         if test -n "$runpath_var" && test -n "$perm_rpath"; then
+           # We should set the runpath_var.
+           rpath=
+           for dir in $perm_rpath; do
+             func_append rpath "$dir:"
+           done
+           eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+         fi
+         test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+       fi
+
+       shlibpath="$finalize_shlibpath"
+       test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath"
+       if test -n "$shlibpath"; then
+         eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+       fi
+
+       # Get the real and link names of the library.
+       eval shared_ext=\"$shrext_cmds\"
+       eval library_names=\"$library_names_spec\"
+       set dummy $library_names
+       shift
+       realname="$1"
+       shift
+
+       if test -n "$soname_spec"; then
+         eval soname=\"$soname_spec\"
+       else
+         soname="$realname"
+       fi
+       if test -z "$dlname"; then
+         dlname=$soname
+       fi
+
+       lib="$output_objdir/$realname"
+       linknames=
+       for link
+       do
+         func_append linknames " $link"
+       done
+
+       # Use standard objects if they are pic
+       test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+       test "X$libobjs" = "X " && libobjs=
+
+       delfiles=
+       if test -n "$export_symbols" && test -n "$include_expsyms"; then
+         $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+         export_symbols="$output_objdir/$libname.uexp"
+         func_append delfiles " $export_symbols"
+       fi
+
+       orig_export_symbols=
+       case $host_os in
+       cygwin* | mingw* | cegcc*)
+         if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+           # exporting using user supplied symfile
+           if test "x`$SED 1q $export_symbols`" != xEXPORTS; then
+             # and it's NOT already a .def file. Must figure out
+             # which of the given symbols are data symbols and tag
+             # them as such. So, trigger use of export_symbols_cmds.
+             # export_symbols gets reassigned inside the "prepare
+             # the list of exported symbols" if statement, so the
+             # include_expsyms logic still works.
+             orig_export_symbols="$export_symbols"
+             export_symbols=
+             always_export_symbols=yes
+           fi
+         fi
+         ;;
+       esac
+
+       # Prepare the list of exported symbols
+       if test -z "$export_symbols"; then
+         if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+           func_verbose "generating symbol list for \`$libname.la'"
+           export_symbols="$output_objdir/$libname.exp"
+           $opt_dry_run || $RM $export_symbols
+           cmds=$export_symbols_cmds
+           save_ifs="$IFS"; IFS='~'
+           for cmd1 in $cmds; do
+             IFS="$save_ifs"
+             # Take the normal branch if the nm_file_list_spec branch
+             # doesn't work or if tool conversion is not needed.
+             case $nm_file_list_spec~$to_tool_file_cmd in
+               *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*)
+                 try_normal_branch=yes
+                 eval cmd=\"$cmd1\"
+                 func_len " $cmd"
+                 len=$func_len_result
+                 ;;
+               *)
+                 try_normal_branch=no
+                 ;;
+             esac
+             if test "$try_normal_branch" = yes \
+                && { test "$len" -lt "$max_cmd_len" \
+                     || test "$max_cmd_len" -le -1; }
+             then
+               func_show_eval "$cmd" 'exit $?'
+               skipped_export=false
+             elif test -n "$nm_file_list_spec"; then
+               func_basename "$output"
+               output_la=$func_basename_result
+               save_libobjs=$libobjs
+               save_output=$output
+               output=${output_objdir}/${output_la}.nm
+               func_to_tool_file "$output"
+               libobjs=$nm_file_list_spec$func_to_tool_file_result
+               func_append delfiles " $output"
+               func_verbose "creating $NM input file list: $output"
+               for obj in $save_libobjs; do
+                 func_to_tool_file "$obj"
+                 $ECHO "$func_to_tool_file_result"
+               done > "$output"
+               eval cmd=\"$cmd1\"
+               func_show_eval "$cmd" 'exit $?'
+               output=$save_output
+               libobjs=$save_libobjs
+               skipped_export=false
+             else
+               # The command line is too long to execute in one step.
+               func_verbose "using reloadable object file for export list..."
+               skipped_export=:
+               # Break out early, otherwise skipped_export may be
+               # set to false by a later but shorter cmd.
+               break
+             fi
+           done
+           IFS="$save_ifs"
+           if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then
+             func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+             func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+           fi
+         fi
+       fi
+
+       if test -n "$export_symbols" && test -n "$include_expsyms"; then
+         tmp_export_symbols="$export_symbols"
+         test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+         $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+       fi
+
+       if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then
+         # The given exports_symbols file has to be filtered, so filter it.
+         func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+         # FIXME: $output_objdir/$libname.filter potentially contains lots of
+         # 's' commands which not all seds can handle. GNU sed should be fine
+         # though. Also, the filter scales superlinearly with the number of
+         # global variables. join(1) would be nice here, but unfortunately
+         # isn't a blessed tool.
+         $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+         func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+         export_symbols=$output_objdir/$libname.def
+         $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+       fi
+
+       tmp_deplibs=
+       for test_deplib in $deplibs; do
+         case " $convenience " in
+         *" $test_deplib "*) ;;
+         *)
+           func_append tmp_deplibs " $test_deplib"
+           ;;
+         esac
+       done
+       deplibs="$tmp_deplibs"
+
+       if test -n "$convenience"; then
+         if test -n "$whole_archive_flag_spec" &&
+           test "$compiler_needs_object" = yes &&
+           test -z "$libobjs"; then
+           # extract the archives, so we have objects to list.
+           # TODO: could optimize this to just extract one archive.
+           whole_archive_flag_spec=
+         fi
+         if test -n "$whole_archive_flag_spec"; then
+           save_libobjs=$libobjs
+           eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+           test "X$libobjs" = "X " && libobjs=
+         else
+           gentop="$output_objdir/${outputname}x"
+           func_append generated " $gentop"
+
+           func_extract_archives $gentop $convenience
+           func_append libobjs " $func_extract_archives_result"
+           test "X$libobjs" = "X " && libobjs=
+         fi
+       fi
+
+       if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+         eval flag=\"$thread_safe_flag_spec\"
+         func_append linker_flags " $flag"
+       fi
+
+       # Make a backup of the uninstalled library when relinking
+       if test "$opt_mode" = relink; then
+         $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+       fi
+
+       # Do each of the archive commands.
+       if test "$module" = yes && test -n "$module_cmds" ; then
+         if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+           eval test_cmds=\"$module_expsym_cmds\"
+           cmds=$module_expsym_cmds
+         else
+           eval test_cmds=\"$module_cmds\"
+           cmds=$module_cmds
+         fi
+       else
+         if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+           eval test_cmds=\"$archive_expsym_cmds\"
+           cmds=$archive_expsym_cmds
+         else
+           eval test_cmds=\"$archive_cmds\"
+           cmds=$archive_cmds
+         fi
+       fi
+
+       if test "X$skipped_export" != "X:" &&
+          func_len " $test_cmds" &&
+          len=$func_len_result &&
+          test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+         :
+       else
+         # The command line is too long to link in one step, link piecewise
+         # or, if using GNU ld and skipped_export is not :, use a linker
+         # script.
+
+         # Save the value of $output and $libobjs because we want to
+         # use them later.  If we have whole_archive_flag_spec, we
+         # want to use save_libobjs as it was before
+         # whole_archive_flag_spec was expanded, because we can't
+         # assume the linker understands whole_archive_flag_spec.
+         # This may have to be revisited, in case too many
+         # convenience libraries get linked in and end up exceeding
+         # the spec.
+         if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+           save_libobjs=$libobjs
+         fi
+         save_output=$output
+         func_basename "$output"
+         output_la=$func_basename_result
+
+         # Clear the reloadable object creation command queue and
+         # initialize k to one.
+         test_cmds=
+         concat_cmds=
+         objlist=
+         last_robj=
+         k=1
+
+         if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then
+           output=${output_objdir}/${output_la}.lnkscript
+           func_verbose "creating GNU ld script: $output"
+           echo 'INPUT (' > $output
+           for obj in $save_libobjs
+           do
+             func_to_tool_file "$obj"
+             $ECHO "$func_to_tool_file_result" >> $output
+           done
+           echo ')' >> $output
+           func_append delfiles " $output"
+           func_to_tool_file "$output"
+           output=$func_to_tool_file_result
+         elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then
+           output=${output_objdir}/${output_la}.lnk
+           func_verbose "creating linker input file list: $output"
+           : > $output
+           set x $save_libobjs
+           shift
+           firstobj=
+           if test "$compiler_needs_object" = yes; then
+             firstobj="$1 "
+             shift
+           fi
+           for obj
+           do
+             func_to_tool_file "$obj"
+             $ECHO "$func_to_tool_file_result" >> $output
+           done
+           func_append delfiles " $output"
+           func_to_tool_file "$output"
+           output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
+         else
+           if test -n "$save_libobjs"; then
+             func_verbose "creating reloadable object files..."
+             output=$output_objdir/$output_la-${k}.$objext
+             eval test_cmds=\"$reload_cmds\"
+             func_len " $test_cmds"
+             len0=$func_len_result
+             len=$len0
+
+             # Loop over the list of objects to be linked.
+             for obj in $save_libobjs
+             do
+               func_len " $obj"
+               func_arith $len + $func_len_result
+               len=$func_arith_result
+               if test "X$objlist" = X ||
+                  test "$len" -lt "$max_cmd_len"; then
+                 func_append objlist " $obj"
+               else
+                 # The command $test_cmds is almost too long, add a
+                 # command to the queue.
+                 if test "$k" -eq 1 ; then
+                   # The first file doesn't have a previous command to add.
+                   reload_objs=$objlist
+                   eval concat_cmds=\"$reload_cmds\"
+                 else
+                   # All subsequent reloadable object files will link in
+                   # the last one created.
+                   reload_objs="$objlist $last_robj"
+                   eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\"
+                 fi
+                 last_robj=$output_objdir/$output_la-${k}.$objext
+                 func_arith $k + 1
+                 k=$func_arith_result
+                 output=$output_objdir/$output_la-${k}.$objext
+                 objlist=" $obj"
+                 func_len " $last_robj"
+                 func_arith $len0 + $func_len_result
+                 len=$func_arith_result
+               fi
+             done
+             # Handle the remaining objects by creating one last
+             # reloadable object file.  All subsequent reloadable object
+             # files will link in the last one created.
+             test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+             reload_objs="$objlist $last_robj"
+             eval concat_cmds=\"\${concat_cmds}$reload_cmds\"
+             if test -n "$last_robj"; then
+               eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\"
+             fi
+             func_append delfiles " $output"
+
+           else
+             output=
+           fi
+
+           if ${skipped_export-false}; then
+             func_verbose "generating symbol list for \`$libname.la'"
+             export_symbols="$output_objdir/$libname.exp"
+             $opt_dry_run || $RM $export_symbols
+             libobjs=$output
+             # Append the command to create the export file.
+             test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+             eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+             if test -n "$last_robj"; then
+               eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+             fi
+           fi
+
+           test -n "$save_libobjs" &&
+             func_verbose "creating a temporary reloadable object file: $output"
+
+           # Loop through the commands generated above and execute them.
+           save_ifs="$IFS"; IFS='~'
+           for cmd in $concat_cmds; do
+             IFS="$save_ifs"
+             $opt_silent || {
+                 func_quote_for_expand "$cmd"
+                 eval "func_echo $func_quote_for_expand_result"
+             }
+             $opt_dry_run || eval "$cmd" || {
+               lt_exit=$?
+
+               # Restore the uninstalled library and exit
+               if test "$opt_mode" = relink; then
+                 ( cd "$output_objdir" && \
+                   $RM "${realname}T" && \
+                   $MV "${realname}U" "$realname" )
+               fi
+
+               exit $lt_exit
+             }
+           done
+           IFS="$save_ifs"
+
+           if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+             func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+             func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+           fi
+         fi
+
+          if ${skipped_export-false}; then
+           if test -n "$export_symbols" && test -n "$include_expsyms"; then
+             tmp_export_symbols="$export_symbols"
+             test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+             $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+           fi
+
+           if test -n "$orig_export_symbols"; then
+             # The given exports_symbols file has to be filtered, so filter it.
+             func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+             # FIXME: $output_objdir/$libname.filter potentially contains lots of
+             # 's' commands which not all seds can handle. GNU sed should be fine
+             # though. Also, the filter scales superlinearly with the number of
+             # global variables. join(1) would be nice here, but unfortunately
+             # isn't a blessed tool.
+             $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+             func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+             export_symbols=$output_objdir/$libname.def
+             $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+           fi
+         fi
+
+         libobjs=$output
+         # Restore the value of output.
+         output=$save_output
+
+         if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+           eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+           test "X$libobjs" = "X " && libobjs=
+         fi
+         # Expand the library linking commands again to reset the
+         # value of $libobjs for piecewise linking.
+
+         # Do each of the archive commands.
+         if test "$module" = yes && test -n "$module_cmds" ; then
+           if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+             cmds=$module_expsym_cmds
+           else
+             cmds=$module_cmds
+           fi
+         else
+           if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+             cmds=$archive_expsym_cmds
+           else
+             cmds=$archive_cmds
+           fi
+         fi
+       fi
+
+       if test -n "$delfiles"; then
+         # Append the command to remove temporary files to $cmds.
+         eval cmds=\"\$cmds~\$RM $delfiles\"
+       fi
+
+       # Add any objects from preloaded convenience libraries
+       if test -n "$dlprefiles"; then
+         gentop="$output_objdir/${outputname}x"
+         func_append generated " $gentop"
+
+         func_extract_archives $gentop $dlprefiles
+         func_append libobjs " $func_extract_archives_result"
+         test "X$libobjs" = "X " && libobjs=
+       fi
+
+       save_ifs="$IFS"; IFS='~'
+       for cmd in $cmds; do
+         IFS="$save_ifs"
+         eval cmd=\"$cmd\"
+         $opt_silent || {
+           func_quote_for_expand "$cmd"
+           eval "func_echo $func_quote_for_expand_result"
+         }
+         $opt_dry_run || eval "$cmd" || {
+           lt_exit=$?
+
+           # Restore the uninstalled library and exit
+           if test "$opt_mode" = relink; then
+             ( cd "$output_objdir" && \
+               $RM "${realname}T" && \
+               $MV "${realname}U" "$realname" )
+           fi
+
+           exit $lt_exit
+         }
+       done
+       IFS="$save_ifs"
+
+       # Restore the uninstalled library and exit
+       if test "$opt_mode" = relink; then
+         $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+         if test -n "$convenience"; then
+           if test -z "$whole_archive_flag_spec"; then
+             func_show_eval '${RM}r "$gentop"'
+           fi
+         fi
+
+         exit $EXIT_SUCCESS
+       fi
+
+       # Create links to the real library.
+       for linkname in $linknames; do
+         if test "$realname" != "$linkname"; then
+           func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+         fi
+       done
+
+       # If -module or -export-dynamic was specified, set the dlname.
+       if test "$module" = yes || test "$export_dynamic" = yes; then
+         # On all known operating systems, these are identical.
+         dlname="$soname"
+       fi
+      fi
+      ;;
+
+    obj)
+      if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+       func_warning "\`-dlopen' is ignored for objects"
+      fi
+
+      case " $deplibs" in
+      *\ -l* | *\ -L*)
+       func_warning "\`-l' and \`-L' are ignored for objects" ;;
+      esac
+
+      test -n "$rpath" && \
+       func_warning "\`-rpath' is ignored for objects"
+
+      test -n "$xrpath" && \
+       func_warning "\`-R' is ignored for objects"
+
+      test -n "$vinfo" && \
+       func_warning "\`-version-info' is ignored for objects"
+
+      test -n "$release" && \
+       func_warning "\`-release' is ignored for objects"
+
+      case $output in
+      *.lo)
+       test -n "$objs$old_deplibs" && \
+         func_fatal_error "cannot build library object \`$output' from non-libtool objects"
+
+       libobj=$output
+       func_lo2o "$libobj"
+       obj=$func_lo2o_result
+       ;;
+      *)
+       libobj=
+       obj="$output"
+       ;;
+      esac
+
+      # Delete the old objects.
+      $opt_dry_run || $RM $obj $libobj
+
+      # Objects from convenience libraries.  This assumes
+      # single-version convenience libraries.  Whenever we create
+      # different ones for PIC/non-PIC, this we'll have to duplicate
+      # the extraction.
+      reload_conv_objs=
+      gentop=
+      # reload_cmds runs $LD directly, so let us get rid of
+      # -Wl from whole_archive_flag_spec and hope we can get by with
+      # turning comma into space..
+      wl=
+
+      if test -n "$convenience"; then
+       if test -n "$whole_archive_flag_spec"; then
+         eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+         reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'`
+       else
+         gentop="$output_objdir/${obj}x"
+         func_append generated " $gentop"
+
+         func_extract_archives $gentop $convenience
+         reload_conv_objs="$reload_objs $func_extract_archives_result"
+       fi
+      fi
+
+      # If we're not building shared, we need to use non_pic_objs
+      test "$build_libtool_libs" != yes && libobjs="$non_pic_objects"
+
+      # Create the old-style object.
+      reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test
+
+      output="$obj"
+      func_execute_cmds "$reload_cmds" 'exit $?'
+
+      # Exit if we aren't doing a library object file.
+      if test -z "$libobj"; then
+       if test -n "$gentop"; then
+         func_show_eval '${RM}r "$gentop"'
+       fi
+
+       exit $EXIT_SUCCESS
+      fi
+
+      if test "$build_libtool_libs" != yes; then
+       if test -n "$gentop"; then
+         func_show_eval '${RM}r "$gentop"'
+       fi
+
+       # Create an invalid libtool object if no PIC, so that we don't
+       # accidentally link it into a program.
+       # $show "echo timestamp > $libobj"
+       # $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+       exit $EXIT_SUCCESS
+      fi
+
+      if test -n "$pic_flag" || test "$pic_mode" != default; then
+       # Only do commands if we really have different PIC objects.
+       reload_objs="$libobjs $reload_conv_objs"
+       output="$libobj"
+       func_execute_cmds "$reload_cmds" 'exit $?'
+      fi
+
+      if test -n "$gentop"; then
+       func_show_eval '${RM}r "$gentop"'
+      fi
+
+      exit $EXIT_SUCCESS
+      ;;
+
+    prog)
+      case $host in
+       *cygwin*) func_stripname '' '.exe' "$output"
+                 output=$func_stripname_result.exe;;
+      esac
+      test -n "$vinfo" && \
+       func_warning "\`-version-info' is ignored for programs"
+
+      test -n "$release" && \
+       func_warning "\`-release' is ignored for programs"
+
+      test "$preload" = yes \
+        && test "$dlopen_support" = unknown \
+       && test "$dlopen_self" = unknown \
+       && test "$dlopen_self_static" = unknown && \
+         func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+      case $host in
+      *-*-rhapsody* | *-*-darwin1.[012])
+       # On Rhapsody replace the C library is the System framework
+       compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'`
+       finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'`
+       ;;
+      esac
+
+      case $host in
+      *-*-darwin*)
+       # Don't allow lazy linking, it breaks C++ global constructors
+       # But is supposedly fixed on 10.4 or later (yay!).
+       if test "$tagname" = CXX ; then
+         case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+           10.[0123])
+             func_append compile_command " ${wl}-bind_at_load"
+             func_append finalize_command " ${wl}-bind_at_load"
+           ;;
+         esac
+       fi
+       # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+       compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+       finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+       ;;
+      esac
+
+
+      # move library search paths that coincide with paths to not yet
+      # installed libraries to the beginning of the library search list
+      new_libs=
+      for path in $notinst_path; do
+       case " $new_libs " in
+       *" -L$path/$objdir "*) ;;
+       *)
+         case " $compile_deplibs " in
+         *" -L$path/$objdir "*)
+           func_append new_libs " -L$path/$objdir" ;;
+         esac
+         ;;
+       esac
+      done
+      for deplib in $compile_deplibs; do
+       case $deplib in
+       -L*)
+         case " $new_libs " in
+         *" $deplib "*) ;;
+         *) func_append new_libs " $deplib" ;;
+         esac
+         ;;
+       *) func_append new_libs " $deplib" ;;
+       esac
+      done
+      compile_deplibs="$new_libs"
+
+
+      func_append compile_command " $compile_deplibs"
+      func_append finalize_command " $finalize_deplibs"
+
+      if test -n "$rpath$xrpath"; then
+       # If the user specified any rpath flags, then add them.
+       for libdir in $rpath $xrpath; do
+         # This is the magic to use -rpath.
+         case "$finalize_rpath " in
+         *" $libdir "*) ;;
+         *) func_append finalize_rpath " $libdir" ;;
+         esac
+       done
+      fi
+
+      # Now hardcode the library paths
+      rpath=
+      hardcode_libdirs=
+      for libdir in $compile_rpath $finalize_rpath; do
+       if test -n "$hardcode_libdir_flag_spec"; then
+         if test -n "$hardcode_libdir_separator"; then
+           if test -z "$hardcode_libdirs"; then
+             hardcode_libdirs="$libdir"
+           else
+             # Just accumulate the unique libdirs.
+             case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+             *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+               ;;
+             *)
+               func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+               ;;
+             esac
+           fi
+         else
+           eval flag=\"$hardcode_libdir_flag_spec\"
+           func_append rpath " $flag"
+         fi
+       elif test -n "$runpath_var"; then
+         case "$perm_rpath " in
+         *" $libdir "*) ;;
+         *) func_append perm_rpath " $libdir" ;;
+         esac
+       fi
+       case $host in
+       *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+         testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'`
+         case :$dllsearchpath: in
+         *":$libdir:"*) ;;
+         ::) dllsearchpath=$libdir;;
+         *) func_append dllsearchpath ":$libdir";;
+         esac
+         case :$dllsearchpath: in
+         *":$testbindir:"*) ;;
+         ::) dllsearchpath=$testbindir;;
+         *) func_append dllsearchpath ":$testbindir";;
+         esac
+         ;;
+       esac
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+        test -n "$hardcode_libdirs"; then
+       libdir="$hardcode_libdirs"
+       eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      compile_rpath="$rpath"
+
+      rpath=
+      hardcode_libdirs=
+      for libdir in $finalize_rpath; do
+       if test -n "$hardcode_libdir_flag_spec"; then
+         if test -n "$hardcode_libdir_separator"; then
+           if test -z "$hardcode_libdirs"; then
+             hardcode_libdirs="$libdir"
+           else
+             # Just accumulate the unique libdirs.
+             case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+             *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+               ;;
+             *)
+               func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+               ;;
+             esac
+           fi
+         else
+           eval flag=\"$hardcode_libdir_flag_spec\"
+           func_append rpath " $flag"
+         fi
+       elif test -n "$runpath_var"; then
+         case "$finalize_perm_rpath " in
+         *" $libdir "*) ;;
+         *) func_append finalize_perm_rpath " $libdir" ;;
+         esac
+       fi
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+        test -n "$hardcode_libdirs"; then
+       libdir="$hardcode_libdirs"
+       eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      finalize_rpath="$rpath"
+
+      if test -n "$libobjs" && test "$build_old_libs" = yes; then
+       # Transform all the library objects into standard objects.
+       compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+       finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+      fi
+
+      func_generate_dlsyms "$outputname" "@PROGRAM@" "no"
+
+      # template prelinking step
+      if test -n "$prelink_cmds"; then
+       func_execute_cmds "$prelink_cmds" 'exit $?'
+      fi
+
+      wrappers_required=yes
+      case $host in
+      *cegcc* | *mingw32ce*)
+        # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
+        wrappers_required=no
+        ;;
+      *cygwin* | *mingw* )
+        if test "$build_libtool_libs" != yes; then
+          wrappers_required=no
+        fi
+        ;;
+      *)
+        if test "$need_relink" = no || test "$build_libtool_libs" != yes; then
+          wrappers_required=no
+        fi
+        ;;
+      esac
+      if test "$wrappers_required" = no; then
+       # Replace the output file specification.
+       compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+       link_command="$compile_command$compile_rpath"
+
+       # We have no uninstalled library dependencies, so finalize right now.
+       exit_status=0
+       func_show_eval "$link_command" 'exit_status=$?'
+
+       if test -n "$postlink_cmds"; then
+         func_to_tool_file "$output"
+         postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+         func_execute_cmds "$postlink_cmds" 'exit $?'
+       fi
+
+       # Delete the generated files.
+       if test -f "$output_objdir/${outputname}S.${objext}"; then
+         func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"'
+       fi
+
+       exit $exit_status
+      fi
+
+      if test -n "$compile_shlibpath$finalize_shlibpath"; then
+       compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+      fi
+      if test -n "$finalize_shlibpath"; then
+       finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+      fi
+
+      compile_var=
+      finalize_var=
+      if test -n "$runpath_var"; then
+       if test -n "$perm_rpath"; then
+         # We should set the runpath_var.
+         rpath=
+         for dir in $perm_rpath; do
+           func_append rpath "$dir:"
+         done
+         compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+       fi
+       if test -n "$finalize_perm_rpath"; then
+         # We should set the runpath_var.
+         rpath=
+         for dir in $finalize_perm_rpath; do
+           func_append rpath "$dir:"
+         done
+         finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+       fi
+      fi
+
+      if test "$no_install" = yes; then
+       # We don't need to create a wrapper script.
+       link_command="$compile_var$compile_command$compile_rpath"
+       # Replace the output file specification.
+       link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+       # Delete the old output file.
+       $opt_dry_run || $RM $output
+       # Link the executable and exit
+       func_show_eval "$link_command" 'exit $?'
+
+       if test -n "$postlink_cmds"; then
+         func_to_tool_file "$output"
+         postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+         func_execute_cmds "$postlink_cmds" 'exit $?'
+       fi
+
+       exit $EXIT_SUCCESS
+      fi
+
+      if test "$hardcode_action" = relink; then
+       # Fast installation is not supported
+       link_command="$compile_var$compile_command$compile_rpath"
+       relink_command="$finalize_var$finalize_command$finalize_rpath"
+
+       func_warning "this platform does not like uninstalled shared libraries"
+       func_warning "\`$output' will be relinked during installation"
+      else
+       if test "$fast_install" != no; then
+         link_command="$finalize_var$compile_command$finalize_rpath"
+         if test "$fast_install" = yes; then
+           relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'`
+         else
+           # fast_install is set to needless
+           relink_command=
+         fi
+       else
+         link_command="$compile_var$compile_command$compile_rpath"
+         relink_command="$finalize_var$finalize_command$finalize_rpath"
+       fi
+      fi
+
+      # Replace the output file specification.
+      link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+      # Delete the old output files.
+      $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+      func_show_eval "$link_command" 'exit $?'
+
+      if test -n "$postlink_cmds"; then
+       func_to_tool_file "$output_objdir/$outputname"
+       postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+       func_execute_cmds "$postlink_cmds" 'exit $?'
+      fi
+
+      # Now create the wrapper script.
+      func_verbose "creating $output"
+
+      # Quote the relink command for shipping.
+      if test -n "$relink_command"; then
+       # Preserve any variables that may affect compiler behavior
+       for var in $variables_saved_for_relink; do
+         if eval test -z \"\${$var+set}\"; then
+           relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+         elif eval var_value=\$$var; test -z "$var_value"; then
+           relink_command="$var=; export $var; $relink_command"
+         else
+           func_quote_for_eval "$var_value"
+           relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+         fi
+       done
+       relink_command="(cd `pwd`; $relink_command)"
+       relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+      fi
+
+      # Only actually do things if not in dry run mode.
+      $opt_dry_run || {
+       # win32 will think the script is a binary if it has
+       # a .exe suffix, so we strip it off here.
+       case $output in
+         *.exe) func_stripname '' '.exe' "$output"
+                output=$func_stripname_result ;;
+       esac
+       # test for cygwin because mv fails w/o .exe extensions
+       case $host in
+         *cygwin*)
+           exeext=.exe
+           func_stripname '' '.exe' "$outputname"
+           outputname=$func_stripname_result ;;
+         *) exeext= ;;
+       esac
+       case $host in
+         *cygwin* | *mingw* )
+           func_dirname_and_basename "$output" "" "."
+           output_name=$func_basename_result
+           output_path=$func_dirname_result
+           cwrappersource="$output_path/$objdir/lt-$output_name.c"
+           cwrapper="$output_path/$output_name.exe"
+           $RM $cwrappersource $cwrapper
+           trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+           func_emit_cwrapperexe_src > $cwrappersource
+
+           # The wrapper executable is built using the $host compiler,
+           # because it contains $host paths and files. If cross-
+           # compiling, it, like the target executable, must be
+           # executed on the $host or under an emulation environment.
+           $opt_dry_run || {
+             $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+             $STRIP $cwrapper
+           }
+
+           # Now, create the wrapper script for func_source use:
+           func_ltwrapper_scriptname $cwrapper
+           $RM $func_ltwrapper_scriptname_result
+           trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+           $opt_dry_run || {
+             # note: this script will not be executed, so do not chmod.
+             if test "x$build" = "x$host" ; then
+               $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+             else
+               func_emit_wrapper no > $func_ltwrapper_scriptname_result
+             fi
+           }
+         ;;
+         * )
+           $RM $output
+           trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+           func_emit_wrapper no > $output
+           chmod +x $output
+         ;;
+       esac
+      }
+      exit $EXIT_SUCCESS
+      ;;
+    esac
+
+    # See if we need to build an old-fashioned archive.
+    for oldlib in $oldlibs; do
+
+      if test "$build_libtool_libs" = convenience; then
+       oldobjs="$libobjs_save $symfileobj"
+       addlibs="$convenience"
+       build_libtool_libs=no
+      else
+       if test "$build_libtool_libs" = module; then
+         oldobjs="$libobjs_save"
+         build_libtool_libs=no
+       else
+         oldobjs="$old_deplibs $non_pic_objects"
+         if test "$preload" = yes && test -f "$symfileobj"; then
+           func_append oldobjs " $symfileobj"
+         fi
+       fi
+       addlibs="$old_convenience"
+      fi
+
+      if test -n "$addlibs"; then
+       gentop="$output_objdir/${outputname}x"
+       func_append generated " $gentop"
+
+       func_extract_archives $gentop $addlibs
+       func_append oldobjs " $func_extract_archives_result"
+      fi
+
+      # Do each command in the archive commands.
+      if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+       cmds=$old_archive_from_new_cmds
+      else
+
+       # Add any objects from preloaded convenience libraries
+       if test -n "$dlprefiles"; then
+         gentop="$output_objdir/${outputname}x"
+         func_append generated " $gentop"
+
+         func_extract_archives $gentop $dlprefiles
+         func_append oldobjs " $func_extract_archives_result"
+       fi
+
+       # POSIX demands no paths to be encoded in archives.  We have
+       # to avoid creating archives with duplicate basenames if we
+       # might have to extract them afterwards, e.g., when creating a
+       # static archive out of a convenience library, or when linking
+       # the entirety of a libtool archive into another (currently
+       # not supported by libtool).
+       if (for obj in $oldobjs
+           do
+             func_basename "$obj"
+             $ECHO "$func_basename_result"
+           done | sort | sort -uc >/dev/null 2>&1); then
+         :
+       else
+         echo "copying selected object files to avoid basename conflicts..."
+         gentop="$output_objdir/${outputname}x"
+         func_append generated " $gentop"
+         func_mkdir_p "$gentop"
+         save_oldobjs=$oldobjs
+         oldobjs=
+         counter=1
+         for obj in $save_oldobjs
+         do
+           func_basename "$obj"
+           objbase="$func_basename_result"
+           case " $oldobjs " in
+           " ") oldobjs=$obj ;;
+           *[\ /]"$objbase "*)
+             while :; do
+               # Make sure we don't pick an alternate name that also
+               # overlaps.
+               newobj=lt$counter-$objbase
+               func_arith $counter + 1
+               counter=$func_arith_result
+               case " $oldobjs " in
+               *[\ /]"$newobj "*) ;;
+               *) if test ! -f "$gentop/$newobj"; then break; fi ;;
+               esac
+             done
+             func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+             func_append oldobjs " $gentop/$newobj"
+             ;;
+           *) func_append oldobjs " $obj" ;;
+           esac
+         done
+       fi
+       func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+       tool_oldlib=$func_to_tool_file_result
+       eval cmds=\"$old_archive_cmds\"
+
+       func_len " $cmds"
+       len=$func_len_result
+       if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+         cmds=$old_archive_cmds
+       elif test -n "$archiver_list_spec"; then
+         func_verbose "using command file archive linking..."
+         for obj in $oldobjs
+         do
+           func_to_tool_file "$obj"
+           $ECHO "$func_to_tool_file_result"
+         done > $output_objdir/$libname.libcmd
+         func_to_tool_file "$output_objdir/$libname.libcmd"
+         oldobjs=" $archiver_list_spec$func_to_tool_file_result"
+         cmds=$old_archive_cmds
+       else
+         # the command line is too long to link in one step, link in parts
+         func_verbose "using piecewise archive linking..."
+         save_RANLIB=$RANLIB
+         RANLIB=:
+         objlist=
+         concat_cmds=
+         save_oldobjs=$oldobjs
+         oldobjs=
+         # Is there a better way of finding the last object in the list?
+         for obj in $save_oldobjs
+         do
+           last_oldobj=$obj
+         done
+         eval test_cmds=\"$old_archive_cmds\"
+         func_len " $test_cmds"
+         len0=$func_len_result
+         len=$len0
+         for obj in $save_oldobjs
+         do
+           func_len " $obj"
+           func_arith $len + $func_len_result
+           len=$func_arith_result
+           func_append objlist " $obj"
+           if test "$len" -lt "$max_cmd_len"; then
+             :
+           else
+             # the above command should be used before it gets too long
+             oldobjs=$objlist
+             if test "$obj" = "$last_oldobj" ; then
+               RANLIB=$save_RANLIB
+             fi
+             test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+             eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\"
+             objlist=
+             len=$len0
+           fi
+         done
+         RANLIB=$save_RANLIB
+         oldobjs=$objlist
+         if test "X$oldobjs" = "X" ; then
+           eval cmds=\"\$concat_cmds\"
+         else
+           eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+         fi
+       fi
+      fi
+      func_execute_cmds "$cmds" 'exit $?'
+    done
+
+    test -n "$generated" && \
+      func_show_eval "${RM}r$generated"
+
+    # Now create the libtool archive.
+    case $output in
+    *.la)
+      old_library=
+      test "$build_old_libs" = yes && old_library="$libname.$libext"
+      func_verbose "creating $output"
+
+      # Preserve any variables that may affect compiler behavior
+      for var in $variables_saved_for_relink; do
+       if eval test -z \"\${$var+set}\"; then
+         relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+       elif eval var_value=\$$var; test -z "$var_value"; then
+         relink_command="$var=; export $var; $relink_command"
+       else
+         func_quote_for_eval "$var_value"
+         relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+       fi
+      done
+      # Quote the link command for shipping.
+      relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+      relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+      if test "$hardcode_automatic" = yes ; then
+       relink_command=
+      fi
+
+      # Only create the output if not a dry run.
+      $opt_dry_run || {
+       for installed in no yes; do
+         if test "$installed" = yes; then
+           if test -z "$install_libdir"; then
+             break
+           fi
+           output="$output_objdir/$outputname"i
+           # Replace all uninstalled libtool libraries with the installed ones
+           newdependency_libs=
+           for deplib in $dependency_libs; do
+             case $deplib in
+             *.la)
+               func_basename "$deplib"
+               name="$func_basename_result"
+               func_resolve_sysroot "$deplib"
+               eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result`
+               test -z "$libdir" && \
+                 func_fatal_error "\`$deplib' is not a valid libtool archive"
+               func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name"
+               ;;
+             -L*)
+               func_stripname -L '' "$deplib"
+               func_replace_sysroot "$func_stripname_result"
+               func_append newdependency_libs " -L$func_replace_sysroot_result"
+               ;;
+             -R*)
+               func_stripname -R '' "$deplib"
+               func_replace_sysroot "$func_stripname_result"
+               func_append newdependency_libs " -R$func_replace_sysroot_result"
+               ;;
+             *) func_append newdependency_libs " $deplib" ;;
+             esac
+           done
+           dependency_libs="$newdependency_libs"
+           newdlfiles=
+
+           for lib in $dlfiles; do
+             case $lib in
+             *.la)
+               func_basename "$lib"
+               name="$func_basename_result"
+               eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+               test -z "$libdir" && \
+                 func_fatal_error "\`$lib' is not a valid libtool archive"
+               func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name"
+               ;;
+             *) func_append newdlfiles " $lib" ;;
+             esac
+           done
+           dlfiles="$newdlfiles"
+           newdlprefiles=
+           for lib in $dlprefiles; do
+             case $lib in
+             *.la)
+               # Only pass preopened files to the pseudo-archive (for
+               # eventual linking with the app. that links it) if we
+               # didn't already link the preopened objects directly into
+               # the library:
+               func_basename "$lib"
+               name="$func_basename_result"
+               eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+               test -z "$libdir" && \
+                 func_fatal_error "\`$lib' is not a valid libtool archive"
+               func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name"
+               ;;
+             esac
+           done
+           dlprefiles="$newdlprefiles"
+         else
+           newdlfiles=
+           for lib in $dlfiles; do
+             case $lib in
+               [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+               *) abs=`pwd`"/$lib" ;;
+             esac
+             func_append newdlfiles " $abs"
+           done
+           dlfiles="$newdlfiles"
+           newdlprefiles=
+           for lib in $dlprefiles; do
+             case $lib in
+               [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+               *) abs=`pwd`"/$lib" ;;
+             esac
+             func_append newdlprefiles " $abs"
+           done
+           dlprefiles="$newdlprefiles"
+         fi
+         $RM $output
+         # place dlname in correct position for cygwin
+         # In fact, it would be nice if we could use this code for all target
+         # systems that can't hard-code library paths into their executables
+         # and that have no shared library path variable independent of PATH,
+         # but it turns out we can't easily determine that from inspecting
+         # libtool variables, so we have to hard-code the OSs to which it
+         # applies here; at the moment, that means platforms that use the PE
+         # object format with DLL files.  See the long comment at the top of
+         # tests/bindir.at for full details.
+         tdlname=$dlname
+         case $host,$output,$installed,$module,$dlname in
+           *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
+             # If a -bindir argument was supplied, place the dll there.
+             if test "x$bindir" != x ;
+             then
+               func_relative_path "$install_libdir" "$bindir"
+               tdlname=$func_relative_path_result$dlname
+             else
+               # Otherwise fall back on heuristic.
+               tdlname=../bin/$dlname
+             fi
+             ;;
+         esac
+         $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that can not go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+         if test "$installed" = no && test "$need_relink" = yes; then
+           $ECHO >> $output "\
+relink_command=\"$relink_command\""
+         fi
+       done
+      }
+
+      # Do a symbolic link so that the libtool archive can be found in
+      # LD_LIBRARY_PATH before the program is installed.
+      func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+      ;;
+    esac
+    exit $EXIT_SUCCESS
+}
+
+{ test "$opt_mode" = link || test "$opt_mode" = relink; } &&
+    func_mode_link ${1+"$@"}
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+    $opt_debug
+    RM="$nonopt"
+    files=
+    rmforce=
+    exit_status=0
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic="$magic"
+
+    for arg
+    do
+      case $arg in
+      -f) func_append RM " $arg"; rmforce=yes ;;
+      -*) func_append RM " $arg" ;;
+      *) func_append files " $arg" ;;
+      esac
+    done
+
+    test -z "$RM" && \
+      func_fatal_help "you must specify an RM program"
+
+    rmdirs=
+
+    for file in $files; do
+      func_dirname "$file" "" "."
+      dir="$func_dirname_result"
+      if test "X$dir" = X.; then
+       odir="$objdir"
+      else
+       odir="$dir/$objdir"
+      fi
+      func_basename "$file"
+      name="$func_basename_result"
+      test "$opt_mode" = uninstall && odir="$dir"
+
+      # Remember odir for removal later, being careful to avoid duplicates
+      if test "$opt_mode" = clean; then
+       case " $rmdirs " in
+         *" $odir "*) ;;
+         *) func_append rmdirs " $odir" ;;
+       esac
+      fi
+
+      # Don't error if the file doesn't exist and rm -f was used.
+      if { test -L "$file"; } >/dev/null 2>&1 ||
+        { test -h "$file"; } >/dev/null 2>&1 ||
+        test -f "$file"; then
+       :
+      elif test -d "$file"; then
+       exit_status=1
+       continue
+      elif test "$rmforce" = yes; then
+       continue
+      fi
+
+      rmfiles="$file"
+
+      case $name in
+      *.la)
+       # Possibly a libtool archive, so verify it.
+       if func_lalib_p "$file"; then
+         func_source $dir/$name
+
+         # Delete the libtool libraries and symlinks.
+         for n in $library_names; do
+           func_append rmfiles " $odir/$n"
+         done
+         test -n "$old_library" && func_append rmfiles " $odir/$old_library"
+
+         case "$opt_mode" in
+         clean)
+           case " $library_names " in
+           *" $dlname "*) ;;
+           *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;;
+           esac
+           test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i"
+           ;;
+         uninstall)
+           if test -n "$library_names"; then
+             # Do each command in the postuninstall commands.
+             func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+           fi
+
+           if test -n "$old_library"; then
+             # Do each command in the old_postuninstall commands.
+             func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+           fi
+           # FIXME: should reinstall the best remaining shared library.
+           ;;
+         esac
+       fi
+       ;;
+
+      *.lo)
+       # Possibly a libtool object, so verify it.
+       if func_lalib_p "$file"; then
+
+         # Read the .lo file
+         func_source $dir/$name
+
+         # Add PIC object to the list of files to remove.
+         if test -n "$pic_object" &&
+            test "$pic_object" != none; then
+           func_append rmfiles " $dir/$pic_object"
+         fi
+
+         # Add non-PIC object to the list of files to remove.
+         if test -n "$non_pic_object" &&
+            test "$non_pic_object" != none; then
+           func_append rmfiles " $dir/$non_pic_object"
+         fi
+       fi
+       ;;
+
+      *)
+       if test "$opt_mode" = clean ; then
+         noexename=$name
+         case $file in
+         *.exe)
+           func_stripname '' '.exe' "$file"
+           file=$func_stripname_result
+           func_stripname '' '.exe' "$name"
+           noexename=$func_stripname_result
+           # $file with .exe has already been added to rmfiles,
+           # add $file without .exe
+           func_append rmfiles " $file"
+           ;;
+         esac
+         # Do a test to see if this is a libtool program.
+         if func_ltwrapper_p "$file"; then
+           if func_ltwrapper_executable_p "$file"; then
+             func_ltwrapper_scriptname "$file"
+             relink_command=
+             func_source $func_ltwrapper_scriptname_result
+             func_append rmfiles " $func_ltwrapper_scriptname_result"
+           else
+             relink_command=
+             func_source $dir/$noexename
+           fi
+
+           # note $name still contains .exe if it was in $file originally
+           # as does the version of $file that was added into $rmfiles
+           func_append rmfiles " $odir/$name $odir/${name}S.${objext}"
+           if test "$fast_install" = yes && test -n "$relink_command"; then
+             func_append rmfiles " $odir/lt-$name"
+           fi
+           if test "X$noexename" != "X$name" ; then
+             func_append rmfiles " $odir/lt-${noexename}.c"
+           fi
+         fi
+       fi
+       ;;
+      esac
+      func_show_eval "$RM $rmfiles" 'exit_status=1'
+    done
+
+    # Try to remove the ${objdir}s in the directories where we deleted files
+    for dir in $rmdirs; do
+      if test -d "$dir"; then
+       func_show_eval "rmdir $dir >/dev/null 2>&1"
+      fi
+    done
+
+    exit $exit_status
+}
+
+{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } &&
+    func_mode_uninstall ${1+"$@"}
+
+test -z "$opt_mode" && {
+  help="$generic_help"
+  func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+  func_fatal_help "invalid operation mode \`$opt_mode'"
+
+if test -n "$exec_cmd"; then
+  eval exec "$exec_cmd"
+  exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# in which we disable both kinds of libraries.  Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them.  This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration.  But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
+# vi:sw=2
+
diff --git a/mgmt/main.c b/mgmt/main.c
new file mode 100644 (file)
index 0000000..b2d6c3c
--- /dev/null
@@ -0,0 +1,1933 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <getopt.h>
+#include <stdbool.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/mgmt.h>
+
+#include <glib.h>
+#include "glib-helper.h"
+
+static bool monitor = false;
+static bool discovery = false;
+static bool resolve_names = true;
+
+typedef void (*cmd_cb)(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data);
+
+static struct pending_cmd {
+       uint16_t op;
+       uint16_t id;
+       cmd_cb cb;
+       void *user_data;
+       struct pending_cmd *next;
+} *pending = NULL;
+
+static int mgmt_send_cmd(int mgmt_sk, uint16_t op, uint16_t id, void *data,
+                               size_t len, cmd_cb func, void *user_data)
+{
+       char buf[1024];
+       struct pending_cmd *cmd;
+       struct mgmt_hdr *hdr = (void *) buf;
+
+       if (len + MGMT_HDR_SIZE > sizeof(buf))
+               return -EINVAL;
+
+       cmd = calloc(1, sizeof(struct pending_cmd));
+       if (cmd == NULL)
+               return -errno;
+
+       cmd->op = op;
+       cmd->id = id;
+       cmd->cb = func;
+       cmd->user_data = user_data;
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(op);
+       hdr->index = htobs(id);
+       hdr->len = htobs(len);
+       memcpy(buf + MGMT_HDR_SIZE, data, len);
+
+       if (write(mgmt_sk, buf, MGMT_HDR_SIZE + len) < 0) {
+               fprintf(stderr, "Unable to write to socket: %s\n",
+                                                       strerror(errno));
+               free(cmd);
+               return -1;
+       }
+
+       cmd->next = pending;
+       pending = cmd;
+
+       return 0;
+}
+
+static int mgmt_open(void)
+{
+       struct sockaddr_hci addr;
+       int sk;
+
+       sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       if (sk < 0) {
+               fprintf(stderr, "socket: %s\n", strerror(errno));
+               return sk;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.hci_family = AF_BLUETOOTH;
+       addr.hci_dev = HCI_DEV_NONE;
+       addr.hci_channel = HCI_CHANNEL_CONTROL;
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               fprintf(stderr, "bind: %s\n", strerror(errno));
+               close(sk);
+               return -1;
+       }
+
+       return sk;
+}
+
+static void mgmt_check_pending(int mgmt_sk, uint16_t op, uint16_t index,
+                               uint16_t status, void *data, uint16_t len)
+{
+       struct pending_cmd *c, *prev;
+
+       for (c = pending, prev = NULL; c != NULL; prev = c, c = c->next) {
+               if (c->op != op)
+                       continue;
+               if (c->id != index)
+                       continue;
+
+               if (c == pending)
+                       pending = c->next;
+               else
+                       prev->next = c->next;
+
+               c->cb(mgmt_sk, op, index, status, data, len, c->user_data);
+
+               free(c);
+               break;
+       }
+}
+
+static int mgmt_cmd_complete(int mgmt_sk, uint16_t index,
+                               struct mgmt_ev_cmd_complete *ev, uint16_t len)
+{
+       uint16_t op;
+
+       if (len < sizeof(*ev)) {
+               fprintf(stderr, "Too short (%u bytes) cmd complete event\n",
+                                                                       len);
+               return -EINVAL;
+       }
+
+       op = bt_get_le16(&ev->opcode);
+
+       len -= sizeof(*ev);
+
+       if (monitor)
+               printf("%s complete, opcode 0x%04x len %u\n", mgmt_opstr(op),
+                                                               op, len);
+
+       mgmt_check_pending(mgmt_sk, op, index, ev->status, ev->data, len);
+
+       return 0;
+}
+
+static int mgmt_cmd_status(int mgmt_sk, uint16_t index,
+                               struct mgmt_ev_cmd_status *ev, uint16_t len)
+{
+       uint16_t opcode;
+
+       if (len < sizeof(*ev)) {
+               fprintf(stderr, "Too short (%u bytes) cmd status event\n",
+                                                                       len);
+               return -EINVAL;
+       }
+
+       opcode = bt_get_le16(&ev->opcode);
+
+       if (monitor)
+               printf("cmd status, opcode 0x%04x status 0x%02x (%s)\n",
+                               opcode, ev->status, mgmt_errstr(ev->status));
+
+       if (ev->status != 0)
+               mgmt_check_pending(mgmt_sk, opcode, index, ev->status,
+                                                               NULL, 0);
+
+       return 0;
+}
+
+static int mgmt_controller_error(uint16_t index,
+                                       struct mgmt_ev_controller_error *ev,
+                                       uint16_t len)
+{
+       if (len < sizeof(*ev)) {
+               fprintf(stderr,
+                       "Too short (%u bytes) controller error event\n", len);
+               return -EINVAL;
+       }
+
+       if (monitor)
+               printf("hci%u error 0x%02x\n", index, ev->error_code);
+
+       return 0;
+}
+
+static int mgmt_index_added(int mgmt_sk, uint16_t index)
+{
+       if (monitor)
+               printf("hci%u added\n", index);
+       return 0;
+}
+
+static int mgmt_index_removed(int mgmt_sk, uint16_t index)
+{
+       if (monitor)
+               printf("hci%u removed\n", index);
+       return 0;
+}
+
+static const char *settings_str[] = {
+                               "powered",
+                               "connectable",
+                               "fast-connectable",
+                               "discoverable",
+                               "pairable",
+                               "link-security",
+                               "ssp",
+                               "br/edr",
+                               "hs",
+                               "le" ,
+};
+
+static void print_settings(uint32_t settings)
+{
+       unsigned i;
+
+       for (i = 0; i < NELEM(settings_str); i++) {
+               if ((settings & (1 << i)) != 0)
+                       printf("%s ", settings_str[i]);
+       }
+}
+
+static int mgmt_new_settings(int mgmt_sk, uint16_t index,
+                                       uint32_t *ev, uint16_t len)
+{
+       if (len < sizeof(*ev)) {
+               fprintf(stderr, "Too short new_settings event (%u)\n", len);
+               return -EINVAL;
+       }
+
+       if (monitor) {
+               printf("hci%u new_settings: ", index);
+               print_settings(bt_get_le32(ev));
+               printf("\n");
+       }
+
+       return 0;
+}
+
+static int mgmt_discovering(int mgmt_sk, uint16_t index,
+                               struct mgmt_ev_discovering *ev, uint16_t len)
+{
+       if (len < sizeof(*ev)) {
+               fprintf(stderr, "Too short (%u bytes) discovering event\n",
+                                                                       len);
+               return -EINVAL;
+       }
+
+       if (ev->discovering == 0 && discovery)
+               exit(EXIT_SUCCESS);
+
+       if (monitor)
+               printf("hci%u type %u discovering %s\n", index,
+                               ev->type, ev->discovering ? "on" : "off");
+
+       return 0;
+}
+
+static int mgmt_new_link_key(int mgmt_sk, uint16_t index,
+                               struct mgmt_ev_new_link_key *ev, uint16_t len)
+{
+
+       if (len != sizeof(*ev)) {
+               fprintf(stderr, "Invalid new_link_key length (%u bytes)\n",
+                                                                       len);
+               return -EINVAL;
+       }
+
+       if (monitor) {
+               char addr[18];
+               ba2str(&ev->key.addr.bdaddr, addr);
+               printf("hci%u new_link_key %s type 0x%02x pin_len %d "
+                               "store_hint %u\n", index, addr, ev->key.type,
+                               ev->key.pin_len, ev->store_hint);
+       }
+
+       return 0;
+}
+
+static const char *typestr(uint8_t type)
+{
+       const char *str[] = { "BR/EDR", "LE Public", "LE Random" };
+
+       if (type <= BDADDR_LE_RANDOM)
+               return str[type];
+
+       return "(unknown)";
+}
+
+static int mgmt_connected(int mgmt_sk, uint16_t index,
+                                       struct mgmt_ev_device_connected *ev,
+                                       uint16_t len)
+{
+       uint16_t eir_len;
+
+       if (len < sizeof(*ev)) {
+               fprintf(stderr,
+                       "Invalid connected event length (%u bytes)\n", len);
+               return -EINVAL;
+       }
+
+       eir_len = bt_get_le16(&ev->eir_len);
+       if (len != sizeof(*ev) + eir_len) {
+               fprintf(stderr, "Invalid connected event length "
+                       "(%u bytes, eir_len %u bytes)\n", len, eir_len);
+               return -EINVAL;
+       }
+
+       if (monitor) {
+               char addr[18];
+               ba2str(&ev->addr.bdaddr, addr);
+               printf("hci%u %s type %s connected eir_len %u\n", index, addr,
+                                       typestr(ev->addr.type), eir_len);
+       }
+
+       return 0;
+}
+
+static int mgmt_disconnected(int mgmt_sk, uint16_t index,
+                               struct mgmt_addr_info *ev, uint16_t len)
+{
+       if (len != sizeof(*ev)) {
+               fprintf(stderr,
+                       "Invalid disconnected event length (%u bytes)\n", len);
+               return -EINVAL;
+       }
+
+       if (monitor) {
+               char addr[18];
+               ba2str(&ev->bdaddr, addr);
+               printf("hci%u %s type %s disconnected\n", index, addr,
+                                                       typestr(ev->type));
+       }
+
+       return 0;
+}
+
+static int mgmt_conn_failed(int mgmt_sk, uint16_t index,
+                               struct mgmt_ev_connect_failed *ev,
+                               uint16_t len)
+{
+       if (len != sizeof(*ev)) {
+               fprintf(stderr,
+                       "Invalid connect_failed event length (%u bytes)\n", len);
+               return -EINVAL;
+       }
+
+       if (monitor) {
+               char addr[18];
+               ba2str(&ev->addr.bdaddr, addr);
+               printf("hci%u %s type %s connect failed (status 0x%02x, %s)\n",
+                               index, addr, typestr(ev->addr.type), ev->status,
+                               mgmt_errstr(ev->status));
+       }
+
+       return 0;
+}
+
+static int mgmt_auth_failed(int mgmt_sk, uint16_t index,
+                               struct mgmt_ev_auth_failed *ev,
+                               uint16_t len)
+{
+       if (len != sizeof(*ev)) {
+               fprintf(stderr,
+                       "Invalid auth_failed event length (%u bytes)\n", len);
+               return -EINVAL;
+       }
+
+       if (monitor) {
+               char addr[18];
+               ba2str(&ev->addr.bdaddr, addr);
+               printf("hci%u %s auth failed with status 0x%02x (%s)\n",
+                       index, addr, ev->status, mgmt_errstr(ev->status));
+       }
+
+       return 0;
+}
+
+static int mgmt_name_changed(int mgmt_sk, uint16_t index,
+                               struct mgmt_ev_local_name_changed *ev,
+                               uint16_t len)
+{
+       if (len != sizeof(*ev)) {
+               fprintf(stderr,
+                       "Invalid local_name_changed length (%u bytes)\n", len);
+               return -EINVAL;
+       }
+
+       if (monitor)
+               printf("hci%u name changed: %s\n", index, ev->name);
+
+       return 0;
+}
+
+static void confirm_name_rsp(int mgmt_sk, uint16_t op, uint16_t id,
+                               uint8_t status, void *rsp, uint16_t len,
+                               void *user_data)
+{
+       struct mgmt_rp_confirm_name *rp = rsp;
+       char addr[18];
+
+       if (len == 0 && status != 0) {
+               fprintf(stderr,
+                       "hci%u confirm_name failed with status 0x%02x (%s)\n",
+                                       id, status, mgmt_errstr(status));
+               return;
+       }
+
+       if (len != sizeof(*rp)) {
+               fprintf(stderr,
+                       "hci%u confirm_name rsp length %u instead of %zu\n",
+                       id, len, sizeof(*rp));
+               return;
+       }
+
+       ba2str(&rp->addr.bdaddr, addr);
+
+       if (status != 0)
+               fprintf(stderr,
+                       "hci%u confirm_name for %s failed: 0x%02x (%s)\n",
+                       id, addr, status, mgmt_errstr(status));
+       else
+               printf("hci%u confirm_name succeeded for %s\n", id, addr);
+}
+
+static int mgmt_device_found(int mgmt_sk, uint16_t index,
+                               struct mgmt_ev_device_found *ev, uint16_t len)
+{
+       uint32_t flags;
+       uint16_t eir_len;
+
+       if (len < sizeof(*ev)) {
+               fprintf(stderr,
+                       "Too short device_found length (%u bytes)\n", len);
+               return -EINVAL;
+       }
+
+       flags = btohs(ev->flags);
+
+       eir_len = bt_get_le16(&ev->eir_len);
+       if (len != sizeof(*ev) + eir_len) {
+               fprintf(stderr, "dev_found: expected %zu bytes, got %u bytes",
+                                               sizeof(*ev) + eir_len, len);
+               return -EINVAL;
+       }
+
+       if (monitor || discovery) {
+               char addr[18];
+               ba2str(&ev->addr.bdaddr, addr);
+               printf("hci%u dev_found: %s type %s rssi %d "
+                       "flags 0x%04x eir_len %u\n", index, addr,
+                       typestr(ev->addr.type), ev->rssi, flags, eir_len);
+       }
+
+       if (discovery && (flags & MGMT_DEV_FOUND_CONFIRM_NAME)) {
+               struct mgmt_cp_confirm_name cp;
+
+               memset(&cp, 0, sizeof(cp));
+               memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+               if (resolve_names)
+                       cp.name_known = 0;
+               else
+                       cp.name_known = 1;
+
+               mgmt_send_cmd(mgmt_sk, MGMT_OP_CONFIRM_NAME, index,
+                                       &cp, sizeof(cp), confirm_name_rsp,
+                                       NULL);
+       }
+
+       return 0;
+}
+
+static void pin_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       if (status != 0) {
+               fprintf(stderr,
+                       "hci%u PIN Code reply failed with status 0x%02x (%s)",
+                                       id, status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       printf("hci%u PIN Reply successful\n", id);
+}
+
+static int mgmt_pin_reply(int mgmt_sk, uint16_t index,
+                                               struct mgmt_addr_info *addr,
+                                               const char *pin, size_t len)
+{
+       struct mgmt_cp_pin_code_reply cp;
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(&cp.addr, addr, sizeof(cp.addr));
+       cp.pin_len = len;
+       memcpy(cp.pin_code, pin, len);
+
+       return mgmt_send_cmd(mgmt_sk, MGMT_OP_PIN_CODE_REPLY, index,
+                                       &cp, sizeof(cp), pin_rsp, NULL);
+}
+
+static void pin_neg_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       if (status != 0) {
+               fprintf(stderr,
+                       "hci%u PIN Neg reply failed with status 0x%02x (%s)",
+                                       id, status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       printf("hci%u PIN Negative Reply successful\n", id);
+}
+
+static int mgmt_pin_neg_reply(int mgmt_sk, uint16_t index,
+                                               struct mgmt_addr_info *addr)
+{
+       struct mgmt_cp_pin_code_neg_reply cp;
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(&cp.addr, addr, sizeof(cp.addr));
+
+       return mgmt_send_cmd(mgmt_sk, MGMT_OP_PIN_CODE_NEG_REPLY, index,
+                                       &cp, sizeof(cp), pin_neg_rsp, NULL);
+}
+
+static int mgmt_request_pin(int mgmt_sk, uint16_t index,
+                               struct mgmt_ev_pin_code_request *ev,
+                               uint16_t len)
+{
+       char pin[18];
+       size_t pin_len;
+
+       if (len != sizeof(*ev)) {
+               fprintf(stderr,
+                       "Invalid pin_code request length (%u bytes)\n", len);
+               return -EINVAL;
+       }
+
+       if (monitor) {
+               char addr[18];
+               ba2str(&ev->addr.bdaddr, addr);
+               printf("hci%u %s request PIN\n", index, addr);
+       }
+
+       printf("PIN Request (press enter to reject) >> ");
+       fflush(stdout);
+
+       memset(pin, 0, sizeof(pin));
+
+       if (fgets(pin, sizeof(pin), stdin) == NULL || pin[0] == '\n')
+               return mgmt_pin_neg_reply(mgmt_sk, index, &ev->addr);
+
+       pin_len = strlen(pin);
+       if (pin[pin_len - 1] == '\n') {
+               pin[pin_len - 1] = '\0';
+               pin_len--;
+       }
+
+       return mgmt_pin_reply(mgmt_sk, index, &ev->addr, pin, pin_len);
+}
+
+static void confirm_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       if (status != 0) {
+               fprintf(stderr,
+                       "hci%u User Confirm reply failed. status 0x%02x (%s)",
+                                       id, status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       printf("hci%u User Confirm Reply successful\n", id);
+}
+
+static int mgmt_confirm_reply(int mgmt_sk, uint16_t index, bdaddr_t *bdaddr)
+{
+       struct mgmt_cp_user_confirm_reply cp;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.addr.bdaddr, bdaddr);
+
+       return mgmt_send_cmd(mgmt_sk, MGMT_OP_USER_CONFIRM_REPLY, index,
+                                       &cp, sizeof(cp), confirm_rsp, NULL);
+}
+
+static void confirm_neg_rsp(int mgmt_sk, uint16_t op, uint16_t id,
+                               uint8_t status, void *rsp, uint16_t len,
+                               void *user_data)
+{
+       if (status != 0) {
+               fprintf(stderr,
+                       "hci%u Confirm Neg reply failed. status 0x%02x (%s)",
+                                       id, status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       printf("hci%u User Confirm Negative Reply successful\n", id);
+}
+
+static int mgmt_confirm_neg_reply(int mgmt_sk, uint16_t index,
+                                                       bdaddr_t *bdaddr)
+{
+       struct mgmt_cp_user_confirm_reply cp;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.addr.bdaddr, bdaddr);
+
+       return mgmt_send_cmd(mgmt_sk, MGMT_OP_USER_CONFIRM_NEG_REPLY, index,
+                               &cp, sizeof(cp), confirm_neg_rsp, NULL);
+}
+
+
+static int mgmt_user_confirm(int mgmt_sk, uint16_t index,
+                               struct mgmt_ev_user_confirm_request *ev,
+                               uint16_t len)
+{
+       char rsp[5];
+       size_t rsp_len;
+       uint32_t val;
+       char addr[18];
+
+       if (len != sizeof(*ev)) {
+               fprintf(stderr,
+                       "Invalid user_confirm request length (%u)\n", len);
+               return -EINVAL;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+       val = bt_get_le32(&ev->value);
+
+       if (monitor)
+               printf("hci%u %s User Confirm %06u hint %u\n", index, addr,
+                                                       val, ev->confirm_hint);
+
+       if (ev->confirm_hint)
+               printf("Accept pairing with %s (yes/no) >> ", addr);
+       else
+               printf("Confirm value %06u for %s (yes/no) >> ", val, addr);
+
+       fflush(stdout);
+
+       memset(rsp, 0, sizeof(rsp));
+
+       if (fgets(rsp, sizeof(rsp), stdin) == NULL || rsp[0] == '\n')
+               return mgmt_confirm_neg_reply(mgmt_sk, index, &ev->addr.bdaddr);
+
+       rsp_len = strlen(rsp);
+       if (rsp[rsp_len - 1] == '\n') {
+               rsp[rsp_len - 1] = '\0';
+               rsp_len--;
+       }
+
+       if (rsp[0] == 'y' || rsp[0] == 'Y')
+               return mgmt_confirm_reply(mgmt_sk, index, &ev->addr.bdaddr);
+       else
+               return mgmt_confirm_neg_reply(mgmt_sk, index, &ev->addr.bdaddr);
+}
+
+static int mgmt_handle_event(int mgmt_sk, uint16_t ev, uint16_t index,
+                                               void *data, uint16_t len)
+{
+       if (monitor)
+               printf("event: %s\n", mgmt_evstr(ev));
+
+       switch (ev) {
+       case MGMT_EV_CMD_COMPLETE:
+               return mgmt_cmd_complete(mgmt_sk, index, data, len);
+       case MGMT_EV_CMD_STATUS:
+               return mgmt_cmd_status(mgmt_sk, index, data, len);
+       case MGMT_EV_CONTROLLER_ERROR:
+               return mgmt_controller_error(index, data, len);
+       case MGMT_EV_INDEX_ADDED:
+               return mgmt_index_added(mgmt_sk, index);
+       case MGMT_EV_INDEX_REMOVED:
+               return mgmt_index_removed(mgmt_sk, index);
+       case MGMT_EV_NEW_SETTINGS:
+               return mgmt_new_settings(mgmt_sk, index, data, len);
+       case MGMT_EV_DISCOVERING:
+               return mgmt_discovering(mgmt_sk, index, data, len);
+       case MGMT_EV_NEW_LINK_KEY:
+               return mgmt_new_link_key(mgmt_sk, index, data, len);
+       case MGMT_EV_DEVICE_CONNECTED:
+               return mgmt_connected(mgmt_sk, index, data, len);
+       case MGMT_EV_DEVICE_DISCONNECTED:
+               return mgmt_disconnected(mgmt_sk, index, data, len);
+       case MGMT_EV_CONNECT_FAILED:
+               return mgmt_conn_failed(mgmt_sk, index, data, len);
+       case MGMT_EV_AUTH_FAILED:
+               return mgmt_auth_failed(mgmt_sk, index, data, len);
+       case MGMT_EV_LOCAL_NAME_CHANGED:
+               return mgmt_name_changed(mgmt_sk, index, data, len);
+       case MGMT_EV_DEVICE_FOUND:
+               return mgmt_device_found(mgmt_sk, index, data, len);
+       case MGMT_EV_PIN_CODE_REQUEST:
+               return mgmt_request_pin(mgmt_sk, index, data, len);
+       case MGMT_EV_USER_CONFIRM_REQUEST:
+               return mgmt_user_confirm(mgmt_sk, index, data, len);
+       default:
+               if (monitor)
+                       printf("Unhandled event 0x%04x (%s)\n", ev, mgmt_evstr(ev));
+               return 0;
+       }
+}
+
+static int mgmt_process_data(int mgmt_sk)
+{
+       char buf[1024];
+       struct mgmt_hdr *hdr = (void *) buf;
+       uint16_t len, ev, index;
+       ssize_t ret;
+
+       ret = read(mgmt_sk, buf, sizeof(buf));
+       if (ret < 0) {
+               fprintf(stderr, "read: %s\n", strerror(errno));
+               return ret;
+       }
+
+       if (ret < MGMT_HDR_SIZE) {
+               fprintf(stderr, "Too small mgmt packet (%zd bytes)\n", ret);
+               return 0;
+       }
+
+       ev = bt_get_le16(&hdr->opcode);
+       index = bt_get_le16(&hdr->index);
+       len = bt_get_le16(&hdr->len);
+
+       if (monitor)
+               printf("event 0x%04x len 0x%04x index 0x%04x\n", ev, len, index);
+
+       if (ret != MGMT_HDR_SIZE + len) {
+               fprintf(stderr, "Packet length mismatch. ret %zd len %u",
+                                                               ret, len);
+               return 0;
+       }
+
+       mgmt_handle_event(mgmt_sk, ev, index, buf + MGMT_HDR_SIZE, len);
+
+       return 0;
+}
+
+static void cmd_monitor(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       printf("Monitoring mgmt events...\n");
+       monitor = true;
+}
+
+static void version_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       struct mgmt_rp_read_version *rp = rsp;
+
+       if (status != 0) {
+               fprintf(stderr, "Reading mgmt version failed with status"
+                       " 0x%02x (%s)\n", status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       if (len < sizeof(*rp)) {
+               fprintf(stderr, "Too small version reply (%u bytes)\n", len);
+               exit(EXIT_FAILURE);
+       }
+
+       printf("MGMT Version %u, revision %u\n", rp->version,
+                                               bt_get_le16(&rp->revision));
+
+       exit(EXIT_SUCCESS);
+}
+
+static void cmd_version(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE,
+                                       NULL, 0, version_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send read_version cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void commands_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       struct mgmt_rp_read_commands *rp = rsp;
+       uint16_t num_commands, num_events, *opcode;
+       size_t expected_len;
+       int i;
+
+       if (status != 0) {
+               fprintf(stderr, "Reading supported commands failed with status"
+                       " 0x%02x (%s)\n", status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       if (len < sizeof(*rp)) {
+               fprintf(stderr, "Too small commands reply (%u bytes)\n", len);
+               exit(EXIT_FAILURE);
+       }
+
+       num_commands = bt_get_le16(&rp->num_commands);
+       num_events = bt_get_le16(&rp->num_events);
+
+       expected_len = sizeof(*rp) + num_commands * sizeof(uint16_t) +
+                                               num_events * sizeof(uint16_t);
+
+       if (len < expected_len) {
+               fprintf(stderr, "Too small commands reply (%u != %zu)\n",
+                                                       len, expected_len);
+               exit(EXIT_FAILURE);
+       }
+
+       opcode = rp->opcodes;
+
+       printf("%u commands:\n", num_commands);
+       for (i = 0; i < num_commands; i++) {
+               uint16_t op = bt_get_le16(opcode++);
+               printf("\t%s (0x%04x)\n", mgmt_opstr(op), op);
+       }
+
+       printf("%u events:\n", num_events);
+       for (i = 0; i < num_events; i++) {
+               uint16_t ev = bt_get_le16(opcode++);
+               printf("\t%s (0x%04x)\n", mgmt_evstr(ev), ev);
+       }
+
+       exit(EXIT_SUCCESS);
+}
+
+static void cmd_commands(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_COMMANDS, MGMT_INDEX_NONE,
+                                       NULL, 0, commands_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send read_commands cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void info_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       struct mgmt_rp_read_info *rp = rsp;
+       char addr[18];
+
+       if (status != 0) {
+               fprintf(stderr,
+                       "Reading hci%u info failed with status 0x%02x (%s)\n",
+                                       id, status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       if (len < sizeof(*rp)) {
+               fprintf(stderr, "Too small info reply (%u bytes)\n", len);
+               exit(EXIT_FAILURE);
+       }
+
+       ba2str(&rp->bdaddr, addr);
+       printf("hci%u:\taddr %s version %u manufacturer %u"
+                       " class 0x%02x%02x%02x\n",
+                       id, addr, rp->version, bt_get_le16(&rp->manufacturer),
+                       rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+
+       printf("\tsupported settings: ");
+       print_settings(bt_get_le32(&rp->supported_settings));
+
+       printf("\n\tcurrent settings: ");
+       print_settings(bt_get_le32(&rp->current_settings));
+
+       printf("\n\tname %s\n", rp->name);
+       printf("\tshort name %s\n", rp->short_name);
+
+       if (pending == NULL)
+               exit(EXIT_SUCCESS);
+}
+
+static void index_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       struct mgmt_rp_read_index_list *rp = rsp;
+       uint16_t count;
+       unsigned int i;
+
+       if (status != 0) {
+               fprintf(stderr,
+                       "Reading index list failed with status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       if (len < sizeof(*rp)) {
+               fprintf(stderr, "Too small index list reply (%u bytes)\n",
+                                                                       len);
+               exit(EXIT_FAILURE);
+       }
+
+       count = bt_get_le16(&rp->num_controllers);
+
+       if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
+               fprintf(stderr,
+                       "Index count (%u) doesn't match reply length (%u)\n",
+                                                               count, len);
+               exit(EXIT_FAILURE);
+       }
+
+       if (monitor)
+               printf("Index list with %u item%s\n",
+                                               count, count > 1 ? "s" : "");
+
+       if (count == 0)
+               exit(EXIT_SUCCESS);
+
+       if (monitor && count > 0)
+               printf("\t");
+
+       for (i = 0; i < count; i++) {
+               uint16_t index;
+
+               index = bt_get_le16(&rp->index[i]);
+
+               if (monitor)
+                       printf("hci%u ", index);
+
+               if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_INFO, index, NULL,
+                                       0, info_rsp, NULL) < 0) {
+                       fprintf(stderr, "Unable to send read_info cmd\n");
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       if (monitor && count > 0)
+               printf("\n");
+}
+
+static void cmd_info(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       if (index == MGMT_INDEX_NONE) {
+               if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_INDEX_LIST,
+                                       MGMT_INDEX_NONE, NULL, 0,
+                                       index_rsp, NULL) < 0) {
+                       fprintf(stderr, "Unable to send index_list cmd\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               return;
+       }
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_INFO, index, NULL,
+                                               0, info_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send read_info cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void setting_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       uint32_t *rp = rsp;
+
+       if (status != 0) {
+               fprintf(stderr,
+                       "%s for hci%u failed with status 0x%02x (%s)\n",
+                       mgmt_opstr(op), id, status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       if (len < sizeof(*rp)) {
+               fprintf(stderr, "Too small %s response (%u bytes)\n",
+                                                       mgmt_opstr(op), len);
+               exit(EXIT_FAILURE);
+       }
+
+       printf("hci%u %s complete, settings: ", id, mgmt_opstr(op));
+       print_settings(bt_get_le32(rp));
+       printf("\n");
+
+       exit(EXIT_SUCCESS);
+}
+
+static void cmd_setting(int mgmt_sk, uint16_t index, uint16_t op,
+                                                       int argc, char **argv)
+{
+       uint8_t val;
+
+       if (argc < 2) {
+               printf("Specify \"on\" or \"off\"\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
+               val = 1;
+       else if (strcasecmp(argv[1], "off") == 0)
+               val = 0;
+       else
+               val = atoi(argv[1]);
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       if (mgmt_send_cmd(mgmt_sk, op, index, &val, sizeof(val),
+                                               setting_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send %s cmd\n", mgmt_opstr(op));
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void cmd_power(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       cmd_setting(mgmt_sk, index, MGMT_OP_SET_POWERED, argc, argv);
+}
+
+static void cmd_discov(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_set_discoverable cp;
+
+       if (argc < 2) {
+               printf("Usage: btmgmt %s <yes/no> [timeout]\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       memset(&cp, 0, sizeof(cp));
+
+       if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
+               cp.val = 1;
+       else if (strcasecmp(argv[1], "off") == 0)
+               cp.val = 0;
+       else
+               cp.val = atoi(argv[1]);
+
+       if (argc > 2)
+               cp.timeout = htobs(atoi(argv[2]));
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_DISCOVERABLE, index,
+                               &cp, sizeof(cp), setting_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send set_discoverable cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void cmd_connectable(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       cmd_setting(mgmt_sk, index, MGMT_OP_SET_CONNECTABLE, argc, argv);
+}
+
+static void cmd_pairable(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       cmd_setting(mgmt_sk, index, MGMT_OP_SET_PAIRABLE, argc, argv);
+}
+
+static void cmd_linksec(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       cmd_setting(mgmt_sk, index, MGMT_OP_SET_LINK_SECURITY, argc, argv);
+}
+
+static void cmd_ssp(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       cmd_setting(mgmt_sk, index, MGMT_OP_SET_SSP, argc, argv);
+}
+
+static void cmd_hs(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       cmd_setting(mgmt_sk, index, MGMT_OP_SET_HS, argc, argv);
+}
+
+static void cmd_le(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       cmd_setting(mgmt_sk, index, MGMT_OP_SET_LE, argc, argv);
+}
+
+static void class_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       struct mgmt_ev_class_of_dev_changed *rp = rsp;
+
+       if (len == 0 && status != 0) {
+               fprintf(stderr, "%s failed, status 0x%02x (%s)\n",
+                               mgmt_opstr(op), status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       if (len != sizeof(*rp)) {
+               fprintf(stderr, "Unexpected %s len %u\n", mgmt_opstr(op), len);
+               exit(EXIT_FAILURE);
+       }
+
+       printf("%s succeeded. Class 0x%02x%02x%02x\n", mgmt_opstr(op),
+               rp->class_of_dev[2], rp->class_of_dev[1], rp->class_of_dev[0]);
+
+       exit(EXIT_SUCCESS);
+}
+
+static void cmd_class(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       uint8_t class[2];
+
+       if (argc < 3) {
+               printf("Usage: btmgmt %s <major> <minor>\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       class[0] = atoi(argv[1]);
+       class[1] = atoi(argv[2]);
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_DEV_CLASS, index,
+                               class, sizeof(class), class_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send set_dev_class cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void disconnect_rsp(int mgmt_sk, uint16_t op, uint16_t id,
+                               uint8_t status, void *rsp, uint16_t len,
+                               void *user_data)
+{
+       struct mgmt_rp_disconnect *rp = rsp;
+       char addr[18];
+
+       if (len == 0 && status != 0) {
+               fprintf(stderr, "Disconnect failed with status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       if (len != sizeof(*rp)) {
+               fprintf(stderr, "Invalid disconnect response length (%u)\n",
+                                                                       len);
+               exit(EXIT_FAILURE);
+       }
+
+       ba2str(&rp->addr.bdaddr, addr);
+
+       if (status == 0) {
+               printf("%s disconnected\n", addr);
+               exit(EXIT_SUCCESS);
+       } else {
+               fprintf(stderr,
+                       "Disconnecting %s failed with status 0x%02x (%s)\n",
+                               addr, status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void cmd_disconnect(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_disconnect cp;
+
+       if (argc < 2) {
+               printf("Usage: btmgmt %s <address>\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       str2ba(argv[1], &cp.addr.bdaddr);
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_DISCONNECT, index,
+                               &cp, sizeof(cp), disconnect_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send disconnect cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void con_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       struct mgmt_rp_get_connections *rp = rsp;
+       uint16_t count, i;
+
+       if (len < sizeof(*rp)) {
+               fprintf(stderr, "Too small (%u bytes) get_connections rsp\n",
+                                                                       len);
+               exit(EXIT_FAILURE);
+       }
+
+       count = bt_get_le16(&rp->conn_count);
+       if (len != sizeof(*rp) + count * sizeof(struct mgmt_addr_info)) {
+               fprintf(stderr, "Invalid get_connections length "
+                                       " (count=%u, len=%u)\n", count, len);
+               exit(EXIT_FAILURE);
+       }
+
+       for (i = 0; i < count; i++) {
+               char addr[18];
+
+               ba2str(&rp->addr[i].bdaddr, addr);
+
+               printf("%s type %s\n", addr, typestr(rp->addr[i].type));
+       }
+
+       exit(EXIT_SUCCESS);
+}
+
+static void cmd_con(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_GET_CONNECTIONS, index, NULL, 0,
+                                                       con_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send get_connections cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void find_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       if (status != 0) {
+               fprintf(stderr,
+                       "Unable to start discovery. status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       printf("Discovery started\n");
+       discovery = true;
+}
+
+static void find_usage(void)
+{
+       printf("Usage: btmgmt find [-l|-b]>\n");
+}
+
+static struct option find_options[] = {
+       { "help",       0, 0, 'h' },
+       { "le-only",    1, 0, 'l' },
+       { "bredr-only", 1, 0, 'b' },
+       { 0, 0, 0, 0 }
+};
+
+static void cmd_find(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_start_discovery cp;
+       uint8_t type;
+       int opt;
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       type = 0;
+       hci_set_bit(BDADDR_BREDR, &type);
+       hci_set_bit(BDADDR_LE_PUBLIC, &type);
+       hci_set_bit(BDADDR_LE_RANDOM, &type);
+
+       while ((opt = getopt_long(argc, argv, "+lbh", find_options,
+                                                               NULL)) != -1) {
+               switch (opt) {
+               case 'l':
+                       hci_clear_bit(BDADDR_BREDR, &type);
+                       hci_set_bit(BDADDR_LE_PUBLIC, &type);
+                       hci_set_bit(BDADDR_LE_RANDOM, &type);
+                       break;
+               case 'b':
+                       hci_set_bit(BDADDR_BREDR, &type);
+                       hci_clear_bit(BDADDR_LE_PUBLIC, &type);
+                       hci_clear_bit(BDADDR_LE_RANDOM, &type);
+                       break;
+               case 'h':
+               default:
+                       find_usage();
+                       exit(EXIT_SUCCESS);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.type = type;
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_START_DISCOVERY, index,
+                               &cp, sizeof(cp), find_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send start_discovery cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void name_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       if (status != 0) {
+               fprintf(stderr, "Unable to set local name. status 0x%02x (%s)",
+                                               status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       exit(EXIT_SUCCESS);
+}
+
+static void cmd_name(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_set_local_name cp;
+
+       if (argc < 2) {
+               printf("Usage: btmgmt %s <name> [shortname]\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       memset(&cp, 0, sizeof(cp));
+       strncpy((char *) cp.name, argv[1], HCI_MAX_NAME_LENGTH);
+       if (argc > 2)
+               strncpy((char *) cp.short_name, argv[2],
+                                       MGMT_MAX_SHORT_NAME_LENGTH);
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_LOCAL_NAME, index,
+                                       &cp, sizeof(cp), name_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send set_name cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void pair_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       struct mgmt_rp_pair_device *rp = rsp;
+       char addr[18];
+
+       if (len == 0 && status != 0) {
+               fprintf(stderr, "Pairing failed with status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       if (len != sizeof(*rp)) {
+               fprintf(stderr, "Unexpected pair_rsp len %u\n", len);
+               exit(EXIT_FAILURE);
+       }
+
+       ba2str(&rp->addr.bdaddr, addr);
+
+       if (status != 0) {
+               fprintf(stderr,
+                       "Pairing with %s (%s) failed. status 0x%02x (%s)\n",
+                       addr, typestr(rp->addr.type), status,
+                       mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       printf("Paired with %s\n", addr);
+
+       exit(EXIT_SUCCESS);
+}
+
+static void pair_usage(void)
+{
+       printf("Usage: btmgmt pair [-c cap] [-t type] <remote address>\n");
+}
+
+static struct option pair_options[] = {
+       { "help",       0, 0, 'h' },
+       { "capability", 1, 0, 'c' },
+       { "type",       1, 0, 't' },
+       { 0, 0, 0, 0 }
+};
+
+static void cmd_pair(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_pair_device cp;
+       uint8_t cap = 0x01;
+       uint8_t type = BDADDR_BREDR;
+       int opt;
+
+       while ((opt = getopt_long(argc, argv, "+c:t:h", pair_options,
+                                                               NULL)) != -1) {
+               switch (opt) {
+               case 'c':
+                       cap = strtol(optarg, NULL, 0);
+                       break;
+               case 't':
+                       type = strtol(optarg, NULL, 0);
+                       break;
+               case 'h':
+               default:
+                       pair_usage();
+                       exit(EXIT_SUCCESS);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               pair_usage();
+               exit(EXIT_FAILURE);
+       }
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       memset(&cp, 0, sizeof(cp));
+       str2ba(argv[0], &cp.addr.bdaddr);
+       cp.addr.type = type;
+       cp.io_cap = cap;
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_PAIR_DEVICE, index, &cp, sizeof(cp),
+                                                       pair_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send pair_device cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void unpair_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       struct mgmt_rp_unpair_device *rp = rsp;
+       char addr[18];
+
+       if (len == 0 && status != 0) {
+               fprintf(stderr, "Unpair device failed. status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       if (len != sizeof(*rp)) {
+               fprintf(stderr, "Unexpected unpair_device_rsp len %u\n", len);
+               exit(EXIT_FAILURE);
+       }
+
+       ba2str(&rp->addr.bdaddr, addr);
+
+       if (status != 0) {
+               fprintf(stderr,
+                       "Unpairing %s failed. status 0x%02x (%s)\n",
+                               addr, status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       printf("%s unpaired\n", addr);
+
+       exit(EXIT_SUCCESS);
+}
+
+static void cmd_unpair(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_unpair_device cp;
+
+       if (argc < 2) {
+               printf("Usage: btmgmt %s <remote address>\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       memset(&cp, 0, sizeof(cp));
+       str2ba(argv[1], &cp.addr.bdaddr);
+       cp.disconnect = 1;
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_UNPAIR_DEVICE, index, &cp,
+                                       sizeof(cp), unpair_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send unpair_device cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void keys_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       if (status != 0) {
+               fprintf(stderr, "Load keys failed with status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       printf("Keys successfully loaded\n");
+
+       exit(EXIT_SUCCESS);
+}
+
+static void cmd_keys(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_load_link_keys cp;
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       memset(&cp, 0, sizeof(cp));
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_LOAD_LINK_KEYS, index,
+                               &cp, sizeof(cp), keys_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send load_keys cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void block_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       struct mgmt_addr_info *rp = rsp;
+       char addr[18];
+
+       if (len == 0 && status != 0) {
+               fprintf(stderr, "%s failed, status 0x%02x (%s)\n",
+                               mgmt_opstr(op), status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       if (len != sizeof(*rp)) {
+               fprintf(stderr, "Unexpected %s len %u\n", mgmt_opstr(op), len);
+               exit(EXIT_FAILURE);
+       }
+
+       ba2str(&rp->bdaddr, addr);
+
+       if (status != 0) {
+               fprintf(stderr, "%s %s (%s) failed. status 0x%02x (%s)\n",
+                               mgmt_opstr(op), addr, typestr(rp->type),
+                               status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       printf("%s %s succeeded\n", mgmt_opstr(op), addr);
+
+       exit(EXIT_SUCCESS);
+}
+
+static void block_usage(void)
+{
+       printf("Usage: btmgmt block [-t type] <remote address>\n");
+}
+
+static struct option block_options[] = {
+       { "help",       0, 0, 'h' },
+       { "type",       1, 0, 't' },
+       { 0, 0, 0, 0 }
+};
+
+static void cmd_block(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_block_device cp;
+       uint8_t type = BDADDR_BREDR;
+       int opt;
+
+       while ((opt = getopt_long(argc, argv, "+t:h", block_options,
+                                                       NULL)) != -1) {
+               switch (opt) {
+               case 't':
+                       type = strtol(optarg, NULL, 0);
+                       break;
+               case 'h':
+               default:
+                       block_usage();
+                       exit(EXIT_SUCCESS);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               block_usage();
+               exit(EXIT_FAILURE);
+       }
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       memset(&cp, 0, sizeof(cp));
+       str2ba(argv[0], &cp.addr.bdaddr);
+       cp.addr.type = type;
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_BLOCK_DEVICE, index,
+                               &cp, sizeof(cp), block_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send block_device cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void unblock_usage(void)
+{
+       printf("Usage: btmgmt unblock [-t type] <remote address>\n");
+}
+
+static void cmd_unblock(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_unblock_device cp;
+       uint8_t type = BDADDR_BREDR;
+       int opt;
+
+       while ((opt = getopt_long(argc, argv, "+t:h", block_options,
+                                                       NULL)) != -1) {
+               switch (opt) {
+               case 't':
+                       type = strtol(optarg, NULL, 0);
+                       break;
+               case 'h':
+               default:
+                       unblock_usage();
+                       exit(EXIT_SUCCESS);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               unblock_usage();
+               exit(EXIT_FAILURE);
+       }
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       memset(&cp, 0, sizeof(cp));
+       str2ba(argv[0], &cp.addr.bdaddr);
+       cp.addr.type = type;
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_UNBLOCK_DEVICE, index,
+                               &cp, sizeof(cp), block_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send unblock_device cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void uuid_to_uuid128(uuid_t *uuid128, const uuid_t *uuid)
+{
+       if (uuid->type == SDP_UUID16)
+               sdp_uuid16_to_uuid128(uuid128, uuid);
+       else if (uuid->type == SDP_UUID32)
+               sdp_uuid32_to_uuid128(uuid128, uuid);
+       else
+               memcpy(uuid128, uuid, sizeof(*uuid));
+}
+
+static void cmd_add_uuid(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_add_uuid cp;
+       uint128_t uint128;
+       uuid_t uuid, uuid128;
+
+       if (argc < 3) {
+               printf("UUID and service hint needed\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       if (bt_string2uuid(&uuid, argv[1]) < 0) {
+               printf("Invalid UUID: %s\n", argv[1]);
+               exit(EXIT_FAILURE);
+       }
+
+       memset(&cp, 0, sizeof(cp));
+
+       uuid_to_uuid128(&uuid128, &uuid);
+       ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+       htob128(&uint128, (uint128_t *) cp.uuid);
+
+       cp.svc_hint = atoi(argv[2]);
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_ADD_UUID, index,
+                               &cp, sizeof(cp), class_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send add_uuid cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void cmd_remove_uuid(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_remove_uuid cp;
+       uint128_t uint128;
+       uuid_t uuid, uuid128;
+
+       if (argc < 2) {
+               printf("UUID needed\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       if (bt_string2uuid(&uuid, argv[1]) < 0) {
+               printf("Invalid UUID: %s\n", argv[1]);
+               exit(EXIT_FAILURE);
+       }
+
+       memset(&cp, 0, sizeof(cp));
+
+       uuid_to_uuid128(&uuid128, &uuid);
+       ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+       htob128(&uint128, (uint128_t *) cp.uuid);
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_REMOVE_UUID, index,
+                               &cp, sizeof(cp), class_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send remove_uuid cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void cmd_clr_uuids(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       char *uuid_any = "00000000-0000-0000-0000-000000000000";
+       char *rm_argv[] = { "rm-uuid", uuid_any, NULL };
+
+       cmd_remove_uuid(mgmt_sk, index, 2, rm_argv);
+}
+
+static void did_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+                               void *rsp, uint16_t len, void *user_data)
+{
+       if (status != 0) {
+               fprintf(stderr, "Set Device ID failed with status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+               exit(EXIT_FAILURE);
+       }
+
+       printf("Device ID successfully set\n");
+
+       exit(EXIT_SUCCESS);
+}
+
+static void did_usage(void)
+{
+       printf("Usage: btmgmt did <source>:<vendor>:<product>:<version>\n");
+       printf("       possible source values: bluetooth, usb\n");
+}
+
+static void cmd_did(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_set_device_id cp;
+       uint16_t vendor, product, version , source;
+       int result;
+
+       if (argc < 2) {
+               did_usage();
+               exit(EXIT_FAILURE);
+       }
+
+       result = sscanf(argv[1], "bluetooth:%4hx:%4hx:%4hx", &vendor, &product,
+                                                               &version);
+       if (result == 3) {
+               source = 0x0001;
+               goto done;
+       }
+
+       result = sscanf(argv[1], "usb:%4hx:%4hx:%4hx", &vendor, &product,
+                                                               &version);
+       if (result == 3) {
+               source = 0x0002;
+               goto done;
+       }
+
+       did_usage();
+       exit(EXIT_FAILURE);
+
+done:
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       cp.source = htobs(source);
+       cp.vendor = htobs(vendor);
+       cp.product = htobs(product);
+       cp.version = htobs(version);
+
+       if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_DEVICE_ID, index,
+                               &cp, sizeof(cp), did_rsp, NULL) < 0) {
+               fprintf(stderr, "Unable to send set_dev_class cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static struct {
+       char *cmd;
+       void (*func)(int mgmt_sk, uint16_t index, int argc, char **argv);
+       char *doc;
+} command[] = {
+       { "monitor",    cmd_monitor,    "Monitor events"                },
+       { "version",    cmd_version,    "Get the MGMT Version"          },
+       { "commands",   cmd_commands,   "List supported commands"       },
+       { "info",       cmd_info,       "Show controller info"          },
+       { "power",      cmd_power,      "Toggle powered state"          },
+       { "discov",     cmd_discov,     "Toggle discoverable state"     },
+       { "connectable",cmd_connectable,"Toggle connectable state"      },
+       { "pairable",   cmd_pairable,   "Toggle pairable state"         },
+       { "linksec",    cmd_linksec,    "Toggle link level security"    },
+       { "ssp",        cmd_ssp,        "Toggle SSP mode"               },
+       { "hs",         cmd_hs,         "Toggle HS Support"             },
+       { "le",         cmd_le,         "Toggle LE Support"             },
+       { "class",      cmd_class,      "Set device major/minor class"  },
+       { "disconnect", cmd_disconnect, "Disconnect device"             },
+       { "con",        cmd_con,        "List connections"              },
+       { "find",       cmd_find,       "Discover nearby devices"       },
+       { "name",       cmd_name,       "Set local name"                },
+       { "pair",       cmd_pair,       "Pair with a remote device"     },
+       { "unpair",     cmd_unpair,     "Unpair device"                 },
+       { "keys",       cmd_keys,       "Load Keys"                     },
+       { "block",      cmd_block,      "Block Device"                  },
+       { "unblock",    cmd_unblock,    "Unblock Device"                },
+       { "add-uuid",   cmd_add_uuid,   "Add UUID"                      },
+       { "rm-uuid",    cmd_add_uuid,   "Remove UUID"                   },
+       { "clr-uuids",  cmd_clr_uuids,  "Clear UUIDs",                  },
+       { "did",        cmd_did,        "Set Device ID",                },
+       { NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+       int i;
+
+       printf("btmgmt ver %s\n", VERSION);
+       printf("Usage:\n"
+               "\tbtmgmt [options] <command> [command parameters]\n");
+
+       printf("Options:\n"
+               "\t--index <id>\tSpecify adapter index\n"
+               "\t--verbose\tEnable extra logging\n"
+               "\t--help\tDisplay help\n");
+
+       printf("Commands:\n");
+       for (i = 0; command[i].cmd; i++)
+               printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
+
+       printf("\n"
+               "For more information on the usage of each command use:\n"
+               "\tbtmgmt <command> --help\n" );
+}
+
+static struct option main_options[] = {
+       { "index",      1, 0, 'i' },
+       { "verbose",    0, 0, 'v' },
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       int opt, i, mgmt_sk;
+       uint16_t index = MGMT_INDEX_NONE;
+       struct pollfd pollfd;
+
+       while ((opt = getopt_long(argc, argv, "+hvi:",
+                                               main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'i':
+                       if (strlen(optarg) > 3 &&
+                                       strncasecmp(optarg, "hci", 3) == 0)
+                               index = atoi(&optarg[4]);
+                       else
+                               index = atoi(optarg);
+                       break;
+               case 'v':
+                       monitor = true;
+                       break;
+               case 'h':
+               default:
+                       usage();
+                       return 0;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               return 0;
+       }
+
+       mgmt_sk = mgmt_open();
+       if (mgmt_sk < 0) {
+               fprintf(stderr, "Unable to open mgmt socket\n");
+               return -1;
+       }
+
+       for (i = 0; command[i].cmd; i++) {
+               if (strcmp(command[i].cmd, argv[0]) != 0)
+                       continue;
+
+               command[i].func(mgmt_sk, index, argc, argv);
+               break;
+       }
+
+       if (command[i].cmd == NULL) {
+               fprintf(stderr, "Unknown command: %s\n", argv[0]);
+               close(mgmt_sk);
+               return -1;
+       }
+
+       pollfd.fd = mgmt_sk;
+       pollfd.events = POLLIN;
+       pollfd.revents = 0;
+
+       while (poll(&pollfd, 1, -1) >= 0) {
+               if (pollfd.revents & (POLLHUP | POLLERR | POLLNVAL))
+                       break;
+
+               if (pollfd.revents & POLLIN)
+                       mgmt_process_data(mgmt_sk);
+
+               pollfd.revents = 0;
+       }
+
+       close(mgmt_sk);
+
+       return 0;
+}
diff --git a/missing b/missing
new file mode 100755 (executable)
index 0000000..86a8fc3
--- /dev/null
+++ b/missing
@@ -0,0 +1,331 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2012-01-06.13; # UTC
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
+# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+  echo 1>&2 "Try \`$0 --help' for more information"
+  exit 1
+fi
+
+run=:
+sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
+sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+  configure_ac=configure.ac
+else
+  configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case $1 in
+--run)
+  # Try to run requested program, and just exit if it succeeds.
+  run=
+  shift
+  "$@" && exit 0
+  # Exit code 63 means version mismatch.  This often happens
+  # when the user try to use an ancient version of a tool on
+  # a file that requires a minimum version.  In this case we
+  # we should proceed has if the program had been absent, or
+  # if --run hadn't been passed.
+  if test $? = 63; then
+    run=:
+    msg="probably too old"
+  fi
+  ;;
+
+  -h|--h|--he|--hel|--help)
+    echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+  -h, --help      display this help and exit
+  -v, --version   output version information and exit
+  --run           try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+  aclocal      touch file \`aclocal.m4'
+  autoconf     touch file \`configure'
+  autoheader   touch file \`config.h.in'
+  autom4te     touch the output file, or create a stub one
+  automake     touch all \`Makefile.in' files
+  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
+  flex         create \`lex.yy.c', if possible, from existing .c
+  help2man     touch the output file
+  lex          create \`lex.yy.c', if possible, from existing .c
+  makeinfo     touch the output file
+  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
+\`g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+    exit $?
+    ;;
+
+  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+    echo "missing $scriptversion (GNU Automake)"
+    exit $?
+    ;;
+
+  -*)
+    echo 1>&2 "$0: Unknown \`$1' option"
+    echo 1>&2 "Try \`$0 --help' for more information"
+    exit 1
+    ;;
+
+esac
+
+# normalize program name to check for.
+program=`echo "$1" | sed '
+  s/^gnu-//; t
+  s/^gnu//; t
+  s/^g//; t'`
+
+# Now exit if we have it, but it failed.  Also exit now if we
+# don't have it and --version was passed (most likely to detect
+# the program).  This is about non-GNU programs, so use $1 not
+# $program.
+case $1 in
+  lex*|yacc*)
+    # Not GNU programs, they don't have --version.
+    ;;
+
+  *)
+    if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+       # We have it, but it failed.
+       exit 1
+    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+       # Could not run --version or --help.  This is probably someone
+       # running `$TOOL --version' or `$TOOL --help' to check whether
+       # $TOOL exists and not knowing $TOOL uses missing.
+       exit 1
+    fi
+    ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case $program in
+  aclocal*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
+         to install the \`Automake' and \`Perl' packages.  Grab them from
+         any GNU archive site."
+    touch aclocal.m4
+    ;;
+
+  autoconf*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`${configure_ac}'.  You might want to install the
+         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
+         archive site."
+    touch configure
+    ;;
+
+  autoheader*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
+         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
+         from any GNU archive site."
+    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+    test -z "$files" && files="config.h"
+    touch_files=
+    for f in $files; do
+      case $f in
+      *:*) touch_files="$touch_files "`echo "$f" |
+                                      sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+      *) touch_files="$touch_files $f.in";;
+      esac
+    done
+    touch $touch_files
+    ;;
+
+  automake*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+         You might want to install the \`Automake' and \`Perl' packages.
+         Grab them from any GNU archive site."
+    find . -type f -name Makefile.am -print |
+          sed 's/\.am$/.in/' |
+          while read f; do touch "$f"; done
+    ;;
+
+  autom4te*)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+         You might have modified some files without having the
+         proper tools for further handling them.
+         You can get \`$1' as part of \`Autoconf' from any GNU
+         archive site."
+
+    file=`echo "$*" | sed -n "$sed_output"`
+    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+    if test -f "$file"; then
+       touch $file
+    else
+       test -z "$file" || exec >$file
+       echo "#! /bin/sh"
+       echo "# Created by GNU Automake missing as a replacement of"
+       echo "#  $ $@"
+       echo "exit 0"
+       chmod +x $file
+       exit 1
+    fi
+    ;;
+
+  bison*|yacc*)
+    echo 1>&2 "\
+WARNING: \`$1' $msg.  You should only need it if
+         you modified a \`.y' file.  You may need the \`Bison' package
+         in order for those modifications to take effect.  You can get
+         \`Bison' from any GNU archive site."
+    rm -f y.tab.c y.tab.h
+    if test $# -ne 1; then
+        eval LASTARG=\${$#}
+       case $LASTARG in
+       *.y)
+           SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+           if test -f "$SRCFILE"; then
+                cp "$SRCFILE" y.tab.c
+           fi
+           SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+           if test -f "$SRCFILE"; then
+                cp "$SRCFILE" y.tab.h
+           fi
+         ;;
+       esac
+    fi
+    if test ! -f y.tab.h; then
+       echo >y.tab.h
+    fi
+    if test ! -f y.tab.c; then
+       echo 'main() { return 0; }' >y.tab.c
+    fi
+    ;;
+
+  lex*|flex*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified a \`.l' file.  You may need the \`Flex' package
+         in order for those modifications to take effect.  You can get
+         \`Flex' from any GNU archive site."
+    rm -f lex.yy.c
+    if test $# -ne 1; then
+        eval LASTARG=\${$#}
+       case $LASTARG in
+       *.l)
+           SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+           if test -f "$SRCFILE"; then
+                cp "$SRCFILE" lex.yy.c
+           fi
+         ;;
+       esac
+    fi
+    if test ! -f lex.yy.c; then
+       echo 'main() { return 0; }' >lex.yy.c
+    fi
+    ;;
+
+  help2man*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+        you modified a dependency of a manual page.  You may need the
+        \`Help2man' package in order for those modifications to take
+        effect.  You can get \`Help2man' from any GNU archive site."
+
+    file=`echo "$*" | sed -n "$sed_output"`
+    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+    if test -f "$file"; then
+       touch $file
+    else
+       test -z "$file" || exec >$file
+       echo ".ab help2man is required to generate this page"
+       exit $?
+    fi
+    ;;
+
+  makeinfo*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified a \`.texi' or \`.texinfo' file, or any other file
+         indirectly affecting the aspect of the manual.  The spurious
+         call might also be the consequence of using a buggy \`make' (AIX,
+         DU, IRIX).  You might want to install the \`Texinfo' package or
+         the \`GNU make' package.  Grab either from any GNU archive site."
+    # The file to touch is that specified with -o ...
+    file=`echo "$*" | sed -n "$sed_output"`
+    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+    if test -z "$file"; then
+      # ... or it is the one specified with @setfilename ...
+      infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+      file=`sed -n '
+       /^@setfilename/{
+         s/.* \([^ ]*\) *$/\1/
+         p
+         q
+       }' $infile`
+      # ... or it is derived from the source name (dir/f.texi becomes f.info)
+      test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
+    fi
+    # If the file does not exist, the user really needs makeinfo;
+    # let's fail without touching anything.
+    test -f $file || exit 1
+    touch $file
+    ;;
+
+  *)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+         You might have modified some files without having the
+         proper tools for further handling them.  Check the \`README' file,
+         it often tells you about the needed prerequisites for installing
+         this package.  You may also peek at any GNU archive site, in case
+         some other package would contain this missing \`$1' program."
+    exit 1
+    ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/monitor/bt.h b/monitor/bt.h
new file mode 100644 (file)
index 0000000..3a6735b
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+
+#define BT_H4_CMD_PKT  0x01
+#define BT_H4_ACL_PKT  0x02
+#define BT_H4_SCO_PKT  0x03
+#define BT_H4_EVT_PKT  0x04
+
+struct bt_hci_cmd_hdr {
+       uint16_t opcode;
+       uint8_t  plen;
+} __attribute__ ((packed));
+
+struct bt_hci_evt_hdr {
+       uint8_t  evt;
+       uint8_t  plen;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_NOP                         0x0000
+
+#define BT_HCI_CMD_INQUIRY                     0x0401
+struct bt_hci_cmd_inquiry {
+       uint8_t  lap[3];
+       uint8_t  length;
+       uint8_t  num_rsp;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_INQUIRY_CANCEL              0x0402
+
+#define BT_HCI_CMD_CREATE_CONN                 0x0405
+struct bt_hci_cmd_create_conn {
+       uint8_t  bdaddr[6];
+       uint16_t pkt_type;
+       uint8_t  pscan_rep_mode;
+       uint8_t  pscan_mode;
+       uint16_t clock_offset;
+       uint8_t  role_switch;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_DISCONNECT                  0x0406
+struct bt_hci_cmd_disconnect {
+       uint16_t handle;
+       uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ADD_SCO_CONN                        0x0407
+struct bt_hci_cmd_add_sco_conn {
+       uint16_t handle;
+       uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_CREATE_CONN_CANCEL          0x0408
+struct bt_hci_cmd_create_conn_cancel {
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ACCEPT_CONN_REQUEST         0x0409
+struct bt_hci_cmd_accept_conn_request {
+       uint8_t  bdaddr[6];
+       uint8_t  role;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REJECT_CONN_REQUEST         0x040a
+struct bt_hci_cmd_reject_conn_request {
+       uint8_t  bdaddr[6];
+       uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_CHANGE_CONN_PKT_TYPE                0x040f
+struct bt_hci_cmd_change_conn_pkt_type {
+       uint16_t handle;
+       uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REMOTE_NAME_REQUEST         0x0419
+struct bt_hci_cmd_remote_name_request {
+       uint8_t  bdaddr[6];
+       uint8_t  pscan_rep_mode;
+       uint8_t  pscan_mode;
+       uint16_t clock_offset;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL  0x041a
+struct bt_hci_cmd_remote_name_request_cancel {
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_REMOTE_FEATURES                0x041b
+struct bt_hci_cmd_read_remote_features {
+       uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_REMOTE_EXT_FEATURES    0x041c
+struct bt_hci_cmd_read_remote_ext_features {
+       uint16_t handle;
+       uint8_t  page;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_REMOTE_VERSION         0x041d
+struct bt_hci_cmd_read_remote_version {
+       uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_DEFAULT_LINK_POLICY    0x080e
+struct bt_hci_rsp_read_default_link_policy {
+       uint8_t  status;
+       uint16_t policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY   0x080f
+struct bt_hci_cmd_write_default_link_policy {
+       uint16_t policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_EVENT_MASK              0x0c01
+struct bt_hci_cmd_set_event_mask {
+       uint8_t  mask[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_RESET                       0x0c03
+
+#define BT_HCI_CMD_SET_EVENT_FILTER            0x0c05
+struct bt_hci_cmd_set_event_filter {
+       uint8_t  type;
+       uint8_t  cond_type;
+       uint8_t  cond[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_STORED_LINK_KEY                0x0c0d
+struct bt_hci_cmd_read_stored_link_key {
+       uint8_t  bdaddr[6];
+       uint8_t  read_all;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_stored_link_key {
+       uint8_t  status;
+       uint16_t max_num_keys;
+       uint16_t num_keys;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_STORED_LINK_KEY       0x0c11
+struct bt_hci_cmd_write_stored_link_key {
+       uint8_t  num_keys;
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_stored_link_key {
+       uint8_t  status;
+       uint8_t  num_keys;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_DELETE_STORED_LINK_KEY      0x0c12
+struct bt_hci_cmd_delete_stored_link_key {
+       uint8_t  bdaddr[6];
+       uint8_t  delete_all;
+} __attribute__ ((packed));
+struct bt_hci_rsp_delete_stored_link_key {
+       uint8_t  status;
+       uint16_t num_keys;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LOCAL_NAME            0x0c13
+struct bt_hci_cmd_write_local_name {
+       uint8_t  name[248];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_NAME             0x0c14
+struct bt_hci_rsp_read_local_name {
+       uint8_t  status;
+       uint8_t  name[248];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT    0x0c15
+struct bt_hci_rsp_read_conn_accept_timeout {
+       uint8_t  status;
+       uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT   0x0c16
+struct bt_hci_cmd_write_conn_accept_timeout {
+       uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_PAGE_TIMEOUT           0x0c17
+struct bt_hci_rsp_read_page_timeout {
+       uint8_t  status;
+       uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_PAGE_TIMEOUT          0x0c18
+struct bt_hci_cmd_write_page_timeout {
+       uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_SCAN_ENABLE            0x0c19
+struct bt_hci_rsp_read_scan_enable {
+       uint8_t  status;
+       uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_SCAN_ENABLE           0x0c1a
+struct bt_hci_cmd_write_scan_enable {
+       uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_AUTH_ENABLE            0x0c1f
+struct bt_hci_rsp_read_auth_enable {
+       uint8_t  status;
+       uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_AUTH_ENABLE           0x0c20
+struct bt_hci_cmd_write_auth_enable {
+       uint8_t  enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_CLASS_OF_DEV           0x0c23
+struct bt_hci_rsp_read_class_of_dev {
+       uint8_t  status;
+       uint8_t  dev_class[3];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_CLASS_OF_DEV          0x0c24
+struct bt_hci_cmd_write_class_of_dev {
+       uint8_t  dev_class[3];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_VOICE_SETTING          0x0c25
+struct bt_hci_rsp_read_voice_setting {
+       uint8_t  status;
+       uint16_t setting;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_VOICE_SETTING         0x0c26
+struct bt_hci_cmd_write_voice_setting {
+       uint16_t setting;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_INQUIRY_MODE           0x0c44
+struct bt_hci_rsp_read_inquiry_mode {
+       uint8_t  status;
+       uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_INQUIRY_MODE          0x0c45
+struct bt_hci_cmd_write_inquiry_mode {
+       uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_AFH_ASSESS_MODE                0x0c48
+struct bt_hci_rsp_read_afh_assess_mode {
+       uint8_t  status;
+       uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_AFH_ASSESS_MODE       0x0c49
+struct bt_hci_cmd_write_afh_assess_mode {
+       uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_EXT_INQUIRY_RSP                0x0c51
+struct bt_hci_rsp_read_ext_inquiry_rsp {
+       uint8_t  status;
+       uint8_t  fec;
+       uint8_t  data[240];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_EXT_INQUIRY_RSP       0x0c52
+struct bt_hci_cmd_write_ext_inquiry_rsp {
+       uint8_t  fec;
+       uint8_t  data[240];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE    0x0c55
+struct bt_hci_rsp_read_simple_pairing_mode {
+       uint8_t  status;
+       uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE   0x0c56
+struct bt_hci_cmd_write_simple_pairing_mode {
+       uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_INQUIRY_RSP_TX_POWER   0x0c58
+struct bt_hci_rsp_read_inquiry_rsp_tx_power {
+       uint8_t  status;
+       int8_t   level;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LE_HOST_SUPPORTED      0x0c6c
+struct bt_hci_rsp_read_le_host_supported {
+       uint8_t  status;
+       uint8_t  supported;
+       uint8_t  simultaneous;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED     0x0c6d
+struct bt_hci_cmd_write_le_host_supported {
+       uint8_t  supported;
+       uint8_t  simultaneous;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_VERSION          0x1001
+struct bt_hci_rsp_read_local_version {
+       uint8_t  status;
+       uint8_t  hci_ver;
+       uint16_t hci_rev;
+       uint8_t  lmp_ver;
+       uint16_t manufacturer;
+       uint16_t lmp_subver;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_COMMANDS         0x1002
+struct bt_hci_rsp_read_local_commands {
+       uint8_t  status;
+       uint8_t  commands[64];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_FEATURES         0x1003
+struct bt_hci_rsp_read_local_features {
+       uint8_t  status;
+       uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_EXT_FEATURES     0x1004
+struct bt_hci_cmd_read_local_ext_features {
+       uint8_t  page;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_local_ext_features {
+       uint8_t  status;
+       uint8_t  page;
+       uint8_t  max_page;
+       uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_BUFFER_SIZE            0x1005
+struct bt_hci_rsp_read_buffer_size {
+       uint8_t  status;
+       uint16_t acl_mtu;
+       uint8_t  sco_mtu;
+       uint16_t acl_max_pkt;
+       uint16_t sco_max_pkt;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_COUNTRY_CODE           0x1007
+struct bt_hci_rsp_read_country_code {
+       uint8_t  status;
+       uint8_t  code;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_BD_ADDR                        0x1009
+struct bt_hci_rsp_read_bd_addr {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_DATA_BLOCK_SIZE                0x100a
+struct bt_hci_rsp_read_data_block_size {
+       uint8_t  status;
+       uint16_t max_acl_len;
+       uint16_t block_len;
+       uint16_t num_blocks;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EVENT_MASK           0x2001
+struct bt_hci_cmd_le_set_event_mask {
+       uint8_t  mask[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_BUFFER_SIZE         0x2002
+struct bt_hci_rsp_le_read_buffer_size {
+       uint8_t  status;
+        uint16_t le_mtu;
+        uint8_t  le_max_pkt;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_LOCAL_FEATURES      0x2003
+struct bt_hci_rsp_le_read_local_features {
+       uint8_t  status;
+       uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_SCAN_PARAMETERS      0x200b
+struct bt_hci_cmd_le_set_scan_parameters {
+       uint8_t  type;
+       uint16_t interval;
+       uint16_t window;
+       uint8_t  own_addr_type;
+       uint8_t  filter_policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_SCAN_ENABLE          0x200c
+struct bt_hci_cmd_le_set_scan_enable {
+       uint8_t  enable;
+       uint8_t  filter_dup;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_SUPPORTED_STATES    0x201c
+struct bt_hci_rsp_le_read_supported_states {
+       uint8_t  status;
+       uint8_t  states[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_INQUIRY_COMPLETE            0x01
+struct bt_hci_evt_inquiry_complete {
+       uint8_t  status;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_INQUIRY_RESULT              0x02
+struct bt_hci_evt_inquiry_result {
+       uint8_t  num_resp;
+       uint8_t  bdaddr[6];
+       uint8_t  pscan_rep_mode;
+       uint8_t  pscan_period_mode;
+       uint8_t  pscan_mode;
+       uint8_t  dev_class[3];
+       uint8_t  clock_offset;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CONN_COMPLETE               0x03
+struct bt_hci_evt_conn_complete {
+       uint8_t  status;
+       uint16_t handle;
+       uint8_t  bdaddr[6];
+       uint8_t  link_type;
+       uint8_t  encr_mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CONN_REQUEST                        0x04
+struct bt_hci_evt_conn_request {
+       uint8_t  bdaddr[6];
+       uint8_t  dev_class[3];
+       uint8_t  link_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_DISCONNECT_COMPLETE         0x05
+struct bt_hci_evt_disconnect_complete {
+       uint8_t  status;
+       uint16_t handle;
+       uint8_t  reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_NAME_REQUEST_COMPLETE        0x07
+struct bt_hci_evt_remote_name_req_complete {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+       uint8_t  name[248];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_FEATURES_COMPLETE    0x0b
+struct bt_hci_evt_remote_features_complete {
+       uint8_t  status;
+       uint16_t handle;
+       uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_VERSION_COMPLETE     0x0c
+struct bt_hci_evt_remote_version_complete {
+       uint8_t  status;
+       uint16_t handle;
+       uint8_t  lmp_ver;
+       uint16_t manufacturer;
+       uint16_t lmp_subver;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CMD_COMPLETE                        0x0e
+struct bt_hci_evt_cmd_complete {
+       uint8_t  ncmd;
+       uint16_t opcode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CMD_STATUS                  0x0f
+struct bt_hci_evt_cmd_status {
+       uint8_t  status;
+       uint8_t  ncmd;
+       uint16_t opcode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_NUM_COMPLETED_PACKETS       0x13
+struct bt_hci_evt_num_completed_packets {
+       uint8_t  num_handles;
+       uint16_t handle;
+       uint16_t count;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CONN_PKT_TYPE_CHANGED       0x1d
+struct bt_hci_evt_conn_pkt_type_changed {
+       uint8_t  status;
+       uint16_t handle;
+       uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI    0x22
+struct bt_hci_evt_inquiry_result_with_rssi {
+       uint8_t  num_resp;
+       uint8_t  bdaddr[6];
+       uint8_t  pscan_rep_mode;
+       uint8_t  pscan_period_mode;
+       uint8_t  dev_class[3];
+       uint16_t clock_offset;
+       int8_t   rssi;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_EXT_FEATURES_COMPLETE        0x23
+struct bt_hci_evt_remote_ext_features_complete {
+       uint8_t  status;
+       uint16_t handle;
+       uint8_t  page;
+       uint8_t  max_page;
+       uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_EXT_INQUIRY_RESULT          0x2f
+struct bt_hci_evt_ext_inquiry_result {
+       uint8_t  num_resp;
+       uint8_t  bdaddr[6];
+       uint8_t  pscan_rep_mode;
+       uint8_t  pscan_period_mode;
+       uint8_t  dev_class[3];
+       uint16_t clock_offset;
+       int8_t   rssi;
+       uint8_t  data[240];
+} __attribute__ ((packed));
+
+#define BT_HCI_ERR_SUCCESS                     0x00
+#define BT_HCI_ERR_UNKNOWN_COMMAND             0x01
+#define BT_HCI_ERR_UNKNOWN_CONN_ID             0x02
+#define BT_HCI_ERR_HARDWARE_FAILURE            0x03
+#define BT_HCI_ERR_PAGE_TIMEOUT                        0x04
+#define BT_HCI_ERR_INVALID_PARAMETERS          0x12
diff --git a/monitor/btsnoop.c b/monitor/btsnoop.c
new file mode 100644 (file)
index 0000000..09c5e25
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#include "btsnoop.h"
+
+static inline uint64_t ntoh64(uint64_t n)
+{
+       uint64_t h;
+       uint64_t tmp = ntohl(n & 0x00000000ffffffff);
+
+       h = ntohl(n >> 32);
+       h |= tmp << 32;
+
+       return h;
+}
+
+#define hton64(x)     ntoh64(x)
+
+struct btsnoop_hdr {
+       uint8_t         id[8];          /* Identification Pattern */
+       uint32_t        version;        /* Version Number = 1 */
+       uint32_t        type;           /* Datalink Type */
+} __attribute__ ((packed));
+#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
+
+struct btsnoop_pkt {
+       uint32_t        size;           /* Original Length */
+       uint32_t        len;            /* Included Length */
+       uint32_t        flags;          /* Packet Flags */
+       uint32_t        drops;          /* Cumulative Drops */
+       uint64_t        ts;             /* Timestamp microseconds */
+       uint8_t         data[0];        /* Packet Data */
+} __attribute__ ((packed));
+#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
+
+static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
+                                     0x6f, 0x6f, 0x70, 0x00 };
+
+static const uint32_t btsnoop_version = 1;
+static const uint32_t btsnoop_type = 1001;
+
+static int btsnoop_fd = -1;
+static uint16_t btsnoop_index = 0xffff;
+
+void btsnoop_open(const char *path)
+{
+       if (btsnoop_fd >= 0)
+               return;
+
+       btsnoop_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC,
+                               S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+}
+
+void btsnoop_write(struct timeval *tv, uint16_t index, uint32_t flags,
+                                       const void *data, uint16_t size)
+{
+       struct btsnoop_hdr hdr;
+       struct btsnoop_pkt pkt;
+       uint64_t ts;
+       ssize_t written;
+
+       if (!tv)
+               return;
+
+       if (btsnoop_fd < 0)
+               return;
+
+       if (btsnoop_index == 0xffff) {
+               memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+               hdr.version = htonl(btsnoop_version);
+               hdr.type = htonl(btsnoop_type);
+
+               written = write(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE);
+               if (written < 0)
+                       return;
+
+               btsnoop_index = index;
+       }
+
+       if (index != btsnoop_index)
+               return;
+
+       ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec;
+
+       pkt.size  = htonl(size);
+       pkt.len   = htonl(size);
+       pkt.flags = htonl(flags);
+       pkt.drops = htonl(0);
+       pkt.ts    = hton64(ts + 0x00E03AB44A676000ll);
+
+       written = write(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
+       if (written < 0)
+               return;
+
+       if (data && size > 0) {
+               written = write(btsnoop_fd, data, size);
+               if (written < 0)
+                       return;
+       }
+}
+
+void btsnoop_close(void)
+{
+       if (btsnoop_fd < 0)
+               return;
+
+       close(btsnoop_fd);
+       btsnoop_fd = -1;
+
+       btsnoop_index = 0xffff;
+}
diff --git a/monitor/btsnoop.h b/monitor/btsnoop.h
new file mode 100644 (file)
index 0000000..9472d1a
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <sys/time.h>
+
+void btsnoop_open(const char *path);
+void btsnoop_write(struct timeval *tv, uint16_t index, uint32_t flags,
+                                       const void *data, uint16_t size);
+void btsnoop_close(void);
diff --git a/monitor/control.c b/monitor/control.c
new file mode 100644 (file)
index 0000000..159ba9d
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/mgmt.h>
+
+#include "mainloop.h"
+#include "packet.h"
+#include "control.h"
+
+struct control_data {
+       uint16_t channel;
+       int fd;
+};
+
+static void free_data(void *user_data)
+{
+       struct control_data *data = user_data;
+
+       close(data->fd);
+
+       free(data);
+}
+
+static void mgmt_index_added(uint16_t len, const void *buf)
+{
+       printf("@ Index Added\n");
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_index_removed(uint16_t len, const void *buf)
+{
+       printf("@ Index Removed\n");
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_controller_error(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_controller_error *ev = buf;
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Controller Error control\n");
+               return;
+       }
+
+       printf("@ Controller Error: 0x%2.2x\n", ev->error_code);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+#ifndef NELEM
+#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+static const char *settings_str[] = {
+       "powered", "connectable", "fast-connectable", "discoverable",
+       "pairable", "link-security", "ssp", "br/edr", "hs", "le"
+};
+
+static void mgmt_new_settings(uint16_t len, const void *buf)
+{
+       uint32_t settings;
+       unsigned int i;
+
+       if (len < 4) {
+               printf("* Malformed New Settings control\n");
+               return;
+       }
+
+       settings = bt_get_le32(buf);
+
+       printf("@ New Settings: 0x%4.4x\n", settings);
+
+       printf("%-12c", ' ');
+       for (i = 0; i < NELEM(settings_str); i++) {
+               if (settings & (1 << i))
+                       printf("%s ", settings_str[i]);
+       }
+       printf("\n");
+
+       buf += 4;
+       len -= 4;
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_class_of_dev_changed(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_class_of_dev_changed *ev = buf;
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Class of Device Changed control\n");
+               return;
+       }
+
+       printf("@ Class of Device Changed: 0x%2.2x%2.2x%2.2x\n",
+                                               ev->class_of_dev[2],
+                                               ev->class_of_dev[1],
+                                               ev->class_of_dev[0]);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_local_name_changed(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_local_name_changed *ev = buf;
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Local Name Changed control\n");
+               return;
+       }
+
+       printf("@ Local Name Changed: %s (%s)\n", ev->name, ev->short_name);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_new_link_key(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_new_link_key *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed New Link Key control\n");
+               return;
+       }
+
+       ba2str(&ev->key.addr.bdaddr, str);
+
+       printf("@ New Link Key: %s (%d)\n", str, ev->key.addr.type);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_new_long_term_key(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_new_long_term_key *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed New Long Term Key control\n");
+               return;
+       }
+
+       ba2str(&ev->key.addr.bdaddr, str);
+
+       printf("@ New Long Term Key: %s (%d)\n", str, ev->key.addr.type);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_device_connected(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_device_connected *ev = buf;
+       uint32_t flags;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Device Connected control\n");
+               return;
+       }
+
+       flags = btohs(ev->flags);
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ Device Connected: %s (%d) flags 0x%4.4x\n",
+                                               str, ev->addr.type, flags);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_device_disconnected(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_device_disconnected *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Device Disconnected control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ Device Disconnected: %s (%d)\n", str, ev->addr.type);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_connect_failed(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_connect_failed *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Connect Failed control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ Connect Failed: %s (%d) status 0x%2.2x\n",
+                                       str, ev->addr.type, ev->status);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_pin_code_request(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_pin_code_request *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed PIN Code Request control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ PIN Code Request: %s (%d) secure 0x%2.2x\n",
+                                       str, ev->addr.type, ev->secure);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_user_confirm_request(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_user_confirm_request *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed User Confirmation Request control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ User Confirmation Request: %s (%d) hint %d value %d\n",
+                       str, ev->addr.type, ev->confirm_hint, ev->value);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_user_passkey_request(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_user_passkey_request *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed User Passkey Request control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ PIN User Passkey Request: %s (%d)\n", str, ev->addr.type);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_auth_failed(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_auth_failed *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Authentication Failed control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ Authentication Failed: %s (%d) status 0x%2.2x\n",
+                                       str, ev->addr.type, ev->status);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_device_found(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_device_found *ev = buf;
+       uint32_t flags;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Device Found control\n");
+               return;
+       }
+
+       flags = btohs(ev->flags);
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ Device Found: %s (%d) rssi %d flags 0x%4.4x\n",
+                                       str, ev->addr.type, ev->rssi, flags);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_discovering(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_discovering *ev = buf;
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Discovering control\n");
+               return;
+       }
+
+       printf("@ Discovering: 0x%2.2x (%d)\n", ev->discovering, ev->type);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_device_blocked(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_device_blocked *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Device Blocked control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ Device Blocked: %s (%d)\n", str, ev->addr.type);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_device_unblocked(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_device_unblocked *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Device Unblocked control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ Device Unblocked: %s (%d)\n", str, ev->addr.type);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_device_unpaired(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_device_unpaired *ev = buf;
+       char str[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed Device Unpaired control\n");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, str);
+
+       printf("@ Device Unpaired: %s (%d)\n", str, ev->addr.type);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+void control_message(uint16_t opcode, const void *data, uint16_t size)
+{
+       switch (opcode) {
+       case MGMT_EV_INDEX_ADDED:
+               mgmt_index_added(size, data);
+               break;
+       case MGMT_EV_INDEX_REMOVED:
+               mgmt_index_removed(size, data);
+               break;
+       case MGMT_EV_CONTROLLER_ERROR:
+               mgmt_controller_error(size, data);
+               break;
+       case MGMT_EV_NEW_SETTINGS:
+               mgmt_new_settings(size, data);
+               break;
+       case MGMT_EV_CLASS_OF_DEV_CHANGED:
+               mgmt_class_of_dev_changed(size, data);
+               break;
+       case MGMT_EV_LOCAL_NAME_CHANGED:
+               mgmt_local_name_changed(size, data);
+               break;
+       case MGMT_EV_NEW_LINK_KEY:
+               mgmt_new_link_key(size, data);
+               break;
+       case MGMT_EV_NEW_LONG_TERM_KEY:
+               mgmt_new_long_term_key(size, data);
+               break;
+       case MGMT_EV_DEVICE_CONNECTED:
+               mgmt_device_connected(size, data);
+               break;
+       case MGMT_EV_DEVICE_DISCONNECTED:
+               mgmt_device_disconnected(size, data);
+               break;
+       case MGMT_EV_CONNECT_FAILED:
+               mgmt_connect_failed(size, data);
+               break;
+       case MGMT_EV_PIN_CODE_REQUEST:
+               mgmt_pin_code_request(size, data);
+               break;
+       case MGMT_EV_USER_CONFIRM_REQUEST:
+               mgmt_user_confirm_request(size, data);
+               break;
+       case MGMT_EV_USER_PASSKEY_REQUEST:
+               mgmt_user_passkey_request(size, data);
+               break;
+       case MGMT_EV_AUTH_FAILED:
+               mgmt_auth_failed(size, data);
+               break;
+       case MGMT_EV_DEVICE_FOUND:
+               mgmt_device_found(size, data);
+               break;
+       case MGMT_EV_DISCOVERING:
+               mgmt_discovering(size, data);
+               break;
+       case MGMT_EV_DEVICE_BLOCKED:
+               mgmt_device_blocked(size, data);
+               break;
+       case MGMT_EV_DEVICE_UNBLOCKED:
+               mgmt_device_unblocked(size, data);
+               break;
+       case MGMT_EV_DEVICE_UNPAIRED:
+               mgmt_device_unpaired(size, data);
+               break;
+       default:
+               printf("* Unknown control (code %d len %d)\n", opcode, size);
+               packet_hexdump(data, size);
+               break;
+       }
+}
+
+static void data_callback(int fd, uint32_t events, void *user_data)
+{
+       struct control_data *data = user_data;
+       unsigned char buf[HCI_MAX_FRAME_SIZE];
+       unsigned char control[32];
+       struct mgmt_hdr hdr;
+       struct msghdr msg;
+       struct iovec iov[2];
+
+       if (events & (EPOLLERR | EPOLLHUP)) {
+               mainloop_remove_fd(fd);
+               return;
+       }
+
+       iov[0].iov_base = &hdr;
+       iov[0].iov_len = MGMT_HDR_SIZE;
+       iov[1].iov_base = buf;
+       iov[1].iov_len = sizeof(buf);
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 2;
+       msg.msg_control = control;
+       msg.msg_controllen = sizeof(control);
+
+       while (1) {
+               struct cmsghdr *cmsg;
+               struct timeval *tv = NULL;
+               uint16_t opcode, index, pktlen;
+               ssize_t len;
+
+               len = recvmsg(fd, &msg, MSG_DONTWAIT);
+               if (len < 0)
+                       break;
+
+               if (len < MGMT_HDR_SIZE)
+                       break;
+
+               for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+                                       cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+                       if (cmsg->cmsg_level != SOL_SOCKET)
+                               continue;
+
+                       if (cmsg->cmsg_type == SCM_TIMESTAMP)
+                               tv = (struct timeval *) CMSG_DATA(cmsg);
+               }
+
+               opcode = btohs(hdr.opcode);
+               index  = btohs(hdr.index);
+               pktlen = btohs(hdr.len);
+
+               switch (data->channel) {
+               case HCI_CHANNEL_CONTROL:
+                       packet_control(tv, index, opcode, buf, pktlen);
+                       break;
+               case HCI_CHANNEL_MONITOR:
+                       packet_monitor(tv, index, opcode, buf, pktlen);
+                       break;
+               }
+       }
+}
+
+static int open_socket(uint16_t channel)
+{
+       struct sockaddr_hci addr;
+       int fd, opt = 1;
+
+       fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       if (fd < 0) {
+               perror("Failed to open channel");
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.hci_family = AF_BLUETOOTH;
+       addr.hci_dev = HCI_DEV_NONE;
+       addr.hci_channel = channel;
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               if (errno == EINVAL) {
+                       /* Fallback to hcidump support */
+                       close(fd);
+                       return -1;
+               }
+               perror("Failed to bind channel");
+               close(fd);
+               return -1;
+       }
+
+       if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) {
+               perror("Failed to enable timestamps");
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+static int open_channel(uint16_t channel)
+{
+       struct control_data *data;
+
+       data = malloc(sizeof(*data));
+       if (!data)
+               return -1;
+
+       memset(data, 0, sizeof(*data));
+       data->channel = channel;
+
+       data->fd = open_socket(channel);
+       if (data->fd < 0) {
+               free(data);
+               return -1;
+       }
+
+       mainloop_add_fd(data->fd, EPOLLIN, data_callback, data, free_data);
+
+       return 0;
+}
+
+int control_tracing(void)
+{
+       if (open_channel(HCI_CHANNEL_MONITOR) < 0)
+               return -1;
+
+       open_channel(HCI_CHANNEL_CONTROL);
+
+       return 0;
+}
diff --git a/monitor/control.h b/monitor/control.h
new file mode 100644 (file)
index 0000000..961f66c
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+
+int control_tracing(void);
+
+void control_message(uint16_t opcode, const void *data, uint16_t size);
diff --git a/monitor/hcidump.c b/monitor/hcidump.c
new file mode 100644 (file)
index 0000000..373d2f5
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "mainloop.h"
+#include "packet.h"
+#include "hcidump.h"
+
+struct hcidump_data {
+       uint16_t index;
+       int fd;
+};
+
+static void free_data(void *user_data)
+{
+       struct hcidump_data *data = user_data;
+
+       close(data->fd);
+
+       free(data);
+}
+
+static int open_hci_dev(uint16_t index)
+{
+       struct sockaddr_hci addr;
+       struct hci_filter flt;
+       int fd, opt = 1;
+
+       fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       if (fd < 0) {
+               perror("Failed to open channel");
+               return -1;
+       }
+
+       /* Setup filter */
+       hci_filter_clear(&flt);
+       hci_filter_all_ptypes(&flt);
+       hci_filter_all_events(&flt);
+
+       if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+               perror("Failed to set HCI filter");
+               close(fd);
+               return -1;
+       }
+
+       if (setsockopt(fd, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
+               perror("Failed to enable HCI data direction info");
+               close(fd);
+               return -1;
+       }
+
+       if (setsockopt(fd, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
+               perror("Failed to enable HCI time stamps");
+               close(fd);
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.hci_family = AF_BLUETOOTH;
+       addr.hci_dev = index;
+       addr.hci_channel = HCI_CHANNEL_RAW;
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Failed to bind channel");
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+static void device_callback(int fd, uint32_t events, void *user_data)
+{
+       struct hcidump_data *data = user_data;
+       unsigned char buf[HCI_MAX_FRAME_SIZE];
+       unsigned char control[64];
+       struct msghdr msg;
+       struct iovec iov;
+
+       if (events & (EPOLLERR | EPOLLHUP)) {
+               mainloop_remove_fd(fd);
+               return;
+       }
+
+       iov.iov_base = buf;
+       iov.iov_len = sizeof(buf);
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       msg.msg_control = control;
+       msg.msg_controllen = sizeof(control);
+
+       while (1) {
+               struct cmsghdr *cmsg;
+               struct timeval *tv = NULL;
+               int *dir = NULL;
+               ssize_t len;
+
+               len = recvmsg(fd, &msg, MSG_DONTWAIT);
+               if (len < 0)
+                       break;
+
+               for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+                                       cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+                       if (cmsg->cmsg_level != SOL_HCI)
+                               continue;
+
+                       switch (cmsg->cmsg_type) {
+                       case HCI_DATA_DIR:
+                               dir = (int *) CMSG_DATA(cmsg);
+                               break;
+                       case HCI_CMSG_TSTAMP:
+                               tv = (struct timeval *) CMSG_DATA(cmsg);
+                               break;
+                       }
+               }
+
+               if (!dir || len < 1)
+                       continue;
+
+               switch (buf[0]) {
+               case HCI_COMMAND_PKT:
+                       packet_hci_command(tv, data->index, buf + 1, len - 1);
+                       break;
+               case HCI_EVENT_PKT:
+                       packet_hci_event(tv, data->index, buf + 1, len - 1);
+                       break;
+               case HCI_ACLDATA_PKT:
+                       packet_hci_acldata(tv, data->index, !!(*dir),
+                                                       buf + 1, len - 1);
+                       break;
+               case HCI_SCODATA_PKT:
+                       packet_hci_scodata(tv, data->index, !!(*dir),
+                                                       buf + 1, len - 1);
+                       break;
+               }
+       }
+}
+
+static void open_device(uint16_t index)
+{
+       struct hcidump_data *data;
+
+       data = malloc(sizeof(*data));
+       if (!data)
+               return;
+
+       memset(data, 0, sizeof(*data));
+       data->index = index;
+
+       data->fd = open_hci_dev(index);
+       if (data->fd < 0) {
+               free(data);
+               return;
+       }
+
+       mainloop_add_fd(data->fd, EPOLLIN, device_callback, data, free_data);
+}
+
+static void device_info(int fd, uint16_t index, uint8_t *type, uint8_t *bus,
+                                               bdaddr_t *bdaddr, char *name)
+{
+       struct hci_dev_info di;
+
+       memset(&di, 0, sizeof(di));
+       di.dev_id = index;
+
+       if (ioctl(fd, HCIGETDEVINFO, (void *) &di) < 0) {
+               perror("Failed to get device information");
+               return;
+       }
+
+       *type = di.type >> 4;
+       *bus = di.type & 0x0f;
+
+       bacpy(bdaddr, &di.bdaddr);
+       memcpy(name, di.name, 8);
+}
+
+static void device_list(int fd, int max_dev)
+{
+       struct hci_dev_list_req *dl;
+       struct hci_dev_req *dr;
+       int i;
+
+       dl = malloc(max_dev * sizeof(*dr) + sizeof(*dl));
+       if (!dl) {
+               perror("Failed to allocate device list memory");
+               return;
+       }
+
+       memset(dl, 0, max_dev * sizeof(*dr) + sizeof(*dl));
+       dl->dev_num = max_dev;
+
+       dr = dl->dev_req;
+
+       if (ioctl(fd, HCIGETDEVLIST, (void *) dl) < 0) {
+               perror("Failed to get device list");
+               goto done;
+       }
+
+       for (i = 0; i < dl->dev_num; i++, dr++) {
+               struct timeval tmp_tv, *tv = NULL;
+               uint8_t type = 0xff, bus = 0xff;
+               char str[18], name[8] = "";
+               bdaddr_t bdaddr;
+
+               bacpy(&bdaddr, BDADDR_ANY);
+
+               if (!gettimeofday(&tmp_tv, NULL))
+                       tv = &tmp_tv;
+
+               device_info(fd, dr->dev_id, &type, &bus, &bdaddr, name);
+               ba2str(&bdaddr, str);
+               packet_new_index(tv, dr->dev_id, str, type, bus, name);
+               open_device(dr->dev_id);
+       }
+
+done:
+       free(dl);
+}
+
+static int open_stack_internal(void)
+{
+       struct sockaddr_hci addr;
+       struct hci_filter flt;
+       int fd, opt = 1;
+
+       fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       if (fd < 0) {
+               perror("Failed to open channel");
+               return -1;
+       }
+
+       /* Setup filter */
+       hci_filter_clear(&flt);
+       hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+       hci_filter_set_event(EVT_STACK_INTERNAL, &flt);
+
+       if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+               perror("Failed to set HCI filter");
+               close(fd);
+               return -1;
+       }
+
+       if (setsockopt(fd, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
+               perror("Failed to enable HCI time stamps");
+               close(fd);
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.hci_family = AF_BLUETOOTH;
+       addr.hci_dev = HCI_DEV_NONE;
+       addr.hci_channel = HCI_CHANNEL_RAW;
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Failed to bind channel");
+               close(fd);
+               return -1;
+       }
+
+       device_list(fd, HCI_MAX_DEV);
+
+       return fd;
+}
+
+static void stack_internal_callback(int fd, uint32_t events, void *user_data)
+{
+       unsigned char buf[HCI_MAX_FRAME_SIZE];
+       unsigned char control[32];
+       struct msghdr msg;
+       struct iovec iov;
+       struct cmsghdr *cmsg;
+       ssize_t len;
+       hci_event_hdr *eh;
+       evt_stack_internal *si;
+       evt_si_device *sd;
+       struct timeval *tv = NULL;
+       uint8_t type = 0xff, bus = 0xff;
+       char str[18], name[8] = "";
+       bdaddr_t bdaddr;
+
+       bacpy(&bdaddr, BDADDR_ANY);
+
+       if (events & (EPOLLERR | EPOLLHUP)) {
+               mainloop_remove_fd(fd);
+               return;
+       }
+
+       iov.iov_base = buf;
+       iov.iov_len = sizeof(buf);
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+       msg.msg_control = control;
+       msg.msg_controllen = sizeof(control);
+
+       len = recvmsg(fd, &msg, MSG_DONTWAIT);
+       if (len < 0)
+               return;
+
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+                                       cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+               if (cmsg->cmsg_level != SOL_HCI)
+                       continue;
+
+               switch (cmsg->cmsg_type) {
+               case HCI_CMSG_TSTAMP:
+                       tv = (struct timeval *) CMSG_DATA(cmsg);
+                       break;
+               }
+       }
+
+       if (len < 1 + HCI_EVENT_HDR_SIZE + EVT_STACK_INTERNAL_SIZE +
+                                                       EVT_SI_DEVICE_SIZE)
+               return;
+
+       if (buf[0] != HCI_EVENT_PKT)
+               return;
+
+       eh = (hci_event_hdr *) (buf + 1);
+       if (eh->evt != EVT_STACK_INTERNAL)
+               return;
+
+       si = (evt_stack_internal *) (buf + 1 + HCI_EVENT_HDR_SIZE);
+       if (si->type != EVT_SI_DEVICE)
+               return;
+
+       sd = (evt_si_device *) &si->data;
+
+       switch (sd->event) {
+       case HCI_DEV_REG:
+               device_info(fd, sd->dev_id, &type, &bus, &bdaddr, name);
+               ba2str(&bdaddr, str);
+               packet_new_index(tv, sd->dev_id, str, type, bus, name);
+               open_device(sd->dev_id);
+               break;
+       case HCI_DEV_UNREG:
+               ba2str(&bdaddr, str);
+               packet_del_index(tv, sd->dev_id, str);
+               break;
+       }
+}
+
+int hcidump_tracing(void)
+{
+       struct hcidump_data *data;
+
+       data = malloc(sizeof(*data));
+       if (!data)
+               return -1;
+
+       memset(data, 0, sizeof(*data));
+       data->index = HCI_DEV_NONE;
+
+       data->fd = open_stack_internal();
+       if (data->fd < 0) {
+               free(data);
+               return -1;
+       }
+
+       mainloop_add_fd(data->fd, EPOLLIN, stack_internal_callback,
+                                                       data, free_data);
+
+       return 0;
+}
diff --git a/monitor/hcidump.h b/monitor/hcidump.h
new file mode 100644 (file)
index 0000000..3801c98
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int hcidump_tracing(void);
diff --git a/monitor/main.c b/monitor/main.c
new file mode 100644 (file)
index 0000000..90e32c5
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include "mainloop.h"
+#include "packet.h"
+#include "control.h"
+#include "hcidump.h"
+#include "btsnoop.h"
+
+static void signal_callback(int signum, void *user_data)
+{
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               mainloop_quit();
+               break;
+       }
+}
+
+static void usage(void)
+{
+       printf("btmon - Bluetooth monitor\n"
+               "Usage:\n");
+       printf("\tbtmon [options]\n");
+       printf("options:\n"
+               "\t-b, --btsnoop <file>  Save dump in btsnoop format\n"
+               "\t-h, --help            Show help options\n");
+}
+
+static const struct option main_options[] = {
+       { "btsnoop",    required_argument, NULL, 'b'    },
+       { "version",    no_argument,       NULL, 'v'    },
+       { "help",       no_argument,       NULL, 'h'    },
+       { }
+};
+
+int main(int argc, char *argv[])
+{
+       unsigned long filter_mask = 0;
+       sigset_t mask;
+
+       mainloop_init();
+
+       for (;;) {
+               int opt;
+
+               opt = getopt_long(argc, argv, "bvh", main_options, NULL);
+               if (opt < 0)
+                       break;
+
+               switch (opt) {
+               case 'b':
+                       btsnoop_open(optarg);
+                       break;
+               case 'v':
+                       printf("%s\n", VERSION);
+                       return EXIT_SUCCESS;
+               case 'h':
+                       usage();
+                       return EXIT_SUCCESS;
+               default:
+                       return EXIT_FAILURE;
+               }
+       }
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       filter_mask |= PACKET_FILTER_SHOW_INDEX;
+       filter_mask |= PACKET_FILTER_SHOW_TIME;
+       filter_mask |= PACKET_FILTER_SHOW_ACL_DATA;
+
+       packet_set_filter(filter_mask);
+
+       printf("Bluetooth monitor ver %s\n", VERSION);
+
+       if (control_tracing() < 0) {
+               if (hcidump_tracing() < 0)
+                       return EXIT_FAILURE;
+       }
+
+       return mainloop_run();
+}
diff --git a/monitor/mainloop.c b/monitor/mainloop.c
new file mode 100644 (file)
index 0000000..1cc787e
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <sys/timerfd.h>
+#include <sys/epoll.h>
+
+#include "mainloop.h"
+
+#define MAX_EPOLL_EVENTS 10
+
+static int epoll_fd;
+static int epoll_terminate;
+
+struct mainloop_data {
+       int fd;
+       uint32_t events;
+       mainloop_event_func callback;
+       mainloop_destroy_func destroy;
+       void *user_data;
+};
+
+#define MAX_MAINLOOP_ENTRIES 128
+
+static struct mainloop_data *mainloop_list[MAX_MAINLOOP_ENTRIES];
+
+struct timeout_data {
+       int fd;
+       mainloop_timeout_func callback;
+       mainloop_destroy_func destroy;
+       void *user_data;
+};
+
+struct signal_data {
+       int fd;
+       sigset_t mask;
+       mainloop_signal_func callback;
+       mainloop_destroy_func destroy;
+       void *user_data;
+};
+
+static struct signal_data *signal_data;
+
+void mainloop_init(void)
+{
+       unsigned int i;
+
+       epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+
+       for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++)
+               mainloop_list[i] = NULL;
+
+       epoll_terminate = 0;
+}
+
+void mainloop_quit(void)
+{
+       epoll_terminate = 1;
+}
+
+static void signal_callback(int fd, uint32_t events, void *user_data)
+{
+       struct signal_data *data = user_data;
+       struct signalfd_siginfo si;
+       ssize_t result;
+
+       if (events & (EPOLLERR | EPOLLHUP)) {
+               mainloop_quit();
+               return;
+       }
+
+       result = read(fd, &si, sizeof(si));
+       if (result != sizeof(si))
+               return;
+
+       if (data->callback)
+               data->callback(si.ssi_signo, data->user_data);
+}
+
+int mainloop_run(void)
+{
+       unsigned int i;
+
+       if (signal_data) {
+               if (sigprocmask(SIG_BLOCK, &signal_data->mask, NULL) < 0)
+                       return 1;
+
+               signal_data->fd = signalfd(-1, &signal_data->mask,
+                                               SFD_NONBLOCK | SFD_CLOEXEC);
+               if (signal_data->fd < 0)
+                       return 1;
+
+               if (mainloop_add_fd(signal_data->fd, EPOLLIN,
+                               signal_callback, signal_data, NULL) < 0) {
+                       close(signal_data->fd);
+                       return 1;
+               }
+       }
+
+       while (!epoll_terminate) {
+               struct epoll_event events[MAX_EPOLL_EVENTS];
+               int n, nfds;
+
+               nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, -1);
+               if (nfds < 0)
+                       continue;
+
+               for (n = 0; n < nfds; n++) {
+                       struct mainloop_data *data = events[n].data.ptr;
+
+                       data->callback(data->fd, events[n].events,
+                                                       data->user_data);
+               }
+       }
+
+       if (signal_data) {
+               mainloop_remove_fd(signal_data->fd);
+               close(signal_data->fd);
+
+               if (signal_data->destroy)
+                       signal_data->destroy(signal_data->user_data);
+       }
+
+       for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++) {
+               struct mainloop_data *data = mainloop_list[i];
+
+               mainloop_list[i] = NULL;
+
+               if (data) {
+                       epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
+
+                       if (data->destroy)
+                               data->destroy(data->user_data);
+
+                       free(data);
+               }
+       }
+
+       close(epoll_fd);
+       epoll_fd = 0;
+
+       return 0;
+}
+
+int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
+                               void *user_data, mainloop_destroy_func destroy)
+{
+       struct mainloop_data *data;
+       struct epoll_event ev;
+       int err;
+
+       if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1 || !callback)
+               return -EINVAL;
+
+       data = malloc(sizeof(*data));
+       if (!data)
+               return -ENOMEM;
+
+       memset(data, 0, sizeof(*data));
+       data->fd = fd;
+       data->events = events;
+       data->callback = callback;
+       data->destroy = destroy;
+       data->user_data = user_data;
+
+       memset(&ev, 0, sizeof(ev));
+       ev.events = events;
+       ev.data.ptr = data;
+
+       err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev);
+       if (err < 0) {
+               free(data);
+               return err;
+       }
+
+       mainloop_list[fd] = data;
+
+       return 0;
+}
+
+int mainloop_modify_fd(int fd, uint32_t events)
+{
+       struct mainloop_data *data;
+       struct epoll_event ev;
+       int err;
+
+       if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1)
+               return -EINVAL;
+
+       data = mainloop_list[fd];
+       if (!data)
+               return -ENXIO;
+
+       if (data->events == events)
+               return 0;
+
+       memset(&ev, 0, sizeof(ev));
+       ev.events = events;
+       ev.data.ptr = data;
+
+       err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, data->fd, &ev);
+       if (err < 0)
+               return err;
+
+       data->events = events;
+
+       return 0;
+}
+
+int mainloop_remove_fd(int fd)
+{
+       struct mainloop_data *data;
+       int err;
+
+       if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1)
+               return -EINVAL;
+
+       data = mainloop_list[fd];
+       if (!data)
+               return -ENXIO;
+
+       mainloop_list[fd] = NULL;
+
+       err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
+
+       if (data->destroy)
+               data->destroy(data->user_data);
+
+       free(data);
+
+       return err;
+}
+
+static void timeout_destroy(void *user_data)
+{
+       struct timeout_data *data = user_data;
+
+       close(data->fd);
+       data->fd = -1;
+
+       if (data->destroy)
+               data->destroy(data->user_data);
+}
+
+static void timeout_callback(int fd, uint32_t events, void *user_data)
+{
+       struct timeout_data *data = user_data;
+       uint64_t expired;
+       ssize_t result;
+
+       if (events & (EPOLLERR | EPOLLHUP))
+               return;
+
+       result = read(data->fd, &expired, sizeof(expired));
+       if (result != sizeof(expired))
+               return;
+
+       if (data->callback)
+               data->callback(data->fd, data->user_data);
+}
+
+static inline int timeout_set(int fd, unsigned int seconds)
+{
+       struct itimerspec itimer;
+
+       memset(&itimer, 0, sizeof(itimer));
+       itimer.it_interval.tv_sec = 0;
+       itimer.it_interval.tv_nsec = 0;
+       itimer.it_value.tv_sec = seconds;
+       itimer.it_value.tv_nsec = 0;
+
+       return timerfd_settime(fd, 0, &itimer, NULL);
+}
+
+int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback,
+                               void *user_data, mainloop_destroy_func destroy)
+{
+       struct timeout_data *data;
+
+       if (!callback)
+               return -EINVAL;
+
+       data = malloc(sizeof(*data));
+       if (!data)
+               return -ENOMEM;
+
+       memset(data, 0, sizeof(*data));
+       data->callback = callback;
+       data->destroy = destroy;
+       data->user_data = user_data;
+
+       data->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+       if (data->fd < 0) {
+               free(data);
+               return -EIO;
+       }
+
+       if (seconds > 0) {
+               if (timeout_set(data->fd, seconds) < 0) {
+                       close(data->fd);
+                       free(data);
+                       return -EIO;
+               }
+       }
+
+       if (mainloop_add_fd(data->fd, EPOLLIN | EPOLLONESHOT,
+                               timeout_callback, data, timeout_destroy) < 0) {
+               close(data->fd);
+               free(data);
+               return -EIO;
+       }
+
+       return data->fd;
+}
+
+int mainloop_modify_timeout(int id, unsigned int seconds)
+{
+       if (seconds > 0) {
+               if (timeout_set(id, seconds) < 0)
+                       return -EIO;
+       }
+
+       if (mainloop_modify_fd(id, EPOLLIN | EPOLLONESHOT) < 0)
+               return -EIO;
+
+       return 0;
+}
+
+int mainloop_remove_timeout(int id)
+{
+       return mainloop_remove_fd(id);
+}
+
+int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback,
+                               void *user_data, mainloop_destroy_func destroy)
+{
+       struct signal_data *data;
+
+       if (!mask || !callback)
+               return -EINVAL;
+
+       data = malloc(sizeof(*data));
+       if (!data)
+               return -ENOMEM;
+
+       memset(data, 0, sizeof(*data));
+       data->callback = callback;
+       data->destroy = destroy;
+       data->user_data = user_data;
+
+       data->fd = -1;
+       memcpy(&data->mask, mask, sizeof(sigset_t));
+
+       free(signal_data);
+       signal_data = data;
+
+       return 0;
+}
diff --git a/monitor/mainloop.h b/monitor/mainloop.h
new file mode 100644 (file)
index 0000000..04745d2
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <signal.h>
+#include <sys/epoll.h>
+
+typedef void (*mainloop_destroy_func) (void *user_data);
+
+typedef void (*mainloop_event_func) (int fd, uint32_t events, void *user_data);
+typedef void (*mainloop_timeout_func) (int id, void *user_data);
+typedef void (*mainloop_signal_func) (int signum, void *user_data);
+
+void mainloop_init(void);
+void mainloop_quit(void);
+int mainloop_run(void);
+
+int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
+                               void *user_data, mainloop_destroy_func destroy);
+int mainloop_modify_fd(int fd, uint32_t events);
+int mainloop_remove_fd(int fd);
+
+int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback,
+                               void *user_data, mainloop_destroy_func destroy);
+int mainloop_modify_timeout(int fd, unsigned int seconds);
+int mainloop_remove_timeout(int id);
+
+int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback,
+                               void *user_data, mainloop_destroy_func destroy);
diff --git a/monitor/packet.c b/monitor/packet.c
new file mode 100644 (file)
index 0000000..0f14ea6
--- /dev/null
@@ -0,0 +1,664 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "control.h"
+#include "btsnoop.h"
+#include "packet.h"
+
+static unsigned long filter_mask = 0;
+
+void packet_set_filter(unsigned long filter)
+{
+       filter_mask = filter;
+}
+
+static void print_channel_header(struct timeval *tv, uint16_t index,
+                                                       uint16_t channel)
+{
+       if (filter_mask & PACKET_FILTER_SHOW_INDEX) {
+               switch (channel) {
+               case HCI_CHANNEL_CONTROL:
+                       printf("{hci%d} ", index);
+                       break;
+               case HCI_CHANNEL_MONITOR:
+                       printf("[hci%d] ", index);
+                       break;
+               }
+       }
+
+       if (tv) {
+               time_t t = tv->tv_sec;
+               struct tm tm;
+
+               localtime_r(&t, &tm);
+
+               if (filter_mask & PACKET_FILTER_SHOW_DATE)
+                       printf("%04d-%02d-%02d ", tm.tm_year + 1900,
+                                               tm.tm_mon + 1, tm.tm_mday);
+
+               if (filter_mask & PACKET_FILTER_SHOW_TIME)
+                       printf("%02d:%02d:%02d.%06lu ", tm.tm_hour,
+                                       tm.tm_min, tm.tm_sec, tv->tv_usec);
+       }
+}
+
+static void print_header(struct timeval *tv, uint16_t index)
+{
+       print_channel_header(tv, index, HCI_CHANNEL_MONITOR);
+}
+
+void packet_hexdump(const unsigned char *buf, uint16_t len)
+{
+       static const char hexdigits[] = "0123456789abcdef";
+       char str[68];
+       uint16_t i;
+
+       if (!len)
+               return;
+
+       for (i = 0; i < len; i++) {
+               str[((i % 16) * 3) + 0] = hexdigits[buf[i] >> 4];
+               str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf];
+               str[((i % 16) * 3) + 2] = ' ';
+               str[(i % 16) + 49] = isprint(buf[i]) ? buf[i] : '.';
+
+               if ((i + 1) % 16 == 0) {
+                       str[47] = ' ';
+                       str[48] = ' ';
+                       str[65] = '\0';
+                       printf("%-12c%s\n", ' ', str);
+                       str[0] = ' ';
+               }
+       }
+
+       if (i % 16 > 0) {
+               uint16_t j;
+               for (j = (i % 16); j < 16; j++) {
+                       str[(j * 3) + 0] = ' ';
+                       str[(j * 3) + 1] = ' ';
+                       str[(j * 3) + 2] = ' ';
+                       str[j + 49] = ' ';
+               }
+               str[47] = ' ';
+               str[48] = ' ';
+               str[65] = '\0';
+               printf("%-12c%s\n", ' ', str);
+       }
+}
+
+void packet_control(struct timeval *tv, uint16_t index, uint16_t opcode,
+                                       const void *data, uint16_t size)
+{
+       print_channel_header(tv, index, HCI_CHANNEL_CONTROL);
+
+       control_message(opcode, data, size);
+}
+
+#define MONITOR_NEW_INDEX      0
+#define MONITOR_DEL_INDEX      1
+#define MONITOR_COMMAND_PKT    2
+#define MONITOR_EVENT_PKT      3
+#define MONITOR_ACL_TX_PKT     4
+#define MONITOR_ACL_RX_PKT     5
+#define MONITOR_SCO_TX_PKT     6
+#define MONITOR_SCO_RX_PKT     7
+
+struct monitor_new_index {
+       uint8_t  type;
+       uint8_t  bus;
+       bdaddr_t bdaddr;
+       char     name[8];
+} __attribute__((packed));
+
+#define MONITOR_NEW_INDEX_SIZE 16
+
+#define MONITOR_DEL_INDEX_SIZE 0
+
+#define MAX_INDEX 16
+
+static struct monitor_new_index index_list[MAX_INDEX];
+
+void packet_monitor(struct timeval *tv, uint16_t index, uint16_t opcode,
+                                       const void *data, uint16_t size)
+{
+       const struct monitor_new_index *ni;
+       char str[18];
+
+       switch (opcode) {
+       case MONITOR_NEW_INDEX:
+               ni = data;
+
+               if (index < MAX_INDEX)
+                       memcpy(&index_list[index], ni, MONITOR_NEW_INDEX_SIZE);
+
+               ba2str(&ni->bdaddr, str);
+               packet_new_index(tv, index, str, ni->type, ni->bus, ni->name);
+               break;
+       case MONITOR_DEL_INDEX:
+               if (index < MAX_INDEX)
+                       ba2str(&index_list[index].bdaddr, str);
+               else
+                       ba2str(BDADDR_ANY, str);
+
+               packet_del_index(tv, index, str);
+               break;
+       case MONITOR_COMMAND_PKT:
+               packet_hci_command(tv, index, data, size);
+               break;
+       case MONITOR_EVENT_PKT:
+               packet_hci_event(tv, index, data, size);
+               break;
+       case MONITOR_ACL_TX_PKT:
+               packet_hci_acldata(tv, index, false, data, size);
+               break;
+       case MONITOR_ACL_RX_PKT:
+               packet_hci_acldata(tv, index, true, data, size);
+               break;
+       case MONITOR_SCO_TX_PKT:
+               packet_hci_scodata(tv, index, false, data, size);
+               break;
+       case MONITOR_SCO_RX_PKT:
+               packet_hci_scodata(tv, index, true, data, size);
+               break;
+       default:
+               print_header(tv, index);
+               printf("* Unknown packet (code %d len %d)\n", opcode, size);
+               packet_hexdump(data, size);
+               break;
+       }
+}
+
+static const struct {
+       uint16_t opcode;
+       const char *str;
+} opcode2str_table[] = {
+       /* OGF 1 - Link Control */
+       { 0x0401, "Inquiry"                             },
+       { 0x0402, "Inquiry Cancel"                      },
+       { 0x0403, "Periodic Inquiry Mode"               },
+       { 0x0404, "Exit Periodic Inquiry Mode"          },
+       { 0x0405, "Create Connection"                   },
+       { 0x0406, "Disconnect"                          },
+       { 0x0407, "Add SCO Connection"                  },
+       { 0x0408, "Create Connection Cancel"            },
+       { 0x0409, "Accept Connection Request"           },
+       { 0x040a, "Reject Connection Request"           },
+       { 0x040b, "Link Key Request Reply"              },
+       { 0x040c, "Link Key Request Negative Reply"     },
+       { 0x040d, "PIN Code Request Reply"              },
+       { 0x040e, "PIN Code Request Negative Reply"     },
+       { 0x040f, "Change Connection Packet Type"       },
+       /* reserved command */
+       { 0x0411, "Authentication Requested"            },
+       /* reserved command */
+       { 0x0413, "Set Connection Encryption"           },
+       /* reserved command */
+       { 0x0415, "Change Connection Link Key"          },
+       /* reserved command */
+       { 0x0417, "Master Link Key"                     },
+       /* reserved command */
+       { 0x0419, "Remote Name Request"                 },
+       { 0x041a, "Remote Name Request Cancel"          },
+       { 0x041b, "Read Remote Supported Features"      },
+       { 0x041c, "Read Remote Extended Features"       },
+       { 0x041d, "Read Remote Version Information"     },
+       /* reserved command */
+       { 0x041f, "Read Clock Offset"                   },
+       { 0x0420, "Read LMP Handle"                     },
+       /* reserved commands */
+       { 0x0428, "Setup Synchronous Connection"        },
+       { 0x0429, "Accept Synchronous Connection"       },
+       { 0x042a, "Reject Synchronous Connection"       },
+       { 0x042b, "IO Capability Request Reply"         },
+       { 0x042c, "User Confirmation Request Reply"     },
+       { 0x042d, "User Confirmation Request Neg Reply" },
+       { 0x042e, "User Passkey Request Reply"          },
+       { 0x042f, "User Passkey Request Negative Reply" },
+       { 0x0430, "Remote OOB Data Request Reply"       },
+       /* reserved commands */
+       { 0x0433, "Remote OOB Data Request Neg Reply"   },
+       { 0x0434, "IO Capability Request Negative Reply"},
+       { 0x0435, "Create Physical Link"                },
+       { 0x0436, "Accept Physical Link"                },
+       { 0x0437, "Disconnect Physical Link"            },
+       { 0x0438, "Create Logical Link"                 },
+       { 0x0439, "Accept Logical Link"                 },
+       { 0x043a, "Disconnect Logical Link"             },
+       { 0x043b, "Logical Link Cancel"                 },
+       { 0x043c, "Flow Specifcation Modify"            },
+
+       /* OGF 2 - Link Policy */
+       { 0x0801, "Holde Mode"                          },
+       /* reserved command */
+       { 0x0803, "Sniff Mode"                          },
+       { 0x0804, "Exit Sniff Mode"                     },
+       { 0x0805, "Park State"                          },
+       { 0x0806, "Exit Park State"                     },
+       { 0x0807, "QoS Setup"                           },
+       /* reserved command */
+       { 0x0809, "Role Discovery"                      },
+       /* reserved command */
+       { 0x080b, "Switch Role"                         },
+       { 0x080c, "Read Link Policy Settings"           },
+       { 0x080d, "Write Link Policy Settings"          },
+       { 0x080e, "Read Default Link Policy Settings"   },
+       { 0x080f, "Write Default Link Policy Settings"  },
+       { 0x0810, "Flow Specification"                  },
+       { 0x0811, "Sniff Subrating"                     },
+
+       /* OGF 3 - Host Control */
+       { 0x0c01, "Set Event Mask"                      },
+       /* reserved command */
+       { 0x0c03, "Reset"                               },
+       /* reserved command */
+       { 0x0c05, "Set Event Filter"                    },
+       /* reserved commands */
+       { 0x0c08, "Flush"                               },
+       { 0x0c09, "Read PIN Type"                       },
+       { 0x0c0a, "Write PIN Type"                      },
+       { 0x0c0b, "Create New Unit Key"                 },
+       /* reserved command */
+       { 0x0c0d, "Read Stored Link Key"                },
+       /* reserved commands */
+       { 0x0c11, "Write Stored Link Key"               },
+       { 0x0c12, "Delete Stored Link Key"              },
+       { 0x0c13, "Write Local Name"                    },
+       { 0x0c14, "Read Local Name"                     },
+       { 0x0c15, "Read Connection Accept Timeout"      },
+       { 0x0c16, "Write Connection Accept Timeout"     },
+       { 0x0c17, "Read Page Timeout"                   },
+       { 0x0c18, "Write Page Timeout"                  },
+       { 0x0c19, "Read Scan Enable"                    },
+       { 0x0c1a, "Write Scan Enable"                   },
+       { 0x0c1b, "Read Page Scan Activity"             },
+       { 0x0c1c, "Write Page Scan Activity"            },
+       { 0x0c1d, "Read Inquiry Scan Activity"          },
+       { 0x0c1e, "Write Inquiry Scan Activity"         },
+       { 0x0c1f, "Read Authentication Enable"          },
+       { 0x0c20, "Write Authentication Enable"         },
+       { 0x0c21, "Read Encryption Mode"                },
+       { 0x0c22, "Write Encryption Mode"               },
+       { 0x0c23, "Read Class of Device"                },
+       { 0x0c24, "Write Class of Device"               },
+       { 0x0c25, "Read Voice Setting"                  },
+       { 0x0c26, "Write Voice Setting"                 },
+       { 0x0c27, "Read Automatic Flush Timeout"        },
+       { 0x0c28, "Write Automatic Flush Timeout"       },
+       { 0x0c29, "Read Num Broadcast Retransmissions"  },
+       { 0x0c2a, "Write Num Broadcast Retransmissions" },
+       { 0x0c2b, "Read Hold Mode Activity"             },
+       { 0x0c2c, "Write Hold Mode Activity"            },
+       { 0x0c2d, "Read Transmit Power Level"           },
+       { 0x0c2e, "Read Sync Flow Control Enable"       },
+       { 0x0c2f, "Write Sync Flow Control Enable"      },
+       /* reserved command */
+       { 0x0c31, "Set Host Controller To Host Flow"    },
+       /* reserved command */
+       { 0x0c33, "Host Buffer Size"                    },
+       /* reserved command */
+       { 0x0c35, "Host Number of Completed Packets"    },
+       { 0x0c36, "Read Link Supervision Timeout"       },
+       { 0x0c37, "Write Link Supervision Timeout"      },
+       { 0x0c38, "Read Number of Supported IAC"        },
+       { 0x0c39, "Read Current IAC LAP"                },
+       { 0x0c3a, "Write Current IAC LAP"               },
+       { 0x0c3b, "Read Page Scan Period Mode"          },
+       { 0x0c3c, "Write Page Scan Period Mode"         },
+       { 0x0c3d, "Read Page Scan Mode"                 },
+       { 0x0c3e, "Write Page Scan Mode"                },
+       { 0x0c3f, "Set AFH Host Channel Classification" },
+       /* reserved commands */
+       { 0x0c42, "Read Inquiry Scan Type"              },
+       { 0x0c43, "Write Inquiry Scan Type"             },
+       { 0x0c44, "Read Inquiry Mode"                   },
+       { 0x0c45, "Write Inquiry Mode"                  },
+       { 0x0c46, "Read Page Scan Type"                 },
+       { 0x0c47, "Write Page Scan Type"                },
+       { 0x0c48, "Read AFH Channel Assessment Mode"    },
+       { 0x0c49, "Write AFH Channel Assessment Mode"   },
+       /* reserved commands */
+       { 0x0c51, "Read Extended Inquiry Response"      },
+       { 0x0c52, "Write Extended Inquiry Response"     },
+       { 0x0c53, "Refresh Encryption Key"              },
+       /* reserved command */
+       { 0x0c55, "Read Simple Pairing Mode"            },
+       { 0x0c56, "Write Simple Pairing Mode"           },
+       { 0x0c57, "Read Local OOB Data"                 },
+       { 0x0c58, "Read Inquiry Response TX Power Level"},
+       { 0x0c59, "Write Inquiry Transmit Power Level"  },
+       { 0x0c5a, "Read Default Erroneous Reporting"    },
+       { 0x0c5b, "Write Default Erroneous Reporting"   },
+       /* reserved commands */
+       { 0x0c5f, "Enhanced Flush"                      },
+       /* reserved command */
+       { 0x0c61, "Read Logical Link Accept Timeout"    },
+       { 0x0c62, "Write Logical Link Accept Timeout"   },
+       { 0x0c63, "Set Event Mask Page 2"               },
+       { 0x0c64, "Read Location Data"                  },
+       { 0x0c65, "Write Location Data"                 },
+       { 0x0c66, "Read Flow Control Mode"              },
+       { 0x0c67, "Write Flow Control Mode"             },
+       { 0x0c68, "Read Enhanced Transmit Power Level"  },
+       { 0x0c69, "Read Best Effort Flush Timeout"      },
+       { 0x0c6a, "Write Best Effort Flush Timeout"     },
+       { 0x0c6b, "Short Range Mode"                    },
+       { 0x0c6c, "Read LE Host Supported"              },
+       { 0x0c6d, "Write LE Host Supported"             },
+
+       /* OGF 4 - Information Parameter */
+       { 0x1001, "Read Local Version Information"      },
+       { 0x1002, "Read Local Supported Commands"       },
+       { 0x1003, "Read Local Supported Features"       },
+       { 0x1004, "Read Local Extended Features"        },
+       { 0x1005, "Read Buffer Size"                    },
+       /* reserved command */
+       { 0x1007, "Read Country Code"                   },
+       /* reserved command */
+       { 0x1009, "Read BD ADDR"                        },
+       { 0x100a, "Read Data Block Size"                },
+
+       /* OGF 5 - Status Parameter */
+       { 0x1401, "Read Failed Contact Counter"         },
+       { 0x1402, "Reset Failed Contact Counter"        },
+       { 0x1403, "Read Link Quality"                   },
+       /* reserved command */
+       { 0x1405, "Read RSSI"                           },
+       { 0x1406, "Read AFH Channel Map"                },
+       { 0x1407, "Read Clock"                          },
+       { 0x1408, "Read Encryption Key Size"            },
+       { 0x1409, "Read Local AMP Info"                 },
+       { 0x140a, "Read Local AMP ASSOC"                },
+       { 0x140b, "Write Remote AMP ASSOC"              },
+
+       /* OGF 8 - LE Control */
+       { 0x2001, "LE Set Event Mask"                   },
+       { 0x2002, "LE Read Buffer Size"                 },
+       { 0x2003, "LE Read Local Supported Features"    },
+       /* reserved command */
+       { 0x2005, "LE Set Random Address"               },
+       { 0x2006, "LE Set Advertising Parameters"       },
+       { 0x2007, "LE Read Advertising Channel TX Power"},
+       { 0x2008, "LE Set Advertising Data"             },
+       { 0x2009, "LE Set Scan Response Data"           },
+       { 0x200a, "LE Set Advertise Enable"             },
+       { 0x200b, "LE Set Scan Parameters"              },
+       { 0x200c, "LE Set Scan Enable"                  },
+       { 0x200d, "LE Create Connection"                },
+       { 0x200e, "LE Create Connection Cancel"         },
+       { 0x200f, "LE Read White List Size"             },
+       { 0x2010, "LE Clear White List"                 },
+       { 0x2011, "LE Add Device To White List"         },
+       { 0x2012, "LE Remove Device From White List"    },
+       { 0x2013, "LE Connection Update"                },
+       { 0x2014, "LE Set Host Channel Classification"  },
+       { 0x2015, "LE Read Channel Map"                 },
+       { 0x2016, "LE Read Remote Used Features"        },
+       { 0x2017, "LE Encrypt"                          },
+       { 0x2018, "LE Rand"                             },
+       { 0x2019, "LE Start Encryption"                 },
+       { 0x201a, "LE Long Term Key Request Reply"      },
+       { 0x201b, "LE Long Term Key Request Neg Reply"  },
+       { 0x201c, "LE Read Supported States"            },
+       { 0x201d, "LE Receiver Test"                    },
+       { 0x201e, "LE Transmitter Test"                 },
+       { 0x201f, "LE Test End"                         },
+       { }
+};
+
+static const char *opcode2str(uint16_t opcode)
+{
+       int i;
+
+       for (i = 0; opcode2str_table[i].str; i++) {
+               if (opcode2str_table[i].opcode == opcode)
+                       return opcode2str_table[i].str;
+       }
+
+       return "Unknown";
+}
+
+static const struct {
+       uint8_t event;
+       const char *str;
+} event2str_table[] = {
+       { 0x01, "Inquiry Complete"                      },
+       { 0x02, "Inquiry Result"                        },
+       { 0x03, "Connect Complete"                      },
+       { 0x04, "Connect Request"                       },
+       { 0x05, "Disconn Complete"                      },
+       { 0x06, "Auth Complete"                         },
+       { 0x07, "Remote Name Req Complete"              },
+       { 0x08, "Encrypt Change"                        },
+       { 0x09, "Change Connection Link Key Complete"   },
+       { 0x0a, "Master Link Key Complete"              },
+       { 0x0b, "Read Remote Supported Features"        },
+       { 0x0c, "Read Remote Version Complete"          },
+       { 0x0d, "QoS Setup Complete"                    },
+       { 0x0e, "Command Complete"                      },
+       { 0x0f, "Command Status"                        },
+       { 0x10, "Hardware Error"                        },
+       { 0x11, "Flush Occurred"                        },
+       { 0x12, "Role Change"                           },
+       { 0x13, "Number of Completed Packets"           },
+       { 0x14, "Mode Change"                           },
+       { 0x15, "Return Link Keys"                      },
+       { 0x16, "PIN Code Request"                      },
+       { 0x17, "Link Key Request"                      },
+       { 0x18, "Link Key Notification"                 },
+       { 0x19, "Loopback Command"                      },
+       { 0x1a, "Data Buffer Overflow"                  },
+       { 0x1b, "Max Slots Change"                      },
+       { 0x1c, "Read Clock Offset Complete"            },
+       { 0x1d, "Connection Packet Type Changed"        },
+       { 0x1e, "QoS Violation"                         },
+       { 0x1f, "Page Scan Mode Change"                 },
+       { 0x20, "Page Scan Repetition Mode Change"      },
+       { 0x21, "Flow Specification Complete"           },
+       { 0x22, "Inquiry Result with RSSI"              },
+       { 0x23, "Read Remote Extended Features"         },
+       /* reserved events */
+       { 0x2c, "Synchronous Connect Complete"          },
+       { 0x2d, "Synchronous Connect Changed"           },
+       { 0x2e, "Sniff Subrate"                         },
+       { 0x2f, "Extended Inquiry Result"               },
+       { 0x30, "Encryption Key Refresh Complete"       },
+       { 0x31, "IO Capability Request"                 },
+       { 0x32, "IO Capability Response"                },
+       { 0x33, "User Confirmation Request"             },
+       { 0x34, "User Passkey Request"                  },
+       { 0x35, "Remote OOB Data Request"               },
+       { 0x36, "Simple Pairing Complete"               },
+       /* reserved event */
+       { 0x38, "Link Supervision Timeout Change"       },
+       { 0x39, "Enhanced Flush Complete"               },
+       /* reserved event */
+       { 0x3b, "User Passkey Notification"             },
+       { 0x3c, "Keypress Notification"                 },
+       { 0x3d, "Remote Host Supported Features"        },
+       { 0x3e, "LE Meta Event"                         },
+       /* reserved event */
+       { 0x40, "Physical Link Complete"                },
+       { 0x41, "Channel Selected"                      },
+       { 0x42, "Disconn Physical Link Complete"        },
+       { 0x43, "Physical Link Loss Early Warning"      },
+       { 0x44, "Physical Link Recovery"                },
+       { 0x45, "Logical Link Complete"                 },
+       { 0x46, "Disconn Logical Link Complete"         },
+       { 0x47, "Flow Spec Modify Complete"             },
+       { 0x48, "Number Of Completed Data Blocks"       },
+       { 0x49, "AMP Start Test"                        },
+       { 0x4a, "AMP Test End"                          },
+       { 0x4b, "AMP Receiver Report"                   },
+       { 0x4c, "Short Range Mode Change Complete"      },
+       { 0x4d, "AMP Status Change"                     },
+       { 0xfe, "Testing"                               },
+       { 0xff, "Vendor"                                },
+       { }
+};
+
+static const char *event2str(uint8_t event)
+{
+       int i;
+
+       for (i = 0; event2str_table[i].str; i++) {
+               if (event2str_table[i].event == event)
+                       return event2str_table[i].str;
+       }
+
+       return "Unknown";
+}
+
+void packet_new_index(struct timeval *tv, uint16_t index, const char *label,
+                               uint8_t type, uint8_t bus, const char *name)
+{
+       print_header(tv, index);
+
+       printf("= New Index: %s (%s,%s,%s)\n", label,
+                               hci_typetostr(type), hci_bustostr(bus), name);
+}
+
+void packet_del_index(struct timeval *tv, uint16_t index, const char *label)
+{
+       print_header(tv, index);
+
+       printf("= Delete Index: %s\n", label);
+}
+
+void packet_hci_command(struct timeval *tv, uint16_t index,
+                                       const void *data, uint16_t size)
+{
+       const hci_command_hdr *hdr = data;
+       uint16_t opcode = btohs(hdr->opcode);
+       uint16_t ogf = cmd_opcode_ogf(opcode);
+       uint16_t ocf = cmd_opcode_ocf(opcode);
+
+       btsnoop_write(tv, index, 0x02, data, size);
+
+       print_header(tv, index);
+
+       if (size < HCI_COMMAND_HDR_SIZE) {
+               printf("* Malformed HCI Command packet\n");
+               return;
+       }
+
+       printf("< HCI Command: %s (0x%2.2x|0x%4.4x) plen %d\n",
+                               opcode2str(opcode), ogf, ocf, hdr->plen);
+
+       data += HCI_COMMAND_HDR_SIZE;
+       size -= HCI_COMMAND_HDR_SIZE;
+
+       packet_hexdump(data, size);
+}
+
+void packet_hci_event(struct timeval *tv, uint16_t index,
+                                       const void *data, uint16_t size)
+{
+       const hci_event_hdr *hdr = data;
+
+       btsnoop_write(tv, index, 0x03, data, size);
+
+       print_header(tv, index);
+
+       if (size < HCI_EVENT_HDR_SIZE) {
+               printf("* Malformed HCI Event packet\n");
+               return;
+       }
+
+       printf("> HCI Event: %s (0x%2.2x) plen %d\n",
+                               event2str(hdr->evt), hdr->evt, hdr->plen);
+
+       data += HCI_EVENT_HDR_SIZE;
+       size -= HCI_EVENT_HDR_SIZE;
+
+       packet_hexdump(data, size);
+}
+
+void packet_hci_acldata(struct timeval *tv, uint16_t index, bool in,
+                                       const void *data, uint16_t size)
+{
+       const hci_acl_hdr *hdr = data;
+       uint16_t handle = btohs(hdr->handle);
+       uint16_t dlen = btohs(hdr->dlen);
+       uint8_t flags = acl_flags(handle);
+
+       btsnoop_write(tv, index, in ? 0x01 : 0x00, data, size);
+
+       print_header(tv, index);
+
+       if (size < HCI_ACL_HDR_SIZE) {
+               printf("* Malformed ACL Data %s packet\n", in ? "RX" : "TX");
+               return;
+       }
+
+       printf("%c ACL Data: handle %d flags 0x%2.2x dlen %d\n",
+                       in ? '>' : '<', acl_handle(handle), flags, dlen);
+
+       data += HCI_ACL_HDR_SIZE;
+       size -= HCI_ACL_HDR_SIZE;
+
+       if (filter_mask & PACKET_FILTER_SHOW_ACL_DATA)
+               packet_hexdump(data, size);
+}
+
+void packet_hci_scodata(struct timeval *tv, uint16_t index, bool in,
+                                       const void *data, uint16_t size)
+{
+       const hci_sco_hdr *hdr = data;
+       uint16_t handle = btohs(hdr->handle);
+       uint8_t flags = acl_flags(handle);
+
+       print_header(tv, index);
+
+       if (size < HCI_SCO_HDR_SIZE) {
+               printf("* Malformed SCO Data %s packet\n", in ? "RX" : "TX");
+               return;
+       }
+
+       printf("%c SCO Data: handle %d flags 0x%2.2x dlen %d\n",
+                       in ? '>' : '<', acl_handle(handle), flags, hdr->dlen);
+
+       data += HCI_SCO_HDR_SIZE;
+       size -= HCI_SCO_HDR_SIZE;
+
+       if (filter_mask & PACKET_FILTER_SHOW_SCO_DATA)
+               packet_hexdump(data, size);
+}
diff --git a/monitor/packet.h b/monitor/packet.h
new file mode 100644 (file)
index 0000000..90fc7ec
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdbool.h>
+#include <sys/time.h>
+
+#define PACKET_FILTER_SHOW_INDEX       (1 << 0)
+#define PACKET_FILTER_SHOW_DATE                (1 << 1)
+#define PACKET_FILTER_SHOW_TIME                (1 << 2)
+#define PACKET_FILTER_SHOW_ACL_DATA    (1 << 3)
+#define PACKET_FILTER_SHOW_SCO_DATA    (1 << 4)
+
+void packet_set_filter(unsigned long filter);
+
+void packet_hexdump(const unsigned char *buf, uint16_t len);
+
+void packet_control(struct timeval *tv, uint16_t index, uint16_t opcode,
+                                       const void *data, uint16_t size);
+void packet_monitor(struct timeval *tv, uint16_t index, uint16_t opcode,
+                                       const void *data, uint16_t size);
+
+void packet_new_index(struct timeval *tv, uint16_t index, const char *label,
+                               uint8_t type, uint8_t bus, const char *name);
+void packet_del_index(struct timeval *tv, uint16_t index, const char *label);
+
+void packet_hci_command(struct timeval *tv, uint16_t index,
+                                       const void *data, uint16_t size);
+void packet_hci_event(struct timeval *tv, uint16_t index,
+                                       const void *data, uint16_t size);
+void packet_hci_acldata(struct timeval *tv, uint16_t index, bool in,
+                                       const void *data, uint16_t size);
+void packet_hci_scodata(struct timeval *tv, uint16_t index, bool in,
+                                       const void *data, uint16_t size);
diff --git a/network/common.c b/network/common.c
new file mode 100644 (file)
index 0000000..4704072
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "common.h"
+
+static int ctl;
+
+static struct {
+       const char      *name;          /* Friendly name */
+       const char      *uuid128;       /* UUID 128 */
+       uint16_t        id;             /* Service class identifier */
+} __svc[] = {
+       { "panu",       PANU_UUID,      BNEP_SVC_PANU   },
+       { "gn",         GN_UUID,        BNEP_SVC_GN     },
+       { "nap",        NAP_UUID,       BNEP_SVC_NAP    },
+       { NULL }
+};
+
+uint16_t bnep_service_id(const char *svc)
+{
+       int i;
+       uint16_t id;
+
+       /* Friendly service name */
+       for (i = 0; __svc[i].name; i++)
+               if (!strcasecmp(svc, __svc[i].name)) {
+                       return __svc[i].id;
+               }
+
+       /* UUID 128 string */
+       for (i = 0; __svc[i].uuid128; i++)
+               if (!strcasecmp(svc, __svc[i].uuid128)) {
+                       return __svc[i].id;
+               }
+
+       /* Try convert to HEX */
+       id = strtol(svc, NULL, 16);
+       if ((id < BNEP_SVC_PANU) || (id > BNEP_SVC_GN))
+               return 0;
+
+       return id;
+}
+
+const char *bnep_uuid(uint16_t id)
+{
+       int i;
+
+       for (i = 0; __svc[i].uuid128; i++)
+               if (__svc[i].id == id)
+                       return __svc[i].uuid128;
+       return NULL;
+}
+
+const char *bnep_name(uint16_t id)
+{
+       int i;
+
+       for (i = 0; __svc[i].name; i++)
+               if (__svc[i].id == id)
+                       return __svc[i].name;
+       return NULL;
+}
+
+int bnep_init(void)
+{
+       ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
+
+       if (ctl < 0) {
+               int err = -errno;
+               error("Failed to open control socket: %s (%d)",
+                                               strerror(-err), -err);
+               return err;
+       }
+
+       return 0;
+}
+
+int bnep_cleanup(void)
+{
+       close(ctl);
+       return 0;
+}
+
+int bnep_kill_connection(bdaddr_t *dst)
+{
+       struct bnep_conndel_req req;
+
+       memset(&req, 0, sizeof(req));
+       baswap((bdaddr_t *)&req.dst, dst);
+       req.flags = 0;
+       if (ioctl(ctl, BNEPCONNDEL, &req)) {
+               int err = -errno;
+               error("Failed to kill connection: %s (%d)",
+                                               strerror(-err), -err);
+               return err;
+       }
+       return 0;
+}
+
+int bnep_kill_all_connections(void)
+{
+       struct bnep_connlist_req req;
+       struct bnep_conninfo ci[7];
+       unsigned int i;
+       int err;
+
+       memset(&req, 0, sizeof(req));
+       req.cnum = 7;
+       req.ci   = ci;
+       if (ioctl(ctl, BNEPGETCONNLIST, &req)) {
+               err = -errno;
+               error("Failed to get connection list: %s (%d)",
+                                               strerror(-err), -err);
+               return err;
+       }
+
+       for (i = 0; i < req.cnum; i++) {
+               struct bnep_conndel_req del;
+
+               memset(&del, 0, sizeof(del));
+               memcpy(del.dst, ci[i].dst, ETH_ALEN);
+               del.flags = 0;
+               ioctl(ctl, BNEPCONNDEL, &del);
+       }
+       return 0;
+}
+
+int bnep_connadd(int sk, uint16_t role, char *dev)
+{
+       struct bnep_connadd_req req;
+
+       memset(&req, 0, sizeof(req));
+       strncpy(req.device, dev, 16);
+       req.device[15] = '\0';
+       req.sock = sk;
+       req.role = role;
+       if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
+               int err = -errno;
+               error("Failed to add device %s: %s(%d)",
+                               dev, strerror(-err), -err);
+               return err;
+       }
+
+       strncpy(dev, req.device, 16);
+       return 0;
+}
+
+int bnep_if_up(const char *devname)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(AF_INET, SOCK_DGRAM, 0);
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
+
+       ifr.ifr_flags |= IFF_UP;
+       ifr.ifr_flags |= IFF_MULTICAST;
+
+       err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+
+       close(sk);
+
+       if (err < 0) {
+               error("Could not bring up %s", devname);
+               return err;
+       }
+
+       return 0;
+}
+
+int bnep_if_down(const char *devname)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(AF_INET, SOCK_DGRAM, 0);
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
+
+       ifr.ifr_flags &= ~IFF_UP;
+
+       /* Bring down the interface */
+       err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+
+       close(sk);
+
+       if (err < 0) {
+               error("Could not bring down %s", devname);
+               return err;
+       }
+
+       return 0;
+}
+
+int bnep_add_to_bridge(const char *devname, const char *bridge)
+{
+       int ifindex = if_nametoindex(devname);
+       struct ifreq ifr;
+       int sk, err;
+
+       if (!devname || !bridge)
+               return -EINVAL;
+
+       sk = socket(AF_INET, SOCK_STREAM, 0);
+       if (sk < 0)
+               return -1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
+       ifr.ifr_ifindex = ifindex;
+
+       err = ioctl(sk, SIOCBRADDIF, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return err;
+
+       info("bridge %s: interface %s added", bridge, devname);
+
+       return 0;
+}
diff --git a/network/common.h b/network/common.h
new file mode 100644 (file)
index 0000000..cb1f08a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int bnep_init(void);
+int bnep_cleanup(void);
+
+uint16_t bnep_service_id(const char *svc);
+const char *bnep_uuid(uint16_t id);
+const char *bnep_name(uint16_t id);
+
+int bnep_kill_connection(bdaddr_t *dst);
+int bnep_kill_all_connections(void);
+
+int bnep_connadd(int sk, uint16_t role, char *dev);
+int bnep_if_up(const char *devname);
+int bnep_if_down(const char *devname);
+int bnep_add_to_bridge(const char *devname, const char *bridge);
diff --git a/network/connection.c b/network/connection.c
new file mode 100644 (file)
index 0000000..544ec3a
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "btio.h"
+#include "dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+
+#include "error.h"
+#include "common.h"
+#include "connection.h"
+
+#define NETWORK_PEER_INTERFACE "org.bluez.Network"
+#define CON_SETUP_RETRIES      3
+#define CON_SETUP_TO           9
+
+typedef enum {
+       CONNECTED,
+       CONNECTING,
+       DISCONNECTED
+} conn_state;
+
+struct network_peer {
+       bdaddr_t        src;
+       bdaddr_t        dst;
+       char            *path;          /* D-Bus path */
+       struct btd_device *device;
+       GSList          *connections;
+};
+
+struct network_conn {
+       DBusMessage     *msg;
+       char            dev[16];        /* Interface name */
+       uint16_t        id;             /* Role: Service Class Identifier */
+       conn_state      state;
+       GIOChannel      *io;
+       guint           watch;          /* Disconnect watch */
+       guint           dc_id;
+       struct network_peer *peer;
+       guint           attempt_cnt;
+       guint           timeout_source;
+};
+
+struct __service_16 {
+       uint16_t dst;
+       uint16_t src;
+} __attribute__ ((packed));
+
+static DBusConnection *connection = NULL;
+static GSList *peers = NULL;
+
+static struct network_peer *find_peer(GSList *list, const char *path)
+{
+       for (; list; list = list->next) {
+               struct network_peer *peer = list->data;
+
+               if (!strcmp(peer->path, path))
+                       return peer;
+       }
+
+       return NULL;
+}
+
+static struct network_conn *find_connection(GSList *list, uint16_t id)
+{
+       for (; list; list = list->next) {
+               struct network_conn *nc = list->data;
+
+               if (nc->id == id)
+                       return nc;
+       }
+
+       return NULL;
+}
+
+static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+                               gpointer data)
+{
+       struct network_conn *nc = data;
+
+       if (connection != NULL) {
+               gboolean connected = FALSE;
+               const char *property = "";
+               emit_property_changed(connection, nc->peer->path,
+                                       NETWORK_PEER_INTERFACE, "Connected",
+                                       DBUS_TYPE_BOOLEAN, &connected);
+               emit_property_changed(connection, nc->peer->path,
+                                       NETWORK_PEER_INTERFACE, "Interface",
+                                       DBUS_TYPE_STRING, &property);
+               emit_property_changed(connection, nc->peer->path,
+                                       NETWORK_PEER_INTERFACE, "UUID",
+                                       DBUS_TYPE_STRING, &property);
+               device_remove_disconnect_watch(nc->peer->device, nc->dc_id);
+               nc->dc_id = 0;
+               if (nc->watch) {
+                       g_dbus_remove_watch(connection, nc->watch);
+                       nc->watch = 0;
+               }
+       }
+
+       info("%s disconnected", nc->dev);
+
+       bnep_if_down(nc->dev);
+       nc->state = DISCONNECTED;
+       memset(nc->dev, 0, sizeof(nc->dev));
+       strcpy(nc->dev, "bnep%d");
+
+       return FALSE;
+}
+
+static void cancel_connection(struct network_conn *nc, const char *err_msg)
+{
+       DBusMessage *reply;
+
+       if (nc->timeout_source > 0) {
+               g_source_remove(nc->timeout_source);
+               nc->timeout_source = 0;
+       }
+
+       if (nc->watch) {
+               g_dbus_remove_watch(connection, nc->watch);
+               nc->watch = 0;
+       }
+
+       if (nc->msg && err_msg) {
+               reply = btd_error_failed(nc->msg, err_msg);
+               g_dbus_send_message(connection, reply);
+       }
+
+       g_io_channel_shutdown(nc->io, TRUE, NULL);
+       g_io_channel_unref(nc->io);
+       nc->io = NULL;
+
+       nc->state = DISCONNECTED;
+}
+
+static void connection_destroy(DBusConnection *conn, void *user_data)
+{
+       struct network_conn *nc = user_data;
+
+       if (nc->state == CONNECTED) {
+               bnep_if_down(nc->dev);
+               bnep_kill_connection(&nc->peer->dst);
+       } else if (nc->io)
+               cancel_connection(nc, NULL);
+}
+
+static void disconnect_cb(struct btd_device *device, gboolean removal,
+                               void *user_data)
+{
+       struct network_conn *nc = user_data;
+
+       info("Network: disconnect %s", nc->peer->path);
+
+       connection_destroy(NULL, user_data);
+}
+
+static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer data)
+{
+       struct network_conn *nc = data;
+       struct bnep_control_rsp *rsp;
+       struct timeval timeo;
+       char pkt[BNEP_MTU];
+       ssize_t r;
+       int sk;
+       const char *pdev, *uuid;
+       gboolean connected;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       g_source_remove(nc->timeout_source);
+       nc->timeout_source = 0;
+
+       if (cond & (G_IO_HUP | G_IO_ERR)) {
+               error("Hangup or error on l2cap server socket");
+               goto failed;
+       }
+
+       sk = g_io_channel_unix_get_fd(chan);
+
+       memset(pkt, 0, BNEP_MTU);
+       r = read(sk, pkt, sizeof(pkt) -1);
+       if (r < 0) {
+               error("IO Channel read error");
+               goto failed;
+       }
+
+       if (r == 0) {
+               error("No packet received on l2cap socket");
+               goto failed;
+       }
+
+       errno = EPROTO;
+
+       if ((size_t) r < sizeof(*rsp)) {
+               error("Packet received is not bnep type");
+               goto failed;
+       }
+
+       rsp = (void *) pkt;
+       if (rsp->type != BNEP_CONTROL) {
+               error("Packet received is not bnep type");
+               goto failed;
+       }
+
+       if (rsp->ctrl != BNEP_SETUP_CONN_RSP)
+               return TRUE;
+
+       r = ntohs(rsp->resp);
+
+       if (r != BNEP_SUCCESS) {
+               error("bnep failed");
+               goto failed;
+       }
+
+       memset(&timeo, 0, sizeof(timeo));
+       timeo.tv_sec = 0;
+
+       setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+
+       if (bnep_connadd(sk, BNEP_SVC_PANU, nc->dev)) {
+               error("%s could not be added", nc->dev);
+               goto failed;
+       }
+
+       bnep_if_up(nc->dev);
+       pdev = nc->dev;
+       uuid = bnep_uuid(nc->id);
+
+       g_dbus_send_reply(connection, nc->msg,
+                       DBUS_TYPE_STRING, &pdev,
+                       DBUS_TYPE_INVALID);
+
+       connected = TRUE;
+       emit_property_changed(connection, nc->peer->path,
+                               NETWORK_PEER_INTERFACE, "Connected",
+                               DBUS_TYPE_BOOLEAN, &connected);
+       emit_property_changed(connection, nc->peer->path,
+                               NETWORK_PEER_INTERFACE, "Interface",
+                               DBUS_TYPE_STRING, &pdev);
+       emit_property_changed(connection, nc->peer->path,
+                               NETWORK_PEER_INTERFACE, "UUID",
+                               DBUS_TYPE_STRING, &uuid);
+
+       nc->state = CONNECTED;
+       nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb,
+                                               nc, NULL);
+
+       info("%s connected", nc->dev);
+       /* Start watchdog */
+       g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                       (GIOFunc) bnep_watchdog_cb, nc);
+       g_io_channel_unref(nc->io);
+       nc->io = NULL;
+
+       return FALSE;
+
+failed:
+       cancel_connection(nc, "bnep setup failed");
+
+       return FALSE;
+}
+
+static int bnep_send_conn_req(struct network_conn *nc)
+{
+       struct bnep_setup_conn_req *req;
+       struct __service_16 *s;
+       unsigned char pkt[BNEP_MTU];
+       int fd;
+
+       /* Send request */
+       req = (void *) pkt;
+       req->type = BNEP_CONTROL;
+       req->ctrl = BNEP_SETUP_CONN_REQ;
+       req->uuid_size = 2;     /* 16bit UUID */
+       s = (void *) req->service;
+       s->dst = htons(nc->id);
+       s->src = htons(BNEP_SVC_PANU);
+
+       fd = g_io_channel_unix_get_fd(nc->io);
+       if (write(fd, pkt, sizeof(*req) + sizeof(*s)) < 0) {
+               int err = -errno;
+               error("bnep connection req send failed: %s", strerror(errno));
+               return err;
+       }
+
+       nc->attempt_cnt++;
+
+       return 0;
+}
+
+static gboolean bnep_conn_req_to(gpointer user_data)
+{
+       struct network_conn *nc;
+
+       nc = user_data;
+       if (nc->attempt_cnt == CON_SETUP_RETRIES) {
+               error("Too many bnep connection attempts");
+       } else {
+               error("bnep connection setup TO, retrying...");
+               if (!bnep_send_conn_req(nc))
+                       return TRUE;
+       }
+
+       cancel_connection(nc, "bnep setup failed");
+
+       return FALSE;
+}
+
+static int bnep_connect(struct network_conn *nc)
+{
+       int err;
+
+       nc->attempt_cnt = 0;
+       if ((err = bnep_send_conn_req(nc)))
+               return err;
+
+       nc->timeout_source = g_timeout_add_seconds(CON_SETUP_TO,
+                                                       bnep_conn_req_to, nc);
+
+       g_io_add_watch(nc->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                       (GIOFunc) bnep_setup_cb, nc);
+
+       return 0;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
+{
+       struct network_conn *nc = data;
+       const char *err_msg;
+       int perr;
+
+       if (err) {
+               error("%s", err->message);
+               err_msg = err->message;
+               goto failed;
+       }
+
+       perr = bnep_connect(nc);
+       if (perr < 0) {
+               err_msg = strerror(-perr);
+               error("bnep connect(): %s (%d)", err_msg, -perr);
+               goto failed;
+       }
+
+       return;
+
+failed:
+       cancel_connection(nc, err_msg);
+}
+
+/* Connect and initiate BNEP session */
+static DBusMessage *connection_connect(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct network_peer *peer = data;
+       struct network_conn *nc;
+       const char *svc;
+       uint16_t id;
+       GError *err = NULL;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &svc,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return NULL;
+
+       id = bnep_service_id(svc);
+       nc = find_connection(peer->connections, id);
+       if (!nc)
+               return btd_error_not_supported(msg);
+
+       if (nc->state != DISCONNECTED)
+               return btd_error_already_connected(msg);
+
+       nc->io = bt_io_connect(BT_IO_L2CAP, connect_cb, nc,
+                               NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &peer->src,
+                               BT_IO_OPT_DEST_BDADDR, &peer->dst,
+                               BT_IO_OPT_PSM, BNEP_PSM,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_OMTU, BNEP_MTU,
+                               BT_IO_OPT_IMTU, BNEP_MTU,
+                               BT_IO_OPT_INVALID);
+       if (!nc->io) {
+               DBusMessage *reply;
+               error("%s", err->message);
+               reply = btd_error_failed(msg, err->message);
+               g_error_free(err);
+               return reply;
+       }
+
+       nc->state = CONNECTING;
+       nc->msg = dbus_message_ref(msg);
+       nc->watch = g_dbus_add_disconnect_watch(conn,
+                                               dbus_message_get_sender(msg),
+                                               connection_destroy,
+                                               nc, NULL);
+
+       return NULL;
+}
+
+static DBusMessage *connection_cancel(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct network_conn *nc = data;
+       const char *owner = dbus_message_get_sender(nc->msg);
+       const char *caller = dbus_message_get_sender(msg);
+
+       if (!g_str_equal(owner, caller))
+               return btd_error_not_authorized(msg);
+
+       connection_destroy(conn, nc);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *connection_disconnect(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct network_peer *peer = data;
+       GSList *l;
+
+       for (l = peer->connections; l; l = l->next) {
+               struct network_conn *nc = l->data;
+
+               if (nc->state == DISCONNECTED)
+                       continue;
+
+               return connection_cancel(conn, msg, nc);
+       }
+
+       return btd_error_not_connected(msg);
+}
+
+static DBusMessage *connection_get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct network_peer *peer = data;
+       struct network_conn *nc = NULL;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       dbus_bool_t connected;
+       const char *property;
+       GSList *l;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       /* Connected */
+       for (l = peer->connections; l; l = l->next) {
+               struct network_conn *tmp = l->data;
+
+               if (tmp->state != CONNECTED)
+                       continue;
+
+               nc = tmp;
+               break;
+       }
+
+       connected = nc ? TRUE : FALSE;
+       dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+       /* Interface */
+       property = nc ? nc->dev : "";
+       dict_append_entry(&dict, "Interface", DBUS_TYPE_STRING, &property);
+
+       /* UUID */
+       property = nc ? bnep_uuid(nc->id) : "";
+       dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &property);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void connection_free(void *data)
+{
+       struct network_conn *nc = data;
+
+       if (nc->dc_id)
+               device_remove_disconnect_watch(nc->peer->device, nc->dc_id);
+
+       connection_destroy(connection, nc);
+
+       g_free(nc);
+       nc = NULL;
+}
+
+static void peer_free(struct network_peer *peer)
+{
+       g_slist_free_full(peer->connections, connection_free);
+       btd_device_unref(peer->device);
+       g_free(peer->path);
+       g_free(peer);
+}
+
+static void path_unregister(void *data)
+{
+       struct network_peer *peer = data;
+
+       DBG("Unregistered interface %s on path %s",
+               NETWORK_PEER_INTERFACE, peer->path);
+
+       peers = g_slist_remove(peers, peer);
+       peer_free(peer);
+}
+
+static const GDBusMethodTable connection_methods[] = {
+       { GDBUS_ASYNC_METHOD("Connect",
+                       NULL, NULL, connection_connect) },
+       { GDBUS_METHOD("Disconnect",
+                       NULL, NULL, connection_disconnect) },
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       connection_get_properties) },
+       { }
+};
+
+static const GDBusSignalTable connection_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+void connection_unregister(const char *path, uint16_t id)
+{
+       struct network_peer *peer;
+       struct network_conn *nc;
+
+       peer = find_peer(peers, path);
+       if (!peer)
+               return;
+
+       nc = find_connection(peer->connections, id);
+       if (!nc)
+               return;
+
+       peer->connections = g_slist_remove(peer->connections, nc);
+       connection_free(nc);
+       if (peer->connections)
+               return;
+
+       g_dbus_unregister_interface(connection, path, NETWORK_PEER_INTERFACE);
+}
+
+static struct network_peer *create_peer(struct btd_device *device,
+                                       const char *path, bdaddr_t *src,
+                                       bdaddr_t *dst)
+{
+       struct network_peer *peer;
+
+       peer = g_new0(struct network_peer, 1);
+       peer->device = btd_device_ref(device);
+       peer->path = g_strdup(path);
+       bacpy(&peer->src, src);
+       bacpy(&peer->dst, dst);
+
+       if (g_dbus_register_interface(connection, path,
+                                       NETWORK_PEER_INTERFACE,
+                                       connection_methods,
+                                       connection_signals, NULL,
+                                       peer, path_unregister) == FALSE) {
+               error("D-Bus failed to register %s interface",
+                       NETWORK_PEER_INTERFACE);
+               peer_free(peer);
+               return NULL;
+       }
+
+       DBG("Registered interface %s on path %s",
+               NETWORK_PEER_INTERFACE, path);
+
+       return peer;
+}
+
+int connection_register(struct btd_device *device, const char *path,
+                       bdaddr_t *src, bdaddr_t *dst, uint16_t id)
+{
+       struct network_peer *peer;
+       struct network_conn *nc;
+
+       if (!path)
+               return -EINVAL;
+
+       peer = find_peer(peers, path);
+       if (!peer) {
+               peer = create_peer(device, path, src, dst);
+               if (!peer)
+                       return -1;
+               peers = g_slist_append(peers, peer);
+       }
+
+       nc = find_connection(peer->connections, id);
+       if (nc)
+               return 0;
+
+       nc = g_new0(struct network_conn, 1);
+       nc->id = id;
+       memset(nc->dev, 0, sizeof(nc->dev));
+       strcpy(nc->dev, "bnep%d");
+       nc->state = DISCONNECTED;
+       nc->peer = peer;
+
+       peer->connections = g_slist_append(peer->connections, nc);
+
+       return 0;
+}
+
+int connection_init(DBusConnection *conn)
+{
+       connection = dbus_connection_ref(conn);
+
+       return 0;
+}
+
+void connection_exit(void)
+{
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
diff --git a/network/connection.h b/network/connection.h
new file mode 100644 (file)
index 0000000..5ea4147
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int connection_init(DBusConnection *conn);
+void connection_exit(void);
+int connection_register(struct btd_device *device, const char *path,
+                       bdaddr_t *src, bdaddr_t *dst, uint16_t id);
+void connection_unregister(const char *path, uint16_t id);
diff --git a/network/main.c b/network/main.c
new file mode 100644 (file)
index 0000000..88e77ee
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "manager.h"
+
+static DBusConnection *connection;
+
+static int network_init(void)
+{
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -EIO;
+
+       if (network_manager_init(connection) < 0) {
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void network_exit(void)
+{
+       network_manager_exit();
+
+       dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(network, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, network_init, network_exit)
diff --git a/network/manager.c b/network/manager.c
new file mode 100644 (file)
index 0000000..7fcd8f0
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "adapter.h"
+#include "device.h"
+#include "manager.h"
+#include "common.h"
+#include "connection.h"
+#include "server.h"
+
+static DBusConnection *connection = NULL;
+
+static gboolean conf_security = TRUE;
+
+static void read_config(const char *file)
+{
+       GKeyFile *keyfile;
+       GError *err = NULL;
+
+       keyfile = g_key_file_new();
+
+       if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+               g_clear_error(&err);
+               goto done;
+       }
+
+       conf_security = !g_key_file_get_boolean(keyfile, "General",
+                                               "DisableSecurity", &err);
+       if (err) {
+               DBG("%s: %s", file, err->message);
+               g_clear_error(&err);
+       }
+
+done:
+       g_key_file_free(keyfile);
+
+       DBG("Config options: Security=%s",
+                               conf_security ? "true" : "false");
+}
+
+static int network_probe(struct btd_device *device, GSList *uuids, uint16_t id)
+{
+       struct btd_adapter *adapter = device_get_adapter(device);
+       const gchar *path = device_get_path(device);
+       bdaddr_t src, dst;
+
+       DBG("path %s", path);
+
+       adapter_get_address(adapter, &src);
+       device_get_address(device, &dst, NULL);
+
+       return connection_register(device, path, &src, &dst, id);
+}
+
+static void network_remove(struct btd_device *device, uint16_t id)
+{
+       const gchar *path = device_get_path(device);
+
+       DBG("path %s", path);
+
+       connection_unregister(path, id);
+}
+
+static int panu_probe(struct btd_device *device, GSList *uuids)
+{
+       return network_probe(device, uuids, BNEP_SVC_PANU);
+}
+
+static void panu_remove(struct btd_device *device)
+{
+       network_remove(device, BNEP_SVC_PANU);
+}
+
+static int gn_probe(struct btd_device *device, GSList *uuids)
+{
+       return network_probe(device, uuids, BNEP_SVC_GN);
+}
+
+static void gn_remove(struct btd_device *device)
+{
+       network_remove(device, BNEP_SVC_GN);
+}
+
+static int nap_probe(struct btd_device *device, GSList *uuids)
+{
+       return network_probe(device, uuids, BNEP_SVC_NAP);
+}
+
+static void nap_remove(struct btd_device *device)
+{
+       network_remove(device, BNEP_SVC_NAP);
+}
+
+static int network_server_probe(struct btd_adapter *adapter)
+{
+       const gchar *path = adapter_get_path(adapter);
+
+       DBG("path %s", path);
+
+       return server_register(adapter);
+}
+
+static void network_server_remove(struct btd_adapter *adapter)
+{
+       const gchar *path = adapter_get_path(adapter);
+
+       DBG("path %s", path);
+
+       server_unregister(adapter);
+}
+
+static struct btd_device_driver network_panu_driver = {
+       .name   = "network-panu",
+       .uuids  = BTD_UUIDS(PANU_UUID),
+       .probe  = panu_probe,
+       .remove = panu_remove,
+};
+
+static struct btd_device_driver network_gn_driver = {
+       .name   = "network-gn",
+       .uuids  = BTD_UUIDS(GN_UUID),
+       .probe  = gn_probe,
+       .remove = gn_remove,
+};
+
+static struct btd_device_driver network_nap_driver = {
+       .name   = "network-nap",
+       .uuids  = BTD_UUIDS(NAP_UUID),
+       .probe  = nap_probe,
+       .remove = nap_remove,
+};
+
+static struct btd_adapter_driver network_server_driver = {
+       .name   = "network-server",
+       .probe  = network_server_probe,
+       .remove = network_server_remove,
+};
+
+int network_manager_init(DBusConnection *conn)
+{
+       read_config(CONFIGDIR "/network.conf");
+
+       if (bnep_init()) {
+               error("Can't init bnep module");
+               return -1;
+       }
+
+       /*
+        * There is one socket to handle the incoming connections. NAP,
+        * GN and PANU servers share the same PSM. The initial BNEP message
+        * (setup connection request) contains the destination service
+        * field that defines which service the source is connecting to.
+        */
+
+       if (server_init(conn, conf_security) < 0)
+               return -1;
+
+       /* Register network server if it doesn't exist */
+       btd_register_adapter_driver(&network_server_driver);
+
+       if (connection_init(conn) < 0)
+               return -1;
+
+       btd_register_device_driver(&network_panu_driver);
+       btd_register_device_driver(&network_gn_driver);
+       btd_register_device_driver(&network_nap_driver);
+
+       connection = dbus_connection_ref(conn);
+
+       return 0;
+}
+
+void network_manager_exit(void)
+{
+       server_exit();
+
+       btd_unregister_device_driver(&network_panu_driver);
+       btd_unregister_device_driver(&network_gn_driver);
+       btd_unregister_device_driver(&network_nap_driver);
+
+       connection_exit();
+
+       btd_unregister_adapter_driver(&network_server_driver);
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+
+       bnep_cleanup();
+}
diff --git a/network/manager.h b/network/manager.h
new file mode 100644 (file)
index 0000000..27bc13f
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int network_manager_init(DBusConnection *conn);
+void network_manager_exit(void);
diff --git a/network/network.conf b/network/network.conf
new file mode 100644 (file)
index 0000000..5f11639
--- /dev/null
@@ -0,0 +1,6 @@
+# Configuration file for the network service
+
+[General]
+
+# Disable link encryption: default=false
+#DisableSecurity=true
diff --git a/network/server.c b/network/server.c
new file mode 100644 (file)
index 0000000..480c7e2
--- /dev/null
@@ -0,0 +1,792 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/dbus-common.h"
+#include "../src/adapter.h"
+
+#include "log.h"
+#include "error.h"
+#include "sdpd.h"
+#include "btio.h"
+
+#include "common.h"
+#include "server.h"
+
+#define NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer"
+#define SETUP_TIMEOUT          1
+
+/* Pending Authorization */
+struct network_session {
+       bdaddr_t        dst;            /* Remote Bluetooth Address */
+       GIOChannel      *io;            /* Pending connect channel */
+       guint           watch;          /* BNEP socket watch */
+};
+
+struct network_adapter {
+       struct btd_adapter *adapter;    /* Adapter pointer */
+       GIOChannel      *io;            /* Bnep socket */
+       struct network_session *setup;  /* Setup in progress */
+       GSList          *servers;       /* Server register to adapter */
+};
+
+/* Main server structure */
+struct network_server {
+       bdaddr_t        src;            /* Bluetooth Local Address */
+       char            *iface;         /* DBus interface */
+       char            *name;          /* Server service name */
+       char            *bridge;        /* Bridge name */
+       uint32_t        record_id;      /* Service record id */
+       uint16_t        id;             /* Service class identifier */
+       GSList          *sessions;      /* Active connections */
+       struct network_adapter *na;     /* Adapter reference */
+       guint           watch_id;       /* Client service watch */
+};
+
+static DBusConnection *connection = NULL;
+static GSList *adapters = NULL;
+static gboolean security = TRUE;
+
+static struct network_adapter *find_adapter(GSList *list,
+                                       struct btd_adapter *adapter)
+{
+       for (; list; list = list->next) {
+               struct network_adapter *na = list->data;
+
+               if (na->adapter == adapter)
+                       return na;
+       }
+
+       return NULL;
+}
+
+static struct network_server *find_server(GSList *list, uint16_t id)
+{
+       for (; list; list = list->next) {
+               struct network_server *ns = list->data;
+
+               if (ns->id == id)
+                       return ns;
+       }
+
+       return NULL;
+}
+
+static sdp_record_t *server_record_new(const char *name, uint16_t id)
+{
+       sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+       uuid_t root_uuid, pan, l2cap, bnep;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *proto[2];
+       sdp_data_t *v, *p;
+       uint16_t psm = BNEP_PSM, version = 0x0100;
+       uint16_t security_desc = (security ? 0x0001 : 0x0000);
+       uint16_t net_access_type = 0xfffe;
+       uint32_t max_net_access_rate = 0;
+       const char *desc = "Network service";
+       sdp_record_t *record;
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       record->attrlist = NULL;
+       record->pattern = NULL;
+
+       switch (id) {
+       case BNEP_SVC_NAP:
+               sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
+               svclass = sdp_list_append(NULL, &pan);
+               sdp_set_service_classes(record, svclass);
+
+               sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+               profile[0].version = 0x0100;
+               pfseq = sdp_list_append(NULL, &profile[0]);
+               sdp_set_profile_descs(record, pfseq);
+
+               sdp_set_info_attr(record, name, NULL, desc);
+
+               sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE,
+                                       SDP_UINT16, &net_access_type);
+               sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+                                       SDP_UINT32, &max_net_access_rate);
+               break;
+       case BNEP_SVC_GN:
+               sdp_uuid16_create(&pan, GN_SVCLASS_ID);
+               svclass = sdp_list_append(NULL, &pan);
+               sdp_set_service_classes(record, svclass);
+
+               sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+               profile[0].version = 0x0100;
+               pfseq = sdp_list_append(NULL, &profile[0]);
+               sdp_set_profile_descs(record, pfseq);
+
+               sdp_set_info_attr(record, name, NULL, desc);
+               break;
+       case BNEP_SVC_PANU:
+               sdp_uuid16_create(&pan, PANU_SVCLASS_ID);
+               svclass = sdp_list_append(NULL, &pan);
+               sdp_set_service_classes(record, svclass);
+
+               sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+               profile[0].version = 0x0100;
+               pfseq = sdp_list_append(NULL, &profile[0]);
+               sdp_set_profile_descs(record, pfseq);
+
+               sdp_set_info_attr(record, name, NULL, desc);
+               break;
+       default:
+               sdp_record_free(record);
+               return NULL;
+       }
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(record, root);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(NULL, &l2cap);
+       p = sdp_data_alloc(SDP_UINT16, &psm);
+       proto[0] = sdp_list_append(proto[0], p);
+       apseq    = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&bnep, BNEP_UUID);
+       proto[1] = sdp_list_append(NULL, &bnep);
+       v = sdp_data_alloc(SDP_UINT16, &version);
+       proto[1] = sdp_list_append(proto[1], v);
+
+       /* Supported protocols */
+       {
+               uint16_t ptype[] = {
+                       0x0800,  /* IPv4 */
+                       0x0806,  /* ARP */
+               };
+               sdp_data_t *head, *pseq;
+               int p;
+
+               for (p = 0, head = NULL; p < 2; p++) {
+                       sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+                       if (head)
+                               sdp_seq_append(head, data);
+                       else
+                               head = data;
+               }
+               pseq = sdp_data_alloc(SDP_SEQ16, head);
+               proto[1] = sdp_list_append(proto[1], pseq);
+       }
+
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_add_lang_attr(record);
+
+       sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC,
+                               SDP_UINT16, &security_desc);
+
+       sdp_data_free(p);
+       sdp_data_free(v);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(root, NULL);
+       sdp_list_free(aproto, NULL);
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(svclass, NULL);
+       sdp_list_free(pfseq, NULL);
+
+       return record;
+}
+
+static ssize_t send_bnep_ctrl_rsp(int sk, uint16_t val)
+{
+       struct bnep_control_rsp rsp;
+
+       rsp.type = BNEP_CONTROL;
+       rsp.ctrl = BNEP_SETUP_CONN_RSP;
+       rsp.resp = htons(val);
+
+       return send(sk, &rsp, sizeof(rsp), 0);
+}
+
+static int server_connadd(struct network_server *ns,
+                               struct network_session *session,
+                               uint16_t dst_role)
+{
+       char devname[16];
+       int err, nsk;
+
+       memset(devname, 0, sizeof(devname));
+       strcpy(devname, "bnep%d");
+
+       nsk = g_io_channel_unix_get_fd(session->io);
+       err = bnep_connadd(nsk, dst_role, devname);
+       if (err < 0)
+               return err;
+
+       info("Added new connection: %s", devname);
+
+       if (bnep_add_to_bridge(devname, ns->bridge) < 0) {
+               error("Can't add %s to the bridge %s: %s(%d)",
+                               devname, ns->bridge, strerror(errno), errno);
+               return -EPERM;
+       }
+
+       bnep_if_up(devname);
+
+       ns->sessions = g_slist_append(ns->sessions, session);
+
+       return 0;
+}
+
+static uint16_t bnep_setup_chk(uint16_t dst_role, uint16_t src_role)
+{
+       /* Allowed PAN Profile scenarios */
+       switch (dst_role) {
+       case BNEP_SVC_NAP:
+       case BNEP_SVC_GN:
+               if (src_role == BNEP_SVC_PANU)
+                       return 0;
+               return BNEP_CONN_INVALID_SRC;
+       case BNEP_SVC_PANU:
+               if (src_role == BNEP_SVC_PANU ||
+                               src_role == BNEP_SVC_GN ||
+                               src_role == BNEP_SVC_NAP)
+                       return 0;
+
+               return BNEP_CONN_INVALID_SRC;
+       }
+
+       return BNEP_CONN_INVALID_DST;
+}
+
+static uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req,
+                               uint16_t *dst_role, uint16_t *src_role)
+{
+       uint8_t *dest, *source;
+
+       dest = req->service;
+       source = req->service + req->uuid_size;
+
+       switch (req->uuid_size) {
+       case 2: /* UUID16 */
+               *dst_role = bt_get_be16(dest);
+               *src_role = bt_get_be16(source);
+               break;
+       case 4: /* UUID32 */
+       case 16: /* UUID128 */
+               *dst_role = bt_get_be32(dest);
+               *src_role = bt_get_be32(source);
+               break;
+       default:
+               return BNEP_CONN_INVALID_SVC;
+       }
+
+       return 0;
+}
+
+static void session_free(void *data)
+{
+       struct network_session *session = data;
+
+       if (session->watch)
+               g_source_remove(session->watch);
+
+       if (session->io)
+               g_io_channel_unref(session->io);
+
+       g_free(session);
+}
+
+static void setup_destroy(void *user_data)
+{
+       struct network_adapter *na = user_data;
+       struct network_session *setup = na->setup;
+
+       if (!setup)
+               return;
+
+       na->setup = NULL;
+
+       session_free(setup);
+}
+
+static gboolean bnep_setup(GIOChannel *chan,
+                       GIOCondition cond, gpointer user_data)
+{
+       struct network_adapter *na = user_data;
+       struct network_server *ns;
+       uint8_t packet[BNEP_MTU];
+       struct bnep_setup_conn_req *req = (void *) packet;
+       uint16_t src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED;
+       int n, sk;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       if (cond & (G_IO_ERR | G_IO_HUP)) {
+               error("Hangup or error on BNEP socket");
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(chan);
+
+       /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+       n = read(sk, packet, sizeof(packet));
+       if (n < 0) {
+               error("read(): %s(%d)", strerror(errno), errno);
+               return FALSE;
+       }
+
+       /* Highest known Control command ID
+        * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
+       if (req->type == BNEP_CONTROL &&
+                               req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+               uint8_t pkt[3];
+
+               pkt[0] = BNEP_CONTROL;
+               pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
+               pkt[2] = req->ctrl;
+
+               send(sk, pkt, sizeof(pkt), 0);
+
+               return FALSE;
+       }
+
+       if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
+               return FALSE;
+
+       rsp = bnep_setup_decode(req, &dst_role, &src_role);
+       if (rsp)
+               goto reply;
+
+       rsp = bnep_setup_chk(dst_role, src_role);
+       if (rsp)
+               goto reply;
+
+       ns = find_server(na->servers, dst_role);
+       if (!ns) {
+               error("Server unavailable: (0x%x)", dst_role);
+               goto reply;
+       }
+
+       if (!ns->record_id) {
+               error("Service record not available");
+               goto reply;
+       }
+
+       if (!ns->bridge) {
+               error("Bridge interface not configured");
+               goto reply;
+       }
+
+       if (server_connadd(ns, na->setup, dst_role) < 0)
+               goto reply;
+
+       na->setup = NULL;
+
+       rsp = BNEP_SUCCESS;
+
+reply:
+       send_bnep_ctrl_rsp(sk, rsp);
+
+       return FALSE;
+}
+
+static void connect_event(GIOChannel *chan, GError *err, gpointer user_data)
+{
+       struct network_adapter *na = user_data;
+
+       if (err) {
+               error("%s", err->message);
+               setup_destroy(na);
+               return;
+       }
+
+       g_io_channel_set_close_on_unref(chan, TRUE);
+
+       na->setup->watch = g_io_add_watch_full(chan, G_PRIORITY_DEFAULT,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               bnep_setup, na, setup_destroy);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+       struct network_adapter *na = user_data;
+       GError *err = NULL;
+
+       if (derr) {
+               error("Access denied: %s", derr->message);
+               goto reject;
+       }
+
+       if (!bt_io_accept(na->setup->io, connect_event, na, NULL,
+                                                       &err)) {
+               error("bt_io_accept: %s", err->message);
+               g_error_free(err);
+               goto reject;
+       }
+
+       return;
+
+reject:
+       g_io_channel_shutdown(na->setup->io, TRUE, NULL);
+       setup_destroy(na);
+}
+
+static void confirm_event(GIOChannel *chan, gpointer user_data)
+{
+       struct network_adapter *na = user_data;
+       struct network_server *ns;
+       int perr;
+       bdaddr_t src, dst;
+       char address[18];
+       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;
+       }
+
+       DBG("BNEP: incoming connect from %s", address);
+
+       if (na->setup) {
+               error("Refusing connect from %s: setup in progress", address);
+               goto drop;
+       }
+
+       ns = find_server(na->servers, BNEP_SVC_NAP);
+       if (!ns)
+               goto drop;
+
+       if (!ns->record_id)
+               goto drop;
+
+       if (!ns->bridge)
+               goto drop;
+
+       na->setup = g_new0(struct network_session, 1);
+       bacpy(&na->setup->dst, &dst);
+       na->setup->io = g_io_channel_ref(chan);
+
+       perr = btd_request_authorization(&src, &dst, BNEP_SVC_UUID,
+                                       auth_cb, na);
+       if (perr < 0) {
+               error("Refusing connect from %s: %s (%d)", address,
+                               strerror(-perr), -perr);
+               setup_destroy(na);
+               goto drop;
+       }
+
+       return;
+
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+int server_init(DBusConnection *conn, gboolean secure)
+{
+       security = secure;
+       connection = dbus_connection_ref(conn);
+
+       return 0;
+}
+
+void server_exit(void)
+{
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
+
+static uint32_t register_server_record(struct network_server *ns)
+{
+       sdp_record_t *record;
+
+       record = server_record_new(ns->name, ns->id);
+       if (!record) {
+               error("Unable to allocate new service record");
+               return 0;
+       }
+
+       if (add_record_to_server(&ns->src, record) < 0) {
+               error("Failed to register service record");
+               sdp_record_free(record);
+               return 0;
+       }
+
+       DBG("got record id 0x%x", record->handle);
+
+       return record->handle;
+}
+
+static void server_disconnect(DBusConnection *conn, void *user_data)
+{
+       struct network_server *ns = user_data;
+
+       ns->watch_id = 0;
+
+       if (ns->record_id) {
+               remove_record_from_server(ns->record_id);
+               ns->record_id = 0;
+       }
+
+       g_free(ns->bridge);
+       ns->bridge = NULL;
+}
+
+static DBusMessage *register_server(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct network_server *ns = data;
+       DBusMessage *reply;
+       const char *uuid, *bridge;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &uuid,
+                               DBUS_TYPE_STRING, &bridge, DBUS_TYPE_INVALID))
+               return NULL;
+
+       if (g_strcmp0(uuid, "nap"))
+               return btd_error_failed(msg, "Invalid UUID");
+
+       if (ns->record_id)
+               return btd_error_already_exists(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       ns->record_id = register_server_record(ns);
+       if (!ns->record_id)
+               return btd_error_failed(msg, "SDP record registration failed");
+
+       g_free(ns->bridge);
+       ns->bridge = g_strdup(bridge);
+
+       ns->watch_id = g_dbus_add_disconnect_watch(conn,
+                                       dbus_message_get_sender(msg),
+                                       server_disconnect, ns, NULL);
+
+       return reply;
+}
+
+static DBusMessage *unregister_server(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct network_server *ns = data;
+       DBusMessage *reply;
+       const char *uuid;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &uuid,
+                                                       DBUS_TYPE_INVALID))
+               return NULL;
+
+       if (g_strcmp0(uuid, "nap"))
+               return btd_error_failed(msg, "Invalid UUID");
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       g_dbus_remove_watch(conn, ns->watch_id);
+
+       server_disconnect(conn, ns);
+
+       return reply;
+}
+
+static void adapter_free(struct network_adapter *na)
+{
+       if (na->io != NULL) {
+               g_io_channel_shutdown(na->io, TRUE, NULL);
+               g_io_channel_unref(na->io);
+       }
+
+       setup_destroy(na);
+       btd_adapter_unref(na->adapter);
+       g_free(na);
+}
+
+static void server_free(struct network_server *ns)
+{
+       if (!ns)
+               return;
+
+       /* FIXME: Missing release/free all bnepX interfaces */
+       if (ns->record_id)
+               remove_record_from_server(ns->record_id);
+
+       g_free(ns->iface);
+       g_free(ns->name);
+       g_free(ns->bridge);
+
+       g_slist_free_full(ns->sessions, session_free);
+
+       g_free(ns);
+}
+
+static void path_unregister(void *data)
+{
+       struct network_server *ns = data;
+       struct network_adapter *na = ns->na;
+
+       DBG("Unregistered interface %s on path %s",
+               ns->iface, adapter_get_path(na->adapter));
+
+       na->servers = g_slist_remove(na->servers, ns);
+       server_free(ns);
+
+       if (na->servers)
+               return;
+
+       adapters = g_slist_remove(adapters, na);
+       adapter_free(na);
+}
+
+static const GDBusMethodTable server_methods[] = {
+       { GDBUS_METHOD("Register",
+                       GDBUS_ARGS({ "uuid", "s" }, { "bridge", "s" }), NULL,
+                       register_server) },
+       { GDBUS_METHOD("Unregister",
+                       GDBUS_ARGS({ "uuid", "s" }), NULL,
+                       unregister_server) },
+       { }
+};
+
+static struct network_adapter *create_adapter(struct btd_adapter *adapter)
+{
+       struct network_adapter *na;
+       GError *err = NULL;
+       bdaddr_t src;
+
+       na = g_new0(struct network_adapter, 1);
+       na->adapter = btd_adapter_ref(adapter);
+
+       adapter_get_address(adapter, &src);
+
+       na->io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event, na,
+                               NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &src,
+                               BT_IO_OPT_PSM, BNEP_PSM,
+                               BT_IO_OPT_OMTU, BNEP_MTU,
+                               BT_IO_OPT_IMTU, BNEP_MTU,
+                               BT_IO_OPT_SEC_LEVEL,
+                               security ? BT_IO_SEC_MEDIUM : BT_IO_SEC_LOW,
+                               BT_IO_OPT_INVALID);
+       if (!na->io) {
+               error("%s", err->message);
+               g_error_free(err);
+               adapter_free(na);
+               return NULL;
+       }
+
+       return na;
+}
+
+int server_register(struct btd_adapter *adapter)
+{
+       struct network_adapter *na;
+       struct network_server *ns;
+       const char *path;
+
+       na = find_adapter(adapters, adapter);
+       if (!na) {
+               na = create_adapter(adapter);
+               if (!na)
+                       return -EINVAL;
+               adapters = g_slist_append(adapters, na);
+       }
+
+       ns = find_server(na->servers, BNEP_SVC_NAP);
+       if (ns)
+               return 0;
+
+       ns = g_new0(struct network_server, 1);
+
+       ns->iface = g_strdup(NETWORK_SERVER_INTERFACE);
+       ns->name = g_strdup("Network service");
+
+       path = adapter_get_path(adapter);
+
+       if (!g_dbus_register_interface(connection, path, ns->iface,
+                                       server_methods, NULL, NULL,
+                                       ns, path_unregister)) {
+               error("D-Bus failed to register %s interface",
+                               ns->iface);
+               server_free(ns);
+               return -1;
+       }
+
+       adapter_get_address(adapter, &ns->src);
+       ns->id = BNEP_SVC_NAP;
+       ns->na = na;
+       ns->record_id = 0;
+       na->servers = g_slist_append(na->servers, ns);
+
+       DBG("Registered interface %s on path %s", ns->iface, path);
+
+       return 0;
+}
+
+int server_unregister(struct btd_adapter *adapter)
+{
+       struct network_adapter *na;
+       struct network_server *ns;
+       uint16_t id = BNEP_SVC_NAP;
+
+       na = find_adapter(adapters, adapter);
+       if (!na)
+               return -EINVAL;
+
+       ns = find_server(na->servers, id);
+       if (!ns)
+               return -EINVAL;
+
+       g_dbus_unregister_interface(connection, adapter_get_path(adapter),
+                                       ns->iface);
+
+       return 0;
+}
diff --git a/network/server.h b/network/server.h
new file mode 100644 (file)
index 0000000..6351f6d
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int server_init(DBusConnection *conn, gboolean secure);
+void server_exit(void);
+int server_register(struct btd_adapter *adapter);
+int server_unregister(struct btd_adapter *adapter);
diff --git a/plugins/adaptername.c b/plugins/adaptername.c
new file mode 100644 (file)
index 0000000..d3341b5
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Red Hat, Inc.
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  Author: Bastien Nocera <hadess@hadess.net>
+ *  Marcel Holtmann <marcel@holtmann.org> (for expand_name)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <bluetooth/bluetooth.h>
+
+#include "plugin.h"
+#include "hcid.h" /* For main_opts */
+#include "adapter.h"
+#include "manager.h"
+#include "device.h" /* Needed for storage.h */
+#include "storage.h"
+#include "log.h"
+
+#include <sys/inotify.h>
+#define EVENT_SIZE  (sizeof (struct inotify_event))
+#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
+
+#define MACHINE_INFO_DIR "/etc/"
+#define MACHINE_INFO_FILE "machine-info"
+
+static GIOChannel *inotify = NULL;
+static int watch_d = -1;
+
+/* This file is part of systemd's hostnamed functionality:
+ * http://0pointer.de/public/systemd-man/machine-info.html
+ * http://www.freedesktop.org/wiki/Software/systemd/hostnamed
+ */
+static char *read_pretty_host_name(void)
+{
+       char *contents, *ret;
+       char **lines;
+       guint i;
+
+       if (g_file_get_contents(MACHINE_INFO_DIR MACHINE_INFO_FILE,
+                                       &contents, NULL, NULL) == FALSE)
+               return NULL;
+
+       lines = g_strsplit_set(contents, "\r\n", 0);
+       g_free(contents);
+
+       if (lines == NULL)
+               return NULL;
+
+       ret = NULL;
+       for (i = 0; lines[i] != NULL; i++) {
+               if (g_str_has_prefix(lines[i], "PRETTY_HOSTNAME=")) {
+                       ret = g_strdup(lines[i] + strlen("PRETTY_HOSTNAME="));
+                       break;
+               }
+       }
+
+       g_strfreev(lines);
+
+       return ret;
+}
+
+/*
+ * Device name expansion
+ *   %d - device id
+ *   %h - hostname
+ */
+static char *expand_name(char *dst, int size, char *str, int dev_id)
+{
+       register int sp, np, olen;
+       char *opt, buf[10];
+
+       if (!str || !dst)
+               return NULL;
+
+       sp = np = 0;
+       while (np < size - 1 && str[sp]) {
+               switch (str[sp]) {
+               case '%':
+                       opt = NULL;
+
+                       switch (str[sp+1]) {
+                       case 'd':
+                               sprintf(buf, "%d", dev_id);
+                               opt = buf;
+                               break;
+
+                       case 'h':
+                               opt = main_opts.host_name;
+                               break;
+
+                       case '%':
+                               dst[np++] = str[sp++];
+                               /* fall through */
+                       default:
+                               sp++;
+                               continue;
+                       }
+
+                       if (opt) {
+                               /* substitute */
+                               olen = strlen(opt);
+                               if (np + olen < size - 1)
+                                       memcpy(dst + np, opt, olen);
+                               np += olen;
+                       }
+                       sp += 2;
+                       continue;
+
+               case '\\':
+                       sp++;
+                       /* fall through */
+               default:
+                       dst[np++] = str[sp++];
+                       break;
+               }
+       }
+       dst[np] = '\0';
+       return dst;
+}
+
+static int get_default_adapter_id(void)
+{
+       struct btd_adapter *default_adapter;
+
+       default_adapter = manager_get_default_adapter();
+       if (default_adapter == NULL)
+               return -1;
+
+       return adapter_get_dev_id(default_adapter);
+}
+
+static void set_pretty_name(struct btd_adapter *adapter,
+                                               const char *pretty_hostname)
+{
+       int current_id;
+       int default_adapter;
+
+       default_adapter = get_default_adapter_id();
+       current_id = adapter_get_dev_id(adapter);
+
+       /* Allow us to change the name */
+       adapter_set_allow_name_changes(adapter, TRUE);
+
+       /* If it's the first device, let's assume it will be the
+        * default one, as we're not told when the default adapter
+        * changes */
+       if (default_adapter < 0)
+               default_adapter = current_id;
+
+       if (default_adapter != current_id) {
+               char *str;
+
+               /* +1 because we don't want an adapter called "Foobar's
+                * laptop #0" */
+               str = g_strdup_printf("%s #%d", pretty_hostname,
+                                                       current_id + 1);
+               DBG("Setting name '%s' for device 'hci%d'", str, current_id);
+
+               adapter_set_name(adapter, str);
+               g_free(str);
+       } else {
+               DBG("Setting name '%s' for device 'hci%d'", pretty_hostname,
+                                                               current_id);
+               adapter_set_name(adapter, pretty_hostname);
+       }
+
+       /* And disable the name change now */
+       adapter_set_allow_name_changes(adapter, FALSE);
+}
+
+static int adaptername_probe(struct btd_adapter *adapter)
+{
+       int current_id;
+       char name[MAX_NAME_LENGTH + 1];
+       char *pretty_hostname;
+       bdaddr_t bdaddr;
+
+       pretty_hostname = read_pretty_host_name();
+       if (pretty_hostname != NULL) {
+               set_pretty_name(adapter, pretty_hostname);
+               g_free(pretty_hostname);
+               return 0;
+       }
+
+       adapter_set_allow_name_changes(adapter, TRUE);
+       adapter_get_address(adapter, &bdaddr);
+       current_id = adapter_get_dev_id(adapter);
+
+       if (read_local_name(&bdaddr, name) < 0)
+               expand_name(name, MAX_NAME_LENGTH, main_opts.name, current_id);
+
+       DBG("Setting name '%s' for device 'hci%d'", name, current_id);
+       adapter_set_name(adapter, name);
+
+       return 0;
+}
+
+static gboolean handle_inotify_cb(GIOChannel *channel, GIOCondition cond,
+                                                               gpointer data)
+{
+       char buf[EVENT_BUF_LEN];
+       GIOStatus err;
+       gsize len, i;
+       gboolean changed;
+
+       changed = FALSE;
+
+       err = g_io_channel_read_chars(channel, buf, EVENT_BUF_LEN, &len, NULL);
+       if (err != G_IO_STATUS_NORMAL) {
+               error("Error reading inotify event: %d\n", err);
+               return FALSE;
+       }
+
+       i = 0;
+       while (i < len) {
+               struct inotify_event *pevent = (struct inotify_event *) &buf[i];
+
+               /* check that it's ours */
+               if (pevent->len && pevent->name != NULL &&
+                               strcmp(pevent->name, MACHINE_INFO_FILE) == 0)
+                       changed = TRUE;
+
+               i += EVENT_SIZE + pevent->len;
+       }
+
+       if (changed != FALSE) {
+               DBG(MACHINE_INFO_DIR MACHINE_INFO_FILE
+                               " changed, changing names for adapters");
+               manager_foreach_adapter((adapter_cb) adaptername_probe, NULL);
+       }
+
+       return TRUE;
+}
+
+static void adaptername_remove(struct btd_adapter *adapter)
+{
+}
+
+static struct btd_adapter_driver adaptername_driver = {
+       .name   = "adaptername",
+       .probe  = adaptername_probe,
+       .remove = adaptername_remove,
+};
+
+static int adaptername_init(void)
+{
+       int err;
+       int inot_fd;
+       guint32 mask;
+
+       err = btd_register_adapter_driver(&adaptername_driver);
+       if (err < 0)
+               return err;
+
+       inot_fd = inotify_init();
+       if (inot_fd < 0) {
+               error("Failed to setup inotify");
+               return 0;
+       }
+
+       mask = IN_CLOSE_WRITE;
+       mask |= IN_DELETE;
+       mask |= IN_CREATE;
+       mask |= IN_MOVED_FROM;
+       mask |= IN_MOVED_TO;
+
+       watch_d = inotify_add_watch(inot_fd, MACHINE_INFO_DIR, mask);
+       if (watch_d < 0) {
+               error("Failed to setup watch for '%s'", MACHINE_INFO_DIR);
+               close(inot_fd);
+               return 0;
+       }
+
+       inotify = g_io_channel_unix_new(inot_fd);
+       g_io_channel_set_close_on_unref(inotify, TRUE);
+       g_io_channel_set_encoding(inotify, NULL, NULL);
+       g_io_channel_set_flags(inotify, G_IO_FLAG_NONBLOCK, NULL);
+       g_io_add_watch(inotify, G_IO_IN, handle_inotify_cb, NULL);
+
+       return 0;
+}
+
+static void adaptername_exit(void)
+{
+       if (watch_d >= 0 && inotify != NULL) {
+               int inot_fd = g_io_channel_unix_get_fd(inotify);
+               inotify_rm_watch(inot_fd, watch_d);
+       }
+
+       if (inotify != NULL) {
+               g_io_channel_shutdown(inotify, FALSE, NULL);
+               g_io_channel_unref(inotify);
+       }
+
+       btd_unregister_adapter_driver(&adaptername_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(adaptername, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_LOW, adaptername_init, adaptername_exit)
diff --git a/plugins/dbusoob.c b/plugins/dbusoob.c
new file mode 100644 (file)
index 0000000..1791342
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  ST-Ericsson SA
+ *
+ *  Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "adapter.h"
+#include "device.h"
+#include "manager.h"
+#include "dbus-common.h"
+#include "event.h"
+#include "error.h"
+#include "oob.h"
+
+#define OOB_INTERFACE  "org.bluez.OutOfBand"
+
+struct oob_request {
+       struct btd_adapter *adapter;
+       DBusMessage *msg;
+};
+
+static GSList *oob_requests = NULL;
+static DBusConnection *connection = NULL;
+
+static gint oob_request_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct oob_request *data = a;
+       const struct btd_adapter *adapter = b;
+
+       return data->adapter != adapter;
+}
+
+static struct oob_request *find_oob_request(struct btd_adapter *adapter)
+{
+       GSList *match;
+
+       match = g_slist_find_custom(oob_requests, adapter, oob_request_cmp);
+
+       if (match)
+               return match->data;
+
+       return NULL;
+}
+
+static void read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+                               uint8_t *randomizer)
+{
+       struct DBusMessage *reply;
+       struct oob_request *oob_request;
+
+       oob_request = find_oob_request(adapter);
+       if (!oob_request)
+               return;
+
+       if (hash && randomizer)
+               reply = g_dbus_create_reply(oob_request->msg,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, 16,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, 16,
+                       DBUS_TYPE_INVALID);
+       else
+               reply = btd_error_failed(oob_request->msg,
+                                       "Failed to read local OOB data.");
+
+       oob_requests = g_slist_remove(oob_requests, oob_request);
+       dbus_message_unref(oob_request->msg);
+       g_free(oob_request);
+
+       if (!reply) {
+               error("Couldn't allocate D-Bus message");
+               return;
+       }
+
+       if (!g_dbus_send_message(connection, reply))
+               error("D-Bus send failed");
+}
+
+static DBusMessage *read_local_data(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct oob_request *oob_request;
+
+       if (find_oob_request(adapter))
+               return btd_error_in_progress(msg);
+
+       if (btd_adapter_read_local_oob_data(adapter))
+               return btd_error_failed(msg, "Request failed.");
+
+       oob_request = g_new(struct oob_request, 1);
+       oob_request->adapter = adapter;
+       oob_requests = g_slist_append(oob_requests, oob_request);
+       oob_request->msg = dbus_message_ref(msg);
+
+       return NULL;
+}
+
+static DBusMessage *add_remote_data(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct btd_adapter *adapter = data;
+       uint8_t *hash, *randomizer;
+       int32_t hlen, rlen;
+       const char *addr;
+       bdaddr_t bdaddr;
+
+       if (!dbus_message_get_args(msg, NULL,
+                       DBUS_TYPE_STRING, &addr,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, &hlen,
+                       DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, &rlen,
+                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (hlen != 16 || rlen != 16 || bachk(addr))
+               return btd_error_invalid_args(msg);
+
+       str2ba(addr, &bdaddr);
+
+       if (btd_adapter_add_remote_oob_data(adapter, &bdaddr, hash, randomizer))
+               return btd_error_failed(msg, "Request failed");
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *remove_remote_data(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct btd_adapter *adapter = data;
+       const char *addr;
+       bdaddr_t bdaddr;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       if (bachk(addr))
+               return btd_error_invalid_args(msg);
+
+       str2ba(addr, &bdaddr);
+
+       if (btd_adapter_remove_remote_oob_data(adapter, &bdaddr))
+               return btd_error_failed(msg, "Request failed");
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable oob_methods[] = {
+       { GDBUS_METHOD("AddRemoteData",
+                       GDBUS_ARGS({ "address", "s" }, { "hash", "ay" },
+                                       { "randomizer", "ay" }), NULL,
+                       add_remote_data) },
+       { GDBUS_METHOD("RemoveRemoteData",
+                       GDBUS_ARGS({ "address", "s" }), NULL,
+                       remove_remote_data) },
+       { GDBUS_ASYNC_METHOD("ReadLocalData",
+                       NULL, GDBUS_ARGS({ "hash", "ay" },
+                                               { "randomizer", "ay" }),
+                       read_local_data) },
+       { }
+};
+
+static int oob_probe(struct btd_adapter *adapter)
+{
+       const char *path = adapter_get_path(adapter);
+
+       if (!g_dbus_register_interface(connection, path, OOB_INTERFACE,
+                               oob_methods, NULL, NULL, adapter, NULL)) {
+                       error("OOB interface init failed on path %s", path);
+                       return -EIO;
+               }
+
+       return 0;
+}
+
+static void oob_remove(struct btd_adapter *adapter)
+{
+       read_local_data_complete(adapter, NULL, NULL);
+
+       g_dbus_unregister_interface(connection, adapter_get_path(adapter),
+                                                       OOB_INTERFACE);
+}
+
+static struct btd_adapter_driver oob_driver = {
+       .name   = "oob",
+       .probe  = oob_probe,
+       .remove = oob_remove,
+};
+
+static int dbusoob_init(void)
+{
+       DBG("Setup dbusoob plugin");
+
+       connection = get_dbus_connection();
+
+       oob_register_cb(read_local_data_complete);
+
+       return btd_register_adapter_driver(&oob_driver);
+}
+
+static void dbusoob_exit(void)
+{
+       DBG("Cleanup dbusoob plugin");
+
+       manager_foreach_adapter((adapter_cb) oob_remove, NULL);
+
+       btd_unregister_adapter_driver(&oob_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(dbusoob, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                                               dbusoob_init, dbusoob_exit)
diff --git a/plugins/external-dummy.c b/plugins/external-dummy.c
new file mode 100644 (file)
index 0000000..ff31290
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "plugin.h"
+#include "log.h"
+
+static int dummy_init(void)
+{
+       DBG("");
+
+       return 0;
+}
+
+static void dummy_exit(void)
+{
+       DBG("");
+}
+
+BLUETOOTH_PLUGIN_DEFINE(external_dummy, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_LOW, dummy_init, dummy_exit)
diff --git a/plugins/formfactor.c b/plugins/formfactor.c
new file mode 100644 (file)
index 0000000..0e19ac6
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "plugin.h"
+#include "adapter.h"
+#include "log.h"
+
+#define DMI_CHASSIS_FILE "/sys/class/dmi/id/chassis_type"
+#define DMI_CHASSIS_FILE_FALLBACK "/sys/devices/virtual/dmi/id/chassis_type"
+
+/* Map the chassis type from chassis_type to a sensible type used in hal
+ *
+ * See also 3.3.4.1 of the "System Management BIOS Reference Specification,
+ * Version 2.6.1" (Preliminary Standard) document, available from
+ * http://www.dmtf.org/standards/smbios.
+ *
+ * TODO: figure out WTF the mapping should be; "Lunch Box"? Give me a break :-)
+ *
+ * Copied from hal/hald/linux/osspec.c
+ */
+static const char *chassis_map[] = {
+       "Other",                 "unknown", /* 0x01 */
+       "Unknown",               "unknown",
+       "Desktop",               "desktop",
+       "Low Profile Desktop",   "desktop",
+       "Pizza Box",             "server",
+       "Mini Tower",            "desktop",
+       "Tower",                 "desktop",
+       "Portable",              "laptop",
+       "Laptop",                "laptop",
+       "Notebook",              "laptop",
+       "Hand Held",             "handheld",
+       "Docking Station",       "laptop",
+       "All In One",            "unknown",
+       "Sub Notebook",          "laptop",
+       "Space-saving",          "desktop",
+       "Lunch Box",             "unknown",
+       "Main Server Chassis",   "server",
+       "Expansion Chassis",     "unknown",
+       "Sub Chassis",           "unknown",
+       "Bus Expansion Chassis", "unknown",
+       "Peripheral Chassis",    "unknown",
+       "RAID Chassis",          "unknown",
+       "Rack Mount Chassis",    "unknown",
+       "Sealed-case PC",        "unknown",
+       "Multi-system",          "unknown",
+       "CompactPCI",            "unknown",
+       "AdvancedTCA",           "unknown",
+       "Blade",                 "server",
+       "Blade Enclosure",       "unknown", /* 0x1D */
+       NULL
+};
+
+static int formfactor_probe(struct btd_adapter *adapter)
+{
+       int chassis_type;
+       uint8_t minor = 0;
+       const char *formfactor;
+       char *contents;
+
+       if (g_file_get_contents(DMI_CHASSIS_FILE,
+                               &contents, NULL, NULL) == FALSE) {
+               if (g_file_get_contents(DMI_CHASSIS_FILE_FALLBACK,
+                                       &contents, NULL, NULL) == FALSE) {
+                       error("Could not get the contents of DMI chassis type");
+                       return 0;
+               }
+       }
+
+       chassis_type = atoi(contents);
+       g_free (contents);
+
+       if (chassis_type > 0x1D || chassis_type <= 0) {
+               error ("Chassis type is not a known chassis type");
+               return 0;
+       }
+
+       formfactor = chassis_map[chassis_type * 2 - 1];
+       if (formfactor != NULL) {
+               if (g_str_equal(formfactor, "laptop") == TRUE)
+                       minor |= (1 << 2) | (1 << 3);
+               else if (g_str_equal(formfactor, "desktop") == TRUE)
+                       minor |= 1 << 2;
+               else if (g_str_equal(formfactor, "server") == TRUE)
+                       minor |= 1 << 3;
+               else if (g_str_equal(formfactor, "handheld") == TRUE)
+                       minor += 1 << 4;
+       }
+
+       /* Computer major class */
+       DBG("Setting 0x%06x for major/minor device class", (1 << 8) | minor);
+
+       btd_adapter_set_class(adapter, 0x01, minor);
+
+       return 0;
+}
+
+static void formfactor_remove(struct btd_adapter *adapter)
+{
+}
+
+static struct btd_adapter_driver formfactor_driver = {
+       .name   = "formfactor",
+       .probe  = formfactor_probe,
+       .remove = formfactor_remove,
+};
+
+static int formfactor_init(void)
+{
+       return btd_register_adapter_driver(&formfactor_driver);
+}
+
+static void formfactor_exit(void)
+{
+       btd_unregister_adapter_driver(&formfactor_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(formfactor, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_LOW, formfactor_init, formfactor_exit)
diff --git a/plugins/gatt-example.c b/plugins/gatt-example.c
new file mode 100644 (file)
index 0000000..cd2c481
--- /dev/null
@@ -0,0 +1,583 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <errno.h>
+#include <adapter.h>
+
+#include "plugin.h"
+#include "hcid.h"
+#include "log.h"
+#include "gattrib.h"
+#include "gatt-service.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
+#include "attrib-server.h"
+
+/* FIXME: Not defined by SIG? UUID128? */
+#define OPCODES_SUPPORTED_UUID          0xA001
+#define BATTERY_STATE_SVC_UUID         0xA002
+#define BATTERY_STATE_UUID             0xA003
+#define THERM_HUMIDITY_SVC_UUID                0xA004
+#define MANUFACTURER_SVC_UUID          0xA005
+#define TEMPERATURE_UUID               0xA006
+#define FMT_CELSIUS_UUID               0xA007
+#define FMT_OUTSIDE_UUID               0xA008
+#define RELATIVE_HUMIDITY_UUID         0xA009
+#define FMT_PERCENT_UUID               0xA00A
+#define BLUETOOTH_SIG_UUID             0xA00B
+#define MANUFACTURER_NAME_UUID         0xA00C
+#define MANUFACTURER_SERIAL_UUID       0xA00D
+#define VENDOR_SPECIFIC_SVC_UUID       0xA00E
+#define VENDOR_SPECIFIC_TYPE_UUID      0xA00F
+#define FMT_KILOGRAM_UUID              0xA010
+#define FMT_HANGING_UUID               0xA011
+
+struct gatt_example_adapter {
+       struct btd_adapter      *adapter;
+       GSList                  *sdp_handles;
+};
+
+static GSList *adapters = NULL;
+
+static void gatt_example_adapter_free(struct gatt_example_adapter *gadapter)
+{
+       while (gadapter->sdp_handles != NULL) {
+               uint32_t handle = GPOINTER_TO_UINT(gadapter->sdp_handles->data);
+
+               attrib_free_sdp(handle);
+               gadapter->sdp_handles = g_slist_remove(gadapter->sdp_handles,
+                                               gadapter->sdp_handles->data);
+       }
+
+       if (gadapter->adapter != NULL)
+               btd_adapter_unref(gadapter->adapter);
+
+       g_free(gadapter);
+}
+
+static gint adapter_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct gatt_example_adapter *gatt_adapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (gatt_adapter->adapter == adapter)
+               return 0;
+
+       return -1;
+}
+
+static uint8_t battery_state_read(struct attribute *a,
+                                 struct btd_device *device, gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       uint8_t value;
+
+       value = 0x04;
+       attrib_db_update(adapter, a->handle, NULL, &value, sizeof(value), NULL);
+
+       return 0;
+}
+
+static gboolean register_battery_service(struct btd_adapter *adapter)
+{
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, BATTERY_STATE_SVC_UUID);
+
+       return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
+                       /* battery state characteristic */
+                       GATT_OPT_CHR_UUID, BATTERY_STATE_UUID,
+                       GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ |
+                                                       ATT_CHAR_PROPER_NOTIFY,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                                               battery_state_read, adapter,
+
+                       GATT_OPT_INVALID);
+}
+
+static void register_termometer_service(struct gatt_example_adapter *adapter,
+                       const uint16_t manuf1[2], const uint16_t manuf2[2])
+{
+       const char *desc_out_temp = "Outside Temperature";
+       const char *desc_out_hum = "Outside Relative Humidity";
+       uint16_t start_handle, h;
+       const int svc_size = 11;
+       uint32_t sdp_handle;
+       uint8_t atval[256];
+       bt_uuid_t uuid;
+       int len;
+
+       bt_uuid16_create(&uuid, THERM_HUMIDITY_SVC_UUID);
+       start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
+       if (start_handle == 0) {
+               error("Not enough free handles to register service");
+               return;
+       }
+
+       DBG("start_handle=0x%04x manuf1=0x%04x-0x%04x, manuf2=0x%04x-0x%04x",
+               start_handle, manuf1[0], manuf1[1], manuf2[0], manuf2[1]);
+
+       h = start_handle;
+
+       /* Thermometer: primary service definition */
+       bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+       att_put_u16(THERM_HUMIDITY_SVC_UUID, &atval[0]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 2);
+
+       bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+
+       /* Thermometer: Include */
+       if (manuf1[0] && manuf1[1]) {
+               att_put_u16(manuf1[0], &atval[0]);
+               att_put_u16(manuf1[1], &atval[2]);
+               att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]);
+               attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE,
+                                               ATT_NOT_PERMITTED, atval, 6);
+       }
+
+       /* Thermometer: Include */
+       if (manuf2[0] && manuf2[1]) {
+               att_put_u16(manuf2[0], &atval[0]);
+               att_put_u16(manuf2[1], &atval[2]);
+               att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[4]);
+               attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE,
+                                               ATT_NOT_PERMITTED, atval, 6);
+       }
+
+       /* Thermometer: temperature characteristic */
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ;
+       att_put_u16(h + 1, &atval[1]);
+       att_put_u16(TEMPERATURE_UUID, &atval[3]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 5);
+
+       /* Thermometer: temperature characteristic value */
+       bt_uuid16_create(&uuid, TEMPERATURE_UUID);
+       atval[0] = 0x8A;
+       atval[1] = 0x02;
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 2);
+
+       /* Thermometer: temperature characteristic format */
+       bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+       atval[0] = 0x0E;
+       atval[1] = 0xFE;
+       att_put_u16(FMT_CELSIUS_UUID, &atval[2]);
+       atval[4] = 0x01;
+       att_put_u16(FMT_OUTSIDE_UUID, &atval[5]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 7);
+
+       /* Thermometer: characteristic user description */
+       bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+       len = strlen(desc_out_temp);
+       strncpy((char *) atval, desc_out_temp, len);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, len);
+
+       /* Thermometer: relative humidity characteristic */
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ;
+       att_put_u16(h + 1, &atval[1]);
+       att_put_u16(RELATIVE_HUMIDITY_UUID, &atval[3]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 5);
+
+       /* Thermometer: relative humidity value */
+       bt_uuid16_create(&uuid, RELATIVE_HUMIDITY_UUID);
+       atval[0] = 0x27;
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 1);
+
+       /* Thermometer: relative humidity characteristic format */
+       bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+       atval[0] = 0x04;
+       atval[1] = 0x00;
+       att_put_u16(FMT_PERCENT_UUID, &atval[2]);
+       att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]);
+       att_put_u16(FMT_OUTSIDE_UUID, &atval[6]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 8);
+
+       /* Thermometer: characteristic user description */
+       bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+       len = strlen(desc_out_hum);
+       strncpy((char *) atval, desc_out_hum, len);
+       attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, len);
+
+       g_assert(h - start_handle + 1 == svc_size);
+
+       /* Add an SDP record for the above service */
+       sdp_handle = attrib_create_sdp(adapter->adapter, start_handle,
+                                                               "Thermometer");
+       if (sdp_handle)
+               adapter->sdp_handles = g_slist_prepend(adapter->sdp_handles,
+                                               GUINT_TO_POINTER(sdp_handle));
+}
+
+static void register_manuf1_service(struct gatt_example_adapter *adapter,
+                                                       uint16_t range[2])
+{
+       const char *manufacturer_name1 = "ACME Temperature Sensor";
+       const char *serial1 = "237495-3282-A";
+       uint16_t start_handle, h;
+       const int svc_size = 5;
+       uint8_t atval[256];
+       bt_uuid_t uuid;
+       int len;
+
+       bt_uuid16_create(&uuid, MANUFACTURER_SVC_UUID);
+       start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
+       if (start_handle == 0) {
+               error("Not enough free handles to register service");
+               return;
+       }
+
+       DBG("start_handle=0x%04x", start_handle);
+
+       h = start_handle;
+
+       /* Secondary Service: Manufacturer Service */
+       bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+       att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 2);
+
+       /* Manufacturer name characteristic definition */
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ;
+       att_put_u16(h + 1, &atval[1]);
+       att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 5);
+
+       /* Manufacturer name characteristic value */
+       bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
+       len = strlen(manufacturer_name1);
+       strncpy((char *) atval, manufacturer_name1, len);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, len);
+
+       /* Manufacturer serial number characteristic */
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ;
+       att_put_u16(h + 1, &atval[1]);
+       att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 5);
+
+       /* Manufacturer serial number characteristic value */
+       bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
+       len = strlen(serial1);
+       strncpy((char *) atval, serial1, len);
+       attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, len);
+
+       g_assert(h - start_handle + 1 == svc_size);
+
+       range[0] = start_handle;
+       range[1] = start_handle + svc_size - 1;
+}
+
+static void register_manuf2_service(struct gatt_example_adapter *adapter,
+                                                       uint16_t range[2])
+{
+       const char *manufacturer_name2 = "ACME Weighing Scales";
+       const char *serial2 = "11267-2327A00239";
+       uint16_t start_handle, h;
+       const int svc_size = 5;
+       uint8_t atval[256];
+       bt_uuid_t uuid;
+       int len;
+
+       bt_uuid16_create(&uuid, MANUFACTURER_SVC_UUID);
+       start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
+       if (start_handle == 0) {
+               error("Not enough free handles to register service");
+               return;
+       }
+
+       DBG("start_handle=0x%04x", start_handle);
+
+       h = start_handle;
+
+       /* Secondary Service: Manufacturer Service */
+       bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+       att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 2);
+
+       /* Manufacturer name characteristic definition */
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ;
+       att_put_u16(h + 1, &atval[1]);
+       att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 5);
+
+       /* Manufacturer name attribute */
+       bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
+       len = strlen(manufacturer_name2);
+       strncpy((char *) atval, manufacturer_name2, len);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, len);
+
+       /* Characteristic: serial number */
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ;
+       att_put_u16(h + 1, &atval[1]);
+       att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 5);
+
+       /* Serial number characteristic value */
+       bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
+       len = strlen(serial2);
+       strncpy((char *) atval, serial2, len);
+       attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, len);
+
+       g_assert(h - start_handle + 1 == svc_size);
+
+       range[0] = start_handle;
+       range[1] = start_handle + svc_size - 1;
+}
+
+static void register_vendor_service(struct gatt_example_adapter *adapter,
+                                                       uint16_t range[2])
+{
+       uint16_t start_handle, h;
+       const int svc_size = 3;
+       uint8_t atval[256];
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, VENDOR_SPECIFIC_SVC_UUID);
+       start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
+       if (start_handle == 0) {
+               error("Not enough free handles to register service");
+               return;
+       }
+
+       DBG("start_handle=0x%04x", start_handle);
+
+       h = start_handle;
+
+       /* Secondary Service: Vendor Specific Service */
+       bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+       att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 2);
+
+       /* Vendor Specific Type characteristic definition */
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ;
+       att_put_u16(h + 1, &atval[1]);
+       att_put_u16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 5);
+
+       /* Vendor Specific Type characteristic value */
+       bt_uuid16_create(&uuid, VENDOR_SPECIFIC_TYPE_UUID);
+       atval[0] = 0x56;
+       atval[1] = 0x65;
+       atval[2] = 0x6E;
+       atval[3] = 0x64;
+       atval[4] = 0x6F;
+       atval[5] = 0x72;
+       attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 6);
+
+       g_assert(h - start_handle + 1 == svc_size);
+
+       range[0] = start_handle;
+       range[1] = start_handle + svc_size - 1;
+}
+
+static void register_weight_service(struct gatt_example_adapter *adapter,
+                                               const uint16_t vendor[2])
+{
+       const char *desc_weight = "Rucksack Weight";
+       const uint128_t char_weight_uuid_btorder = {
+               .data = { 0x80, 0x88, 0xF2, 0x18, 0x90, 0x2C, 0x45, 0x0B,
+                         0xB6, 0xC4, 0x62, 0x89, 0x1E, 0x8C, 0x25, 0xE9 } };
+       const uint128_t prim_weight_uuid_btorder = {
+               .data = { 0x4F, 0x0A, 0xC0, 0x96, 0x35, 0xD4, 0x49, 0x11,
+                         0x96, 0x31, 0xDE, 0xA8, 0xDC, 0x74, 0xEE, 0xFE } };
+       uint128_t prim_weight_uuid, char_weight_uuid;
+       uint16_t start_handle, h;
+       const int svc_size = 6;
+       uint32_t sdp_handle;
+       uint8_t atval[256];
+       bt_uuid_t uuid;
+       int len;
+
+       btoh128(&char_weight_uuid_btorder, &char_weight_uuid);
+       btoh128(&prim_weight_uuid_btorder, &prim_weight_uuid);
+       bt_uuid128_create(&uuid, prim_weight_uuid);
+       start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
+       if (start_handle == 0) {
+               error("Not enough free handles to register service");
+               return;
+       }
+
+       DBG("start_handle=0x%04x, vendor=0x%04x-0x%04x", start_handle,
+                                                       vendor[0], vendor[1]);
+
+       h = start_handle;
+
+       /* Weight service: primary service definition */
+       bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+       memcpy(atval, &prim_weight_uuid_btorder, 16);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 16);
+
+       if (vendor[0] && vendor[1]) {
+               /* Weight: include */
+               bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+               att_put_u16(vendor[0], &atval[0]);
+               att_put_u16(vendor[1], &atval[2]);
+               att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]);
+               attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE,
+                                               ATT_NOT_PERMITTED, atval, 6);
+       }
+
+       /* Weight: characteristic */
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ;
+       att_put_u16(h + 1, &atval[1]);
+       memcpy(&atval[3], &char_weight_uuid_btorder, 16);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 19);
+
+       /* Weight: characteristic value */
+       bt_uuid128_create(&uuid, char_weight_uuid);
+       atval[0] = 0x82;
+       atval[1] = 0x55;
+       atval[2] = 0x00;
+       atval[3] = 0x00;
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 4);
+
+       /* Weight: characteristic format */
+       bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+       atval[0] = 0x08;
+       atval[1] = 0xFD;
+       att_put_u16(FMT_KILOGRAM_UUID, &atval[2]);
+       att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]);
+       att_put_u16(FMT_HANGING_UUID, &atval[6]);
+       attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 8);
+
+       /* Weight: characteristic user description */
+       bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+       len = strlen(desc_weight);
+       strncpy((char *) atval, desc_weight, len);
+       attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, len);
+       g_assert(h - start_handle + 1 == svc_size);
+
+       /* Add an SDP record for the above service */
+       sdp_handle = attrib_create_sdp(adapter->adapter, start_handle,
+                                                       "Weight Service");
+       if (sdp_handle)
+               adapter->sdp_handles = g_slist_prepend(adapter->sdp_handles,
+                                               GUINT_TO_POINTER(sdp_handle));
+}
+
+static int gatt_example_adapter_probe(struct btd_adapter *adapter)
+{
+       uint16_t manuf1_range[2] = {0, 0}, manuf2_range[2] = {0, 0};
+       uint16_t vendor_range[2] = {0, 0};
+       struct gatt_example_adapter *gadapter;
+
+       gadapter = g_new0(struct gatt_example_adapter, 1);
+       gadapter->adapter = btd_adapter_ref(adapter);
+
+       if (!register_battery_service(adapter)) {
+               DBG("Battery service could not be registered");
+               gatt_example_adapter_free(gadapter);
+               return -EIO;
+       }
+
+       register_manuf1_service(gadapter, manuf1_range);
+       register_manuf2_service(gadapter, manuf2_range);
+       register_termometer_service(gadapter, manuf1_range, manuf2_range);
+       register_vendor_service(gadapter, vendor_range);
+       register_weight_service(gadapter, vendor_range);
+
+       adapters = g_slist_append(adapters, gadapter);
+
+       return 0;
+}
+
+static void gatt_example_adapter_remove(struct btd_adapter *adapter)
+{
+       struct gatt_example_adapter *gadapter;
+       GSList *l;
+
+       l = g_slist_find_custom(adapters, adapter, adapter_cmp);
+       if (l == NULL)
+               return;
+
+       gadapter = l->data;
+       adapters = g_slist_remove(adapters, gadapter);
+       gatt_example_adapter_free(gadapter);
+}
+
+static struct btd_adapter_driver gatt_example_adapter_driver = {
+       .name   = "gatt-example-adapter-driver",
+       .probe  = gatt_example_adapter_probe,
+       .remove = gatt_example_adapter_remove,
+};
+
+static int gatt_example_init(void)
+{
+       if (!main_opts.gatt_enabled) {
+               DBG("GATT is disabled");
+               return -ENOTSUP;
+       }
+
+       return btd_register_adapter_driver(&gatt_example_adapter_driver);
+}
+
+static void gatt_example_exit(void)
+{
+       if (!main_opts.gatt_enabled)
+               return;
+
+       btd_unregister_adapter_driver(&gatt_example_adapter_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(gatt_example, VERSION, BLUETOOTH_PLUGIN_PRIORITY_LOW,
+                                       gatt_example_init, gatt_example_exit)
diff --git a/plugins/hal.c b/plugins/hal.c
new file mode 100644 (file)
index 0000000..21d7688
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <dbus/dbus.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "plugin.h"
+#include "adapter.h"
+#include "log.h"
+
+static void formfactor_reply(DBusPendingCall *call, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       const char *formfactor = NULL;
+       DBusMessage *reply;
+       uint8_t minor = 0;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       if (dbus_set_error_from_message(NULL, reply) == TRUE) {
+               error("Failed to access HAL");
+               dbus_message_unref(reply);
+               return;
+       }
+
+       if (dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &formfactor,
+                                               DBUS_TYPE_INVALID) == FALSE) {
+               error("Wrong formfactor arguments");
+               dbus_message_unref(reply);
+               return;
+       }
+
+       DBG("Computer is classified as %s", formfactor);
+
+       if (formfactor != NULL) {
+               if (g_str_equal(formfactor, "laptop") == TRUE)
+                       minor |= (1 << 2) | (1 << 3);
+               else if (g_str_equal(formfactor, "desktop") == TRUE)
+                       minor |= 1 << 2;
+               else if (g_str_equal(formfactor, "server") == TRUE)
+                       minor |= 1 << 3;
+               else if (g_str_equal(formfactor, "handheld") == TRUE)
+                       minor += 1 << 4;
+       }
+
+       dbus_message_unref(reply);
+
+       /* Computer major class */
+       DBG("Setting 0x%06x for major/minor device class", (1 << 8) | minor);
+
+       btd_adapter_set_class(adapter, 0x01, minor);
+}
+
+static DBusConnection *connection;
+
+static int hal_probe(struct btd_adapter *adapter)
+{
+       const char *property = "system.formfactor";
+       DBusMessage *message;
+       DBusPendingCall *call;
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -ENOMEM;
+
+       message = dbus_message_new_method_call("org.freedesktop.Hal",
+                               "/org/freedesktop/Hal/devices/computer",
+                                               "org.freedesktop.Hal.Device",
+                                                       "GetPropertyString");
+       if (message == NULL) {
+               error("Failed to create formfactor request");
+               dbus_connection_unref(connection);
+               return -ENOMEM;
+       }
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &property,
+                                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, message,
+                                               &call, -1) == FALSE) {
+               error("Failed to send formfactor request");
+               dbus_message_unref(message);
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(call, formfactor_reply, adapter, NULL);
+
+       dbus_pending_call_unref(call);
+
+       dbus_message_unref(message);
+
+       return 0;
+}
+
+static void hal_remove(struct btd_adapter *adapter)
+{
+       dbus_connection_unref(connection);
+}
+
+static struct btd_adapter_driver hal_driver = {
+       .name   = "hal",
+       .probe  = hal_probe,
+       .remove = hal_remove,
+};
+
+static int hal_init(void)
+{
+       return btd_register_adapter_driver(&hal_driver);
+}
+
+static void hal_exit(void)
+{
+       btd_unregister_adapter_driver(&hal_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(hal, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_LOW, hal_init, hal_exit)
diff --git a/plugins/hciops.c b/plugins/hciops.c
new file mode 100644 (file)
index 0000000..d74f2ea
--- /dev/null
@@ -0,0 +1,3943 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include "hcid.h"
+#include "sdpd.h"
+#include "btio.h"
+#include "adapter.h"
+#include "device.h"
+#include "plugin.h"
+#include "log.h"
+#include "storage.h"
+#include "event.h"
+#include "manager.h"
+#include "oob.h"
+#include "eir.h"
+
+#define DISCOV_HALTED 0
+#define DISCOV_INQ 1
+#define DISCOV_SCAN 2
+#define DISCOV_NAMES 3
+
+#define TIMEOUT_BR_LE_SCAN 5120 /* TGAP(100)/2 */
+#define TIMEOUT_LE_SCAN 10240 /* TGAP(gen_disc_scan_min) */
+
+#define LENGTH_BR_INQ 0x08
+#define LENGTH_BR_LE_INQ 0x04
+
+static int start_scanning(int index, int timeout);
+
+static int child_pipe[2] = { -1, -1 };
+
+static guint child_io_id = 0;
+static guint ctl_io_id = 0;
+
+enum adapter_type {
+       BR_EDR,
+       LE_ONLY,
+       BR_EDR_LE,
+       UNKNOWN,
+};
+
+/* Commands sent by kernel on starting an adapter */
+enum {
+       PENDING_BDADDR,
+       PENDING_VERSION,
+       PENDING_FEATURES,
+       PENDING_NAME,
+};
+
+struct bt_conn {
+       struct dev_info *dev;
+       bdaddr_t bdaddr;
+       uint16_t handle;
+       uint8_t loc_cap;
+       uint8_t loc_auth;
+       uint8_t rem_cap;
+       uint8_t rem_auth;
+       uint8_t rem_oob_data;
+       gboolean bonding_initiator;
+       gboolean secmode3;
+       GIOChannel *io; /* For raw L2CAP socket (bonding) */
+};
+
+struct oob_data {
+       bdaddr_t bdaddr;
+       uint8_t hash[16];
+       uint8_t randomizer[16];
+};
+
+enum name_state {
+       NAME_UNKNOWN,
+       NAME_NEEDED,
+       NAME_NOT_NEEDED,
+       NAME_PENDING,
+};
+
+struct found_dev {
+       bdaddr_t bdaddr;
+       int8_t rssi;
+       enum name_state name_state;
+};
+
+static int max_dev = -1;
+static struct dev_info {
+       int id;
+       int sk;
+       bdaddr_t bdaddr;
+       char name[249];
+       uint8_t eir[HCI_MAX_EIR_LENGTH];
+       uint8_t features[8];
+       uint8_t extfeatures[8];
+       uint8_t ssp_mode;
+
+       int8_t tx_power;
+
+       int discov_state;
+
+       uint32_t current_cod;
+       uint32_t wanted_cod;
+       uint32_t pending_cod;
+       gboolean cache_enable;
+       gboolean already_up;
+       gboolean registered;
+       gboolean pairable;
+
+       uint8_t io_capability;
+
+       struct hci_version ver;
+
+       uint16_t did_source;
+       uint16_t did_vendor;
+       uint16_t did_product;
+       uint16_t did_version;
+
+       gboolean up;
+       uint32_t pending;
+
+       GIOChannel *io;
+       guint watch_id;
+
+       gboolean debug_keys;
+       GSList *keys;
+       uint8_t pin_length;
+
+       GSList *oob_data;
+
+       GSList *uuids;
+
+       GSList *connections;
+
+       GSList *found_devs;
+       GSList *need_name;
+
+       guint stop_scan_id;
+
+       uint16_t discoverable_timeout;
+       guint discoverable_id;
+} *devs = NULL;
+
+static int found_dev_rssi_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct found_dev *d1 = a, *d2 = b;
+       int rssi1, rssi2;
+
+       if (d2->name_state == NAME_NOT_NEEDED)
+               return -1;
+
+       rssi1 = d1->rssi < 0 ? -d1->rssi : d1->rssi;
+       rssi2 = d2->rssi < 0 ? -d2->rssi : d2->rssi;
+
+       return rssi1 - rssi2;
+}
+
+static int found_dev_bda_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct found_dev *d1 = a, *d2 = b;
+
+       return bacmp(&d1->bdaddr, &d2->bdaddr);
+}
+
+static void found_dev_cleanup(struct dev_info *info)
+{
+       g_slist_free_full(info->found_devs, g_free);
+       info->found_devs = NULL;
+
+       g_slist_free_full(info->need_name, g_free);
+       info->need_name = NULL;
+}
+
+static int resolve_name(struct dev_info *info, bdaddr_t *bdaddr)
+{
+       remote_name_req_cp cp;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d dba %s", info->id, addr);
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+       cp.pscan_rep_mode = 0x02;
+
+       if (hci_send_cmd(info->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ,
+                                       REMOTE_NAME_REQ_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int resolve_names(struct dev_info *info, struct btd_adapter *adapter)
+{
+       struct found_dev *dev;
+
+       DBG("found_dev %u need_name %u", g_slist_length(info->found_devs),
+                                       g_slist_length(info->need_name));
+
+       if (g_slist_length(info->need_name) == 0)
+               return -ENOENT;
+
+       dev = info->need_name->data;
+       resolve_name(info, &dev->bdaddr);
+       dev->name_state = NAME_PENDING;
+
+       return 0;
+}
+
+static void set_state(int index, int state)
+{
+       struct btd_adapter *adapter;
+       struct dev_info *dev = &devs[index];
+
+       if (dev->discov_state == state)
+               return;
+
+       adapter = manager_find_adapter_by_id(index);
+       if (!adapter) {
+               error("No matching adapter found");
+               return;
+       }
+
+       dev->discov_state = state;
+
+       DBG("hci%d: new state %d", index, dev->discov_state);
+
+       switch (dev->discov_state) {
+       case DISCOV_HALTED:
+               found_dev_cleanup(dev);
+               adapter_set_discovering(adapter, FALSE);
+               break;
+       case DISCOV_INQ:
+       case DISCOV_SCAN:
+               adapter_set_discovering(adapter, TRUE);
+               break;
+       case DISCOV_NAMES:
+               if (resolve_names(dev, adapter) < 0)
+                       set_state(index, DISCOV_HALTED);
+               break;
+       }
+}
+
+static inline gboolean is_le_capable(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       return (dev->features[4] & LMP_LE &&
+                       dev->extfeatures[0] & LMP_HOST_LE) ? TRUE : FALSE;
+}
+
+static inline gboolean is_bredr_capable(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       return (dev->features[4] & LMP_NO_BREDR) == 0 ? TRUE : FALSE;
+}
+
+static int get_adapter_type(int index)
+{
+       if (is_le_capable(index) && is_bredr_capable(index))
+               return BR_EDR_LE;
+       else if (is_le_capable(index))
+               return LE_ONLY;
+       else if (is_bredr_capable(index))
+               return BR_EDR;
+
+       return UNKNOWN;
+}
+
+static int ignore_device(struct hci_dev_info *di)
+{
+       return hci_test_bit(HCI_RAW, &di->flags) || di->type >> 4 != HCI_BREDR;
+}
+
+static struct dev_info *init_dev_info(int index, int sk, gboolean registered,
+                                                       gboolean already_up)
+{
+       struct dev_info *dev = &devs[index];
+
+       memset(dev, 0, sizeof(*dev));
+
+       dev->id = index;
+       dev->sk = sk;
+       dev->cache_enable = TRUE;
+       dev->registered = registered;
+       dev->already_up = already_up;
+       dev->io_capability = 0x03; /* No Input No Output */
+       dev->discov_state = DISCOV_HALTED;
+
+       return dev;
+}
+
+/* Async HCI command handling with callback support */
+
+struct hci_cmd_data {
+       bt_hci_result_t         cb;
+       uint16_t                handle;
+       uint16_t                ocf;
+       gpointer                caller_data;
+};
+
+static gboolean hci_event_watch(GIOChannel *io,
+                       GIOCondition cond, gpointer user_data)
+{
+       unsigned char buf[HCI_MAX_EVENT_SIZE], *body;
+       struct hci_cmd_data *cmd = user_data;
+       evt_cmd_status *evt_status;
+       evt_auth_complete *evt_auth;
+       evt_encrypt_change *evt_enc;
+       hci_event_hdr *hdr;
+       set_conn_encrypt_cp cp;
+       int dd;
+       uint16_t ocf;
+       uint8_t status = HCI_OE_POWER_OFF;
+
+       if (cond & G_IO_NVAL) {
+               cmd->cb(status, cmd->caller_data);
+               return FALSE;
+       }
+
+       if (cond & (G_IO_ERR | G_IO_HUP))
+               goto failed;
+
+       dd = g_io_channel_unix_get_fd(io);
+
+       if (read(dd, buf, sizeof(buf)) < 0)
+               goto failed;
+
+       hdr = (hci_event_hdr *) (buf + 1);
+       body = buf + (1 + HCI_EVENT_HDR_SIZE);
+
+       switch (hdr->evt) {
+       case EVT_CMD_STATUS:
+               evt_status = (evt_cmd_status *) body;
+               ocf = cmd_opcode_ocf(evt_status->opcode);
+               if (ocf != cmd->ocf)
+                       return TRUE;
+               switch (ocf) {
+               case OCF_AUTH_REQUESTED:
+               case OCF_SET_CONN_ENCRYPT:
+                       if (evt_status->status != 0) {
+                               /* Baseband rejected command */
+                               status = evt_status->status;
+                               goto failed;
+                       }
+                       break;
+               default:
+                       return TRUE;
+               }
+               /* Wait for the next event */
+               return TRUE;
+       case EVT_AUTH_COMPLETE:
+               evt_auth = (evt_auth_complete *) body;
+               if (evt_auth->handle != cmd->handle) {
+                       /* Skipping */
+                       return TRUE;
+               }
+
+               if (evt_auth->status != 0x00) {
+                       status = evt_auth->status;
+                       /* Abort encryption */
+                       goto failed;
+               }
+
+               memset(&cp, 0, sizeof(cp));
+               cp.handle  = cmd->handle;
+               cp.encrypt = 1;
+
+               cmd->ocf = OCF_SET_CONN_ENCRYPT;
+
+               if (hci_send_cmd(dd, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT,
+                                       SET_CONN_ENCRYPT_CP_SIZE, &cp) < 0) {
+                       status = HCI_COMMAND_DISALLOWED;
+                       goto failed;
+               }
+               /* Wait for encrypt change event */
+               return TRUE;
+       case EVT_ENCRYPT_CHANGE:
+               evt_enc = (evt_encrypt_change *) body;
+               if (evt_enc->handle != cmd->handle)
+                       return TRUE;
+
+               /* Procedure finished: reporting status */
+               status = evt_enc->status;
+               break;
+       default:
+               /* Skipping */
+               return TRUE;
+       }
+
+failed:
+       cmd->cb(status, cmd->caller_data);
+       g_io_channel_shutdown(io, TRUE, NULL);
+
+       return FALSE;
+}
+
+static int write_inq_mode(int index, uint8_t mode)
+{
+       struct dev_info *dev = &devs[index];
+       write_inquiry_mode_cp cp;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.mode = mode;
+
+       if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_INQUIRY_MODE,
+                                       WRITE_INQUIRY_MODE_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static uint8_t get_inquiry_mode(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       if (dev->features[6] & LMP_EXT_INQ)
+               return 2;
+
+       if (dev->features[3] & LMP_RSSI_INQ)
+               return 1;
+
+       if (dev->ver.manufacturer == 11 && dev->ver.hci_rev == 0x00 &&
+                                       dev->ver.lmp_subver == 0x0757)
+               return 1;
+
+       if (dev->ver.manufacturer == 15) {
+               if (dev->ver.hci_rev == 0x03 &&
+                                       dev->ver.lmp_subver == 0x6963)
+                       return 1;
+               if (dev->ver.hci_rev == 0x09 &&
+                                       dev->ver.lmp_subver == 0x6963)
+                       return 1;
+               if (dev->ver.hci_rev == 0x00 &&
+                                       dev->ver.lmp_subver == 0x6965)
+                       return 1;
+       }
+
+       if (dev->ver.manufacturer == 31 && dev->ver.hci_rev == 0x2005 &&
+                                       dev->ver.lmp_subver == 0x1805)
+               return 1;
+
+       return 0;
+}
+
+static int init_ssp_mode(int index)
+{
+       struct dev_info *dev = &devs[index];
+       write_simple_pairing_mode_cp cp;
+
+       if (ioctl(dev->sk, HCIGETAUTHINFO, NULL) < 0 && errno == EINVAL)
+               return 0;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.mode = 0x01;
+
+       if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
+                               OCF_WRITE_SIMPLE_PAIRING_MODE,
+                               WRITE_SIMPLE_PAIRING_MODE_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int hciops_set_discoverable(int index, gboolean discoverable,
+                                                       uint16_t timeout)
+{
+       struct dev_info *dev = &devs[index];
+       uint8_t mode;
+
+       if (discoverable)
+               mode = (SCAN_PAGE | SCAN_INQUIRY);
+       else
+               mode = SCAN_PAGE;
+
+       DBG("hci%d discoverable %d", index, discoverable);
+
+       if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE,
+                                                               1, &mode) < 0)
+               return -errno;
+
+       dev->discoverable_timeout = timeout;
+
+       return 0;
+}
+
+static int hciops_set_pairable(int index, gboolean pairable)
+{
+       struct btd_adapter *adapter;
+
+       DBG("hci%d pairable %d", index, pairable);
+
+       adapter = manager_find_adapter(&devs[index].bdaddr);
+       if (adapter)
+               btd_adapter_pairable_changed(adapter, pairable);
+
+       devs[index].pairable = pairable;
+
+       return 0;
+}
+
+static int hciops_power_off(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       DBG("hci%d", index);
+
+       if (ioctl(dev->sk, HCIDEVDOWN, index) < 0 && errno != EALREADY)
+               return -errno;
+
+       return 0;
+}
+
+static void set_event_mask(int index)
+{
+       struct dev_info *dev = &devs[index];
+       /* The second byte is 0xff instead of 0x9f (two reserved bits
+        * disabled) since a Broadcom 1.2 dongle doesn't respond to the
+        * command otherwise */
+       uint8_t events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 };
+
+       /* Events for 1.2 and newer controllers */
+       if (dev->ver.lmp_ver > 1) {
+               events[4] |= 0x01; /* Flow Specification Complete */
+               events[4] |= 0x02; /* Inquiry Result with RSSI */
+               events[4] |= 0x04; /* Read Remote Extended Features Complete */
+               events[5] |= 0x08; /* Synchronous Connection Complete */
+               events[5] |= 0x10; /* Synchronous Connection Changed */
+       }
+
+       if (dev->features[3] & LMP_RSSI_INQ)
+               events[4] |= 0x02; /* Inquiry Result with RSSI */
+
+       if (dev->features[5] & LMP_SNIFF_SUBR)
+               events[5] |= 0x20; /* Sniff Subrating */
+
+       if (dev->features[5] & LMP_PAUSE_ENC)
+               events[5] |= 0x80; /* Encryption Key Refresh Complete */
+
+       if (dev->features[6] & LMP_EXT_INQ)
+               events[5] |= 0x40; /* Extended Inquiry Result */
+
+       if (dev->features[6] & LMP_NFLUSH_PKTS)
+               events[7] |= 0x01; /* Enhanced Flush Complete */
+
+       if (dev->features[7] & LMP_LSTO)
+               events[6] |= 0x80; /* Link Supervision Timeout Changed */
+
+       if (dev->features[6] & LMP_SIMPLE_PAIR) {
+               events[6] |= 0x01;      /* IO Capability Request */
+               events[6] |= 0x02;      /* IO Capability Response */
+               events[6] |= 0x04;      /* User Confirmation Request */
+               events[6] |= 0x08;      /* User Passkey Request */
+               events[6] |= 0x10;      /* Remote OOB Data Request */
+               events[6] |= 0x20;      /* Simple Pairing Complete */
+               events[7] |= 0x04;      /* User Passkey Notification */
+               events[7] |= 0x08;      /* Keypress Notification */
+               events[7] |= 0x10;      /* Remote Host Supported
+                                        * Features Notification */
+       }
+
+       if (dev->features[4] & LMP_LE)
+               events[7] |= 0x20;      /* LE Meta-Event */
+
+       hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_SET_EVENT_MASK,
+                                               sizeof(events), events);
+}
+
+static void start_adapter(int index)
+{
+       struct dev_info *dev = &devs[index];
+       uint8_t inqmode;
+       uint16_t link_policy;
+
+       set_event_mask(index);
+
+       if (dev->features[6] & LMP_SIMPLE_PAIR)
+               init_ssp_mode(index);
+
+       inqmode = get_inquiry_mode(index);
+       if (inqmode)
+               write_inq_mode(index, inqmode);
+
+       if (dev->features[7] & LMP_INQ_TX_PWR)
+               hci_send_cmd(dev->sk, OGF_HOST_CTL,
+                               OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL, 0, NULL);
+
+       /* Set default link policy */
+       link_policy = main_opts.link_policy;
+
+       if (!(dev->features[0] & LMP_RSWITCH))
+               link_policy &= ~HCI_LP_RSWITCH;
+       if (!(dev->features[0] & LMP_HOLD))
+               link_policy &= ~HCI_LP_HOLD;
+       if (!(dev->features[0] & LMP_SNIFF))
+               link_policy &= ~HCI_LP_SNIFF;
+       if (!(dev->features[1] & LMP_PARK))
+               link_policy &= ~HCI_LP_PARK;
+
+       link_policy = htobs(link_policy);
+       hci_send_cmd(dev->sk, OGF_LINK_POLICY, OCF_WRITE_DEFAULT_LINK_POLICY,
+                                       sizeof(link_policy), &link_policy);
+
+       dev->current_cod = 0;
+       memset(dev->eir, 0, sizeof(dev->eir));
+}
+
+static int hciops_stop_inquiry(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       DBG("hci%d", index);
+
+       if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_INQUIRY_CANCEL, 0, 0) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static void update_ext_inquiry_response(int index)
+{
+       struct dev_info *dev = &devs[index];
+       write_ext_inquiry_response_cp cp;
+
+       DBG("hci%d", index);
+
+       if (!(dev->features[6] & LMP_EXT_INQ))
+               return;
+
+       if (dev->ssp_mode == 0)
+               return;
+
+       if (dev->cache_enable)
+               return;
+
+       memset(&cp, 0, sizeof(cp));
+
+       eir_create(dev->name, dev->tx_power, dev->did_vendor, dev->did_product,
+                       dev->did_version, dev->did_source, dev->uuids,
+                       cp.data);
+
+       if (memcmp(cp.data, dev->eir, sizeof(cp.data)) == 0)
+               return;
+
+       memcpy(dev->eir, cp.data, sizeof(cp.data));
+
+       if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
+                               OCF_WRITE_EXT_INQUIRY_RESPONSE,
+                               WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE, &cp) < 0)
+               error("Unable to write EIR data: %s (%d)",
+                                               strerror(errno), errno);
+}
+
+static int hciops_set_name(int index, const char *name)
+{
+       struct dev_info *dev = &devs[index];
+       change_local_name_cp cp;
+
+       DBG("hci%d, name %s", index, name);
+
+       memset(&cp, 0, sizeof(cp));
+       strncpy((char *) cp.name, name, sizeof(cp.name));
+
+       if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME,
+                               CHANGE_LOCAL_NAME_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       memcpy(dev->name, cp.name, 248);
+       update_ext_inquiry_response(index);
+
+       return 0;
+}
+
+static int write_class(int index, uint32_t class)
+{
+       struct dev_info *dev = &devs[index];
+       write_class_of_dev_cp cp;
+
+       DBG("hci%d class 0x%06x", index, class);
+
+       memcpy(cp.dev_class, &class, 3);
+
+       if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV,
+                                       WRITE_CLASS_OF_DEV_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       dev->pending_cod = class;
+
+       return 0;
+}
+
+static int hciops_set_dev_class(int index, uint8_t major, uint8_t minor)
+{
+       struct dev_info *dev = &devs[index];
+       int err;
+
+       DBG("hci%d major %u minor %u", index, major, minor);
+
+       /* Update only the major and minor class bits keeping remaining bits
+        * intact*/
+       dev->wanted_cod &= 0xffe000;
+       dev->wanted_cod |= ((major & 0x1f) << 8) | minor;
+
+       if (dev->wanted_cod == dev->current_cod ||
+                       dev->cache_enable || dev->pending_cod)
+               return 0;
+
+       DBG("Changing Major/Minor class to 0x%06x", dev->wanted_cod);
+
+       err = write_class(index, dev->wanted_cod);
+       if (err < 0)
+               error("Adapter class update failed: %s (%d)",
+                                               strerror(-err), -err);
+
+       return err;
+}
+
+static gboolean init_adapter(int index)
+{
+       struct dev_info *dev = &devs[index];
+       struct btd_adapter *adapter = NULL;
+       gboolean existing_adapter = dev->registered;
+       uint8_t mode, on_mode, major, minor;
+       gboolean pairable, discoverable;
+       const char *name;
+       uint16_t discoverable_timeout;
+
+       if (!dev->registered) {
+               adapter = btd_manager_register_adapter(index, TRUE);
+               if (adapter)
+                       dev->registered = TRUE;
+       } else {
+               adapter = manager_find_adapter(&dev->bdaddr);
+               /* FIXME: manager_find_adapter should return a new ref */
+               btd_adapter_ref(adapter);
+       }
+
+       if (adapter == NULL)
+               return FALSE;
+
+       btd_adapter_get_mode(adapter, &mode, &on_mode,
+                                               &discoverable_timeout,
+                                               &pairable);
+
+       if (existing_adapter)
+               mode = on_mode;
+
+       if (mode == MODE_OFF) {
+               hciops_power_off(index);
+               goto done;
+       }
+
+       start_adapter(index);
+
+       name = btd_adapter_get_name(adapter);
+       if (name)
+               hciops_set_name(index, name);
+
+       btd_adapter_get_class(adapter, &major, &minor);
+       hciops_set_dev_class(index, major, minor);
+
+       btd_adapter_start(adapter);
+
+       discoverable = (mode == MODE_DISCOVERABLE);
+
+       hciops_set_discoverable(index, discoverable, discoverable_timeout);
+       hciops_set_pairable(index, pairable);
+
+       if (dev->already_up)
+               hciops_stop_inquiry(index);
+
+done:
+       btd_adapter_unref(adapter);
+       return TRUE;
+}
+
+static int hciops_encrypt_link(int index, bdaddr_t *dst, bt_hci_result_t cb,
+                                                       gpointer user_data)
+{
+       GIOChannel *io;
+       struct hci_cmd_data *cmd;
+       struct hci_conn_info_req *cr;
+       auth_requested_cp cp;
+       struct hci_filter nf;
+       int dd, err;
+       uint32_t link_mode;
+       uint16_t handle;
+
+       dd = hci_open_dev(index);
+       if (dd < 0)
+               return -errno;
+
+       cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+       cr->type = ACL_LINK;
+       bacpy(&cr->bdaddr, dst);
+
+       err = ioctl(dd, HCIGETCONNINFO, cr);
+       link_mode = cr->conn_info->link_mode;
+       handle = cr->conn_info->handle;
+       g_free(cr);
+
+       if (err < 0) {
+               err = -errno;
+               goto fail;
+       }
+
+       if (link_mode & HCI_LM_ENCRYPT) {
+               err = -EALREADY;
+               goto fail;
+       }
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = htobs(handle);
+
+       if (hci_send_cmd(dd, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
+                               AUTH_REQUESTED_CP_SIZE, &cp) < 0) {
+               err = -errno;
+               goto fail;
+       }
+
+       cmd = g_new0(struct hci_cmd_data, 1);
+       cmd->handle = handle;
+       cmd->ocf = OCF_AUTH_REQUESTED;
+       cmd->cb = cb;
+       cmd->caller_data = user_data;
+
+       hci_filter_clear(&nf);
+       hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
+       hci_filter_set_event(EVT_CMD_STATUS, &nf);
+       hci_filter_set_event(EVT_AUTH_COMPLETE, &nf);
+       hci_filter_set_event(EVT_ENCRYPT_CHANGE, &nf);
+
+       if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
+               err = -errno;
+               g_free(cmd);
+               goto fail;
+       }
+
+       io = g_io_channel_unix_new(dd);
+       g_io_channel_set_close_on_unref(io, FALSE);
+       g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+                       G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN,
+                       hci_event_watch, cmd, g_free);
+       g_io_channel_unref(io);
+
+       return 0;
+
+fail:
+       close(dd);
+       return err;
+}
+
+static int hciops_set_did(int index, uint16_t vendor, uint16_t product,
+                                       uint16_t version, uint16_t source)
+{
+       struct dev_info *dev = &devs[index];
+
+       dev->did_vendor = vendor;
+       dev->did_product = product;
+       dev->did_version = version;
+       dev->did_source = source;
+
+       return 0;
+}
+
+/* End async HCI command handling */
+
+/* Start of HCI event callbacks */
+
+static gint conn_handle_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct bt_conn *conn = a;
+       uint16_t handle = *((const uint16_t *) b);
+
+       return (int) conn->handle - (int) handle;
+}
+
+static struct bt_conn *find_conn_by_handle(struct dev_info *dev,
+                                                       uint16_t handle)
+{
+       GSList *match;
+
+       match = g_slist_find_custom(dev->connections, &handle,
+                                                       conn_handle_cmp);
+       if (match)
+               return match->data;
+
+       return NULL;
+}
+
+static gint conn_bdaddr_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct bt_conn *conn = a;
+       const bdaddr_t *bdaddr = b;
+
+       return bacmp(&conn->bdaddr, bdaddr);
+}
+
+static struct bt_conn *find_connection(struct dev_info *dev, bdaddr_t *bdaddr)
+{
+       GSList *match;
+
+       match = g_slist_find_custom(dev->connections, bdaddr, conn_bdaddr_cmp);
+       if (match)
+               return match->data;
+
+       return NULL;
+}
+
+static struct bt_conn *get_connection(struct dev_info *dev, bdaddr_t *bdaddr)
+{
+       struct bt_conn *conn;
+
+       conn = find_connection(dev, bdaddr);
+       if (conn)
+               return conn;
+
+       conn = g_new0(struct bt_conn, 1);
+
+       conn->dev = dev;
+       conn->loc_cap = dev->io_capability;
+       conn->loc_auth = 0xff;
+       conn->rem_auth = 0xff;
+       bacpy(&conn->bdaddr, bdaddr);
+
+       dev->connections = g_slist_append(dev->connections, conn);
+
+       return conn;
+}
+
+static int get_handle(int index, bdaddr_t *bdaddr, uint16_t *handle)
+{
+       struct dev_info *dev = &devs[index];
+       struct bt_conn *conn;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d dba %s", index, addr);
+
+       conn = find_connection(dev, bdaddr);
+       if (conn == NULL)
+               return -ENOENT;
+
+       *handle = conn->handle;
+
+       return 0;
+}
+
+static int disconnect_addr(int index, bdaddr_t *dba, uint8_t reason)
+{
+       disconnect_cp cp;
+       uint16_t handle;
+       int err;
+
+       err = get_handle(index, dba, &handle);
+       if (err < 0)
+               return err;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = htobs(handle);
+       cp.reason = reason;
+
+       if (hci_send_cmd(devs[index].sk, OGF_LINK_CTL, OCF_DISCONNECT,
+                                               DISCONNECT_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static void bonding_complete(struct dev_info *dev, struct bt_conn *conn,
+                                                               uint8_t status)
+{
+       struct btd_adapter *adapter;
+
+       DBG("status 0x%02x", status);
+
+       if (conn->io != NULL) {
+               /* bonding_connect_cb takes care of the successul case */
+               if (status != 0)
+                       g_io_channel_shutdown(conn->io, TRUE, NULL);
+               g_io_channel_unref(conn->io);
+               conn->io = NULL;
+       }
+
+       conn->bonding_initiator = FALSE;
+
+       adapter = manager_find_adapter(&dev->bdaddr);
+       if (adapter)
+               adapter_bonding_complete(adapter, &conn->bdaddr, status);
+}
+
+static int get_auth_info(int index, bdaddr_t *bdaddr, uint8_t *auth)
+{
+       struct dev_info *dev = &devs[index];
+       struct hci_auth_info_req req;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d dba %s", index, addr);
+
+       memset(&req, 0, sizeof(req));
+       bacpy(&req.bdaddr, bdaddr);
+
+       if (ioctl(dev->sk, HCIGETAUTHINFO, (unsigned long) &req) < 0)
+               return -errno;
+
+       if (auth)
+               *auth = req.type;
+
+       return 0;
+}
+
+/* Link Key handling */
+
+static void link_key_request(int index, bdaddr_t *dba)
+{
+       struct dev_info *dev = &devs[index];
+       struct link_key_info *key_info;
+       struct bt_conn *conn;
+       GSList *match;
+       char da[18];
+
+       ba2str(dba, da);
+       DBG("hci%d dba %s", index, da);
+
+       conn = get_connection(dev, dba);
+       if (conn->handle == 0)
+               conn->secmode3 = TRUE;
+
+       get_auth_info(index, dba, &conn->loc_auth);
+
+       DBG("kernel auth requirements = 0x%02x", conn->loc_auth);
+
+       match = g_slist_find_custom(dev->keys, dba, (GCompareFunc) bacmp);
+       if (match)
+               key_info = match->data;
+       else
+               key_info = NULL;
+
+       DBG("Matching key %s", key_info ? "found" : "not found");
+
+       if (key_info == NULL || (!dev->debug_keys && key_info->type == 0x03)) {
+               /* Link key not found */
+               hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_NEG_REPLY,
+                                                               6, dba);
+               return;
+       }
+
+       /* Link key found */
+
+       DBG("link key type 0x%02x", key_info->type);
+
+       /* Don't use unauthenticated combination keys if MITM is
+        * required */
+       if (key_info->type == 0x04 && conn->loc_auth != 0xff &&
+                                               (conn->loc_auth & 0x01))
+               hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_NEG_REPLY,
+                                                               6, dba);
+       else {
+               link_key_reply_cp lr;
+
+               memcpy(lr.link_key, key_info->key, 16);
+               bacpy(&lr.bdaddr, dba);
+
+               hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_LINK_KEY_REPLY,
+                                               LINK_KEY_REPLY_CP_SIZE, &lr);
+       }
+}
+
+static void link_key_notify(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_link_key_notify *evt = ptr;
+       bdaddr_t *dba = &evt->bdaddr;
+       struct link_key_info *key_info;
+       uint8_t old_key_type, key_type;
+       struct bt_conn *conn;
+       GSList *match;
+       char da[18];
+       uint8_t status = 0;
+
+       ba2str(dba, da);
+       DBG("hci%d dba %s type %d", index, da, evt->key_type);
+
+       conn = get_connection(dev, &evt->bdaddr);
+
+       match = g_slist_find_custom(dev->keys, dba, (GCompareFunc) bacmp);
+       if (match)
+               key_info = match->data;
+       else
+               key_info = NULL;
+
+       if (key_info == NULL) {
+               key_info = g_new0(struct link_key_info, 1);
+               bacpy(&key_info->bdaddr, &evt->bdaddr);
+               old_key_type = 0xff;
+       } else {
+               dev->keys = g_slist_remove(dev->keys, key_info);
+               old_key_type = key_info->type;
+       }
+
+       memcpy(key_info->key, evt->link_key, sizeof(evt->link_key));
+       key_info->type = evt->key_type;
+       key_info->pin_len = dev->pin_length;
+
+       key_type = evt->key_type;
+
+       DBG("key type 0x%02x old key type 0x%02x", key_type, old_key_type);
+       DBG("local auth 0x%02x and remote auth 0x%02x",
+                                       conn->loc_auth, conn->rem_auth);
+
+       if (key_type == HCI_LK_CHANGED_COMBINATION) {
+               /* Some buggy controller combinations generate a changed
+                * combination key for legacy pairing even when there's no
+                * previous key */
+               if (conn->rem_auth == 0xff && old_key_type == 0xff)
+                       key_type = HCI_LK_COMBINATION;
+               else if (old_key_type != 0xff)
+                       key_type = old_key_type;
+               else
+                       /* This is Changed Combination Link Key for
+                        * a temporary link key.*/
+                       goto done;
+       }
+
+       key_info->type = key_type;
+
+       /* Skip the storage check if this is a debug key */
+       if (key_type == HCI_LK_DEBUG_COMBINATION)
+               goto done;
+
+       /* Store the link key persistently if one of the following is true:
+        * 1. this is a legacy link key
+        * 2. this is a changed combination key and there was a previously
+        *    stored one
+        * 3. neither local nor remote side had no-bonding as a requirement
+        * 4. the local side had dedicated bonding as a requirement
+        * 5. the remote side is using dedicated bonding since in that case
+        *    also the local requirements are set to dedicated bonding
+        * If none of the above match only keep the link key around for
+        * this connection and set the temporary flag for the device.
+        */
+       if (key_type < HCI_LK_DEBUG_COMBINATION ||
+                       (key_type == HCI_LK_CHANGED_COMBINATION
+                                       && old_key_type != HCI_LK_INVALID) ||
+                       (conn->loc_auth > 0x01 && conn->rem_auth > 0x01) ||
+                       (conn->loc_auth == 0x02 || conn->loc_auth == 0x03) ||
+                       (conn->rem_auth == 0x02 || conn->rem_auth == 0x03)) {
+               int err;
+
+               err = btd_event_link_key_notify(&dev->bdaddr, dba,
+                                               evt->link_key, key_type,
+                                               dev->pin_length);
+
+               if (err == -ENODEV)
+                       status = HCI_OE_LOW_RESOURCES;
+               else if (err < 0)
+                       status = HCI_MEMORY_FULL;
+
+               goto done;
+       }
+
+done:
+       dev->pin_length = 0;
+
+       if (status != 0) {
+               g_free(key_info);
+               bonding_complete(dev, conn, status);
+               disconnect_addr(index, dba, status);
+               return;
+       }
+
+       dev->keys = g_slist_prepend(dev->keys, key_info);
+
+       /* If we're connected and not dedicated bonding initiators we're
+        * done with the bonding process */
+       if (!conn->bonding_initiator && conn->handle != 0)
+               bonding_complete(dev, conn, 0);
+}
+
+static void return_link_keys(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_return_link_keys *evt = ptr;
+       uint8_t num = evt->num_keys;
+       unsigned char key[16];
+       char da[18];
+       bdaddr_t dba;
+       int i;
+
+       DBG("hci%d num_keys %u", index, num);
+
+       ptr++;
+
+       for (i = 0; i < num; i++) {
+               bacpy(&dba, ptr); ba2str(&dba, da);
+               memcpy(key, ptr + 6, 16);
+
+               DBG("hci%d returned key for %s", index, da);
+
+               btd_event_returned_link_key(&dev->bdaddr, &dba);
+
+               ptr += 22;
+       }
+}
+
+/* Simple Pairing handling */
+
+static int hciops_confirm_reply(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                                                       gboolean success)
+{
+       struct dev_info *dev = &devs[index];
+       user_confirm_reply_cp cp;
+       char addr[18];
+       int err;
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d dba %s success %d", index, addr, success);
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+
+       if (success)
+               err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+                                       OCF_USER_CONFIRM_REPLY,
+                                       USER_CONFIRM_REPLY_CP_SIZE, &cp);
+       else
+               err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+                                       OCF_USER_CONFIRM_NEG_REPLY,
+                                       USER_CONFIRM_REPLY_CP_SIZE, &cp);
+
+       if (err < 0)
+               err = -errno;
+
+       return err;
+}
+
+static void user_confirm_request(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_user_confirm_request *req = ptr;
+       gboolean loc_mitm, rem_mitm;
+       struct bt_conn *conn;
+
+       DBG("hci%d", index);
+
+       conn = find_connection(dev, &req->bdaddr);
+       if (conn == NULL)
+               return;
+
+       loc_mitm = (conn->loc_auth & 0x01) ? TRUE : FALSE;
+       rem_mitm = (conn->rem_auth & 0x01) ? TRUE : FALSE;
+
+       /* If we require MITM but the remote device can't provide that
+        * (it has NoInputNoOutput) then reject the confirmation
+        * request. The only exception is when we're dedicated bonding
+        * initiators since then we always have the MITM bit set. */
+       if (!conn->bonding_initiator && loc_mitm && conn->rem_cap == 0x03) {
+               error("Rejecting request: remote device can't provide MITM");
+               goto fail;
+       }
+
+       /* If no side requires MITM protection; auto-accept */
+       if ((conn->loc_auth == 0xff || !loc_mitm || conn->rem_cap == 0x03) &&
+                                       (!rem_mitm || conn->loc_cap == 0x03)) {
+               DBG("auto accept of confirmation");
+
+               /* Wait 5 milliseconds before doing auto-accept */
+               usleep(5000);
+
+               if (hciops_confirm_reply(index, &req->bdaddr,
+                                               BDADDR_BREDR, TRUE) < 0)
+                       goto fail;
+
+               return;
+       }
+
+       if (btd_event_user_confirm(&dev->bdaddr, &req->bdaddr,
+                                               btohl(req->passkey)) == 0)
+               return;
+
+fail:
+       hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_USER_CONFIRM_NEG_REPLY,
+                                                               6, ptr);
+}
+
+static void user_passkey_request(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_user_passkey_request *req = ptr;
+
+       DBG("hci%d", index);
+
+       if (btd_event_user_passkey(&dev->bdaddr, &req->bdaddr) < 0)
+               hci_send_cmd(dev->sk, OGF_LINK_CTL,
+                               OCF_USER_PASSKEY_NEG_REPLY, 6, ptr);
+}
+
+static void user_passkey_notify(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_user_passkey_notify *req = ptr;
+
+       DBG("hci%d", index);
+
+       btd_event_user_notify(&dev->bdaddr, &req->bdaddr,
+                                               btohl(req->passkey));
+}
+
+static gint oob_bdaddr_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct oob_data *data = a;
+       const bdaddr_t *bdaddr = b;
+
+       return bacmp(&data->bdaddr, bdaddr);
+}
+
+static void remote_oob_data_request(int index, bdaddr_t *bdaddr)
+{
+       struct dev_info *dev = &devs[index];
+       GSList *match;
+
+       DBG("hci%d", index);
+
+       match = g_slist_find_custom(dev->oob_data, bdaddr, oob_bdaddr_cmp);
+
+       if (match) {
+               struct oob_data *data;
+               remote_oob_data_reply_cp cp;
+
+               data = match->data;
+
+               bacpy(&cp.bdaddr, &data->bdaddr);
+               memcpy(cp.hash, data->hash, sizeof(cp.hash));
+               memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer));
+
+               dev->oob_data = g_slist_delete_link(dev->oob_data, match);
+
+               hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_REPLY,
+                               REMOTE_OOB_DATA_REPLY_CP_SIZE, &cp);
+
+       } else {
+               hci_send_cmd(dev->sk, OGF_LINK_CTL,
+                               OCF_REMOTE_OOB_DATA_NEG_REPLY, 6, bdaddr);
+       }
+}
+
+static int get_io_cap(int index, bdaddr_t *bdaddr, uint8_t *cap, uint8_t *auth)
+{
+       struct dev_info *dev = &devs[index];
+       struct bt_conn *conn;
+       int err;
+
+       conn = find_connection(dev, bdaddr);
+       if (conn == NULL)
+               return -ENOENT;
+
+       err = get_auth_info(index, bdaddr, &conn->loc_auth);
+       if (err < 0)
+               return err;
+
+       DBG("initial authentication requirement is 0x%02x", conn->loc_auth);
+
+       if (!dev->pairable && !conn->bonding_initiator) {
+               if (conn->rem_auth < 0x02) {
+                       DBG("Allowing no bonding in non-bondable mode");
+                       /* Kernel defaults to general bonding and so
+                        * overwrite for this special case. Otherwise
+                        * non-pairable test cases will fail. */
+                       conn->loc_auth = conn->rem_auth;
+                       goto done;
+               }
+
+               return -EPERM;
+       }
+
+       /* If the kernel doesn't know the local requirement just mirror
+        * the remote one */
+       if (conn->loc_auth == 0xff)
+               conn->loc_auth = conn->rem_auth;
+
+       if (conn->loc_auth == 0x00 || conn->loc_auth == 0x04) {
+               /* If remote requests dedicated bonding follow that lead */
+               if (conn->rem_auth == 0x02 || conn->rem_auth == 0x03) {
+
+                       /* If both remote and local IO capabilities allow MITM
+                        * then require it, otherwise don't */
+                       if (conn->rem_cap == 0x03 || conn->loc_cap == 0x03)
+                               conn->loc_auth = 0x02;
+                       else
+                               conn->loc_auth = 0x03;
+               }
+
+               /* If remote indicates no bonding then follow that. This
+                * is important since the kernel might give general bonding
+                * as default. */
+               if (conn->rem_auth == 0x00 || conn->rem_auth == 0x01)
+                       conn->loc_auth = 0x00;
+
+               /* If remote requires MITM then also require it, unless
+                * our IO capability is NoInputNoOutput (so some
+                * just-works security cases can be tested) */
+               if (conn->rem_auth != 0xff && (conn->rem_auth & 0x01) &&
+                                                       conn->loc_cap != 0x03)
+                       conn->loc_auth |= 0x01;
+       }
+
+done:
+       *cap = conn->loc_cap;
+       *auth = conn->loc_auth;
+
+       DBG("final authentication requirement is 0x%02x", *auth);
+
+       return 0;
+}
+
+static void io_capa_request(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       bdaddr_t *dba = ptr;
+       uint8_t cap, auth = 0xff;
+       char da[18];
+       int err;
+
+       ba2str(dba, da);
+       DBG("hci%d IO capability request for %s", index, da);
+
+       err = get_io_cap(index, dba, &cap, &auth);
+       if (err < 0) {
+               io_capability_neg_reply_cp cp;
+
+               error("Getting IO capability failed: %s (%d)",
+                                               strerror(-err), -err);
+
+               memset(&cp, 0, sizeof(cp));
+               bacpy(&cp.bdaddr, dba);
+               cp.reason = HCI_PAIRING_NOT_ALLOWED;
+               hci_send_cmd(dev->sk, OGF_LINK_CTL,
+                                       OCF_IO_CAPABILITY_NEG_REPLY,
+                                       IO_CAPABILITY_NEG_REPLY_CP_SIZE, &cp);
+       } else {
+               io_capability_reply_cp cp;
+               struct bt_conn *conn;
+               GSList *match;
+
+               memset(&cp, 0, sizeof(cp));
+               bacpy(&cp.bdaddr, dba);
+               cp.capability = cap;
+               cp.authentication = auth;
+
+               conn = find_connection(dev, dba);
+               match = g_slist_find_custom(dev->oob_data, dba, oob_bdaddr_cmp);
+
+               if ((conn->bonding_initiator || conn->rem_oob_data == 0x01) &&
+                               match)
+                       cp.oob_data = 0x01;
+               else
+                       cp.oob_data = 0x00;
+
+               hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_IO_CAPABILITY_REPLY,
+                                       IO_CAPABILITY_REPLY_CP_SIZE, &cp);
+       }
+}
+
+static void io_capa_response(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_io_capability_response *evt = ptr;
+       struct bt_conn *conn;
+       char da[18];
+
+       ba2str(&evt->bdaddr, da);
+       DBG("hci%d IO capability response from %s", index, da);
+
+       conn = find_connection(dev, &evt->bdaddr);
+       if (conn) {
+               conn->rem_cap = evt->capability;
+               conn->rem_auth = evt->authentication;
+               conn->rem_oob_data = evt->oob_data;
+       }
+}
+
+/* PIN code handling */
+
+static void pin_code_request(int index, bdaddr_t *dba)
+{
+       struct dev_info *dev = &devs[index];
+       struct bt_conn *conn;
+       char addr[18];
+       int err;
+
+       ba2str(dba, addr);
+       DBG("hci%d PIN request for %s", index, addr);
+
+       conn = get_connection(dev, dba);
+       if (conn->handle == 0)
+               conn->secmode3 = TRUE;
+
+       /* Check if the adapter is not pairable and if there isn't a bonding in
+        * progress */
+       if (!dev->pairable && !conn->bonding_initiator) {
+               DBG("Rejecting PIN request in non-pairable mode");
+               goto reject;
+       }
+
+       err = btd_event_request_pin(&dev->bdaddr, dba, FALSE);
+       if (err < 0) {
+               error("PIN code negative reply: %s", strerror(-err));
+               goto reject;
+       }
+
+       return;
+
+reject:
+       hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, dba);
+}
+
+static inline void remote_features_notify(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_remote_host_features_notify *evt = ptr;
+
+       if (evt->features[0] & 0x01)
+               btd_event_set_legacy_pairing(&dev->bdaddr, &evt->bdaddr,
+                                                                       FALSE);
+       else
+               btd_event_set_legacy_pairing(&dev->bdaddr, &evt->bdaddr,
+                                                                       TRUE);
+
+       write_features_info(&dev->bdaddr, &evt->bdaddr, NULL, evt->features);
+}
+
+static void read_local_version_complete(int index,
+                               const read_local_version_rp *rp)
+{
+       struct dev_info *dev = &devs[index];
+
+       if (rp->status)
+               return;
+
+       dev->ver.manufacturer = btohs(bt_get_unaligned(&rp->manufacturer));
+       dev->ver.hci_ver = rp->hci_ver;
+       dev->ver.hci_rev = btohs(bt_get_unaligned(&rp->hci_rev));
+       dev->ver.lmp_ver = rp->lmp_ver;
+       dev->ver.lmp_subver = btohs(bt_get_unaligned(&rp->lmp_subver));
+
+       if (!dev->pending)
+               return;
+
+       hci_clear_bit(PENDING_VERSION, &dev->pending);
+
+       DBG("Got version for hci%d", index);
+
+       if (!dev->pending && dev->up)
+               init_adapter(index);
+}
+
+static void read_local_features_complete(int index,
+                               const read_local_features_rp *rp)
+{
+       struct dev_info *dev = &devs[index];
+
+       if (rp->status)
+               return;
+
+       memcpy(dev->features, rp->features, 8);
+
+       if (!dev->pending)
+               return;
+
+       hci_clear_bit(PENDING_FEATURES, &dev->pending);
+
+       DBG("Got features for hci%d", index);
+
+       if (!dev->pending && dev->up)
+               init_adapter(index);
+}
+
+static void update_name(int index, const char *name)
+{
+       struct btd_adapter *adapter;
+
+       adapter = manager_find_adapter_by_id(index);
+       if (adapter)
+               adapter_name_changed(adapter, name);
+
+       update_ext_inquiry_response(index);
+}
+
+static void read_local_name_complete(int index, read_local_name_rp *rp)
+{
+       struct dev_info *dev = &devs[index];
+
+       DBG("hci%d status %u", index, rp->status);
+
+       if (rp->status)
+               return;
+
+       memcpy(dev->name, rp->name, 248);
+
+       if (!dev->pending) {
+               update_name(index, (char *) rp->name);
+               return;
+       }
+
+       hci_clear_bit(PENDING_NAME, &dev->pending);
+
+       DBG("Got name for hci%d", index);
+
+       if (!dev->pending && dev->up)
+               init_adapter(index);
+}
+
+static void read_tx_power_complete(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+
+       read_inq_response_tx_power_level_rp *rp = ptr;
+
+       DBG("hci%d status %u", index, rp->status);
+
+       if (rp->status)
+               return;
+
+       dev->tx_power = rp->level;
+       update_ext_inquiry_response(index);
+}
+
+static void read_simple_pairing_mode_complete(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       read_simple_pairing_mode_rp *rp = ptr;
+
+       DBG("hci%d status %u", index, rp->status);
+
+       if (rp->status)
+               return;
+
+       dev->ssp_mode = rp->mode;
+       update_ext_inquiry_response(index);
+}
+
+static void read_local_ext_features_complete(int index,
+                               const read_local_ext_features_rp *rp)
+{
+       struct dev_info *dev = &devs[index];
+
+       DBG("hci%d status %u", index, rp->status);
+
+       if (rp->status)
+               return;
+
+       /* Local Extended feature page number is 1 */
+       if (rp->page_num != 1)
+               return;
+
+       memcpy(dev->extfeatures, rp->features, sizeof(dev->extfeatures));
+}
+
+static void read_bd_addr_complete(int index, read_bd_addr_rp *rp)
+{
+       struct dev_info *dev = &devs[index];
+
+       DBG("hci%d status %u", index, rp->status);
+
+       if (rp->status)
+               return;
+
+       bacpy(&dev->bdaddr, &rp->bdaddr);
+
+       if (!dev->pending)
+               return;
+
+       hci_clear_bit(PENDING_BDADDR, &dev->pending);
+
+       DBG("Got bdaddr for hci%d", index);
+
+       if (!dev->pending && dev->up)
+               init_adapter(index);
+}
+
+static inline void cs_inquiry_evt(int index, uint8_t status)
+{
+       if (status) {
+               error("Inquiry Failed with status 0x%02x", status);
+               return;
+       }
+
+       set_state(index, DISCOV_INQ);
+}
+
+static inline void cmd_status(int index, void *ptr)
+{
+       evt_cmd_status *evt = ptr;
+       uint16_t opcode = btohs(evt->opcode);
+
+       if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY))
+               cs_inquiry_evt(index, evt->status);
+}
+
+static gboolean discoverable_timeout_handler(gpointer user_data)
+{
+       struct dev_info *dev = user_data;
+
+       hciops_set_discoverable(dev->id, FALSE, 0);
+
+       return FALSE;
+}
+
+/* Limited Discoverable bit mask in CoD */
+#define LIMITED_BIT                    0x002000
+
+static int hciops_set_limited_discoverable(int index, gboolean limited)
+{
+       struct dev_info *dev = &devs[index];
+       int num = (limited ? 2 : 1);
+       uint8_t lap[] = { 0x33, 0x8b, 0x9e, 0x00, 0x8b, 0x9e };
+       write_current_iac_lap_cp cp;
+
+       DBG("hci%d limited %d", index, limited);
+
+       /* Check if limited bit needs to be set/reset */
+       if (limited)
+               dev->wanted_cod |= LIMITED_BIT;
+       else
+               dev->wanted_cod &= ~LIMITED_BIT;
+
+       /* If we dont need the toggling, save an unnecessary CoD write */
+       if (dev->pending_cod || dev->wanted_cod == dev->current_cod)
+               return 0;
+
+       /*
+        * 1: giac
+        * 2: giac + liac
+        */
+       memset(&cp, 0, sizeof(cp));
+       cp.num_current_iac = num;
+       memcpy(&cp.lap, lap, num * 3);
+
+       if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_CURRENT_IAC_LAP,
+                                               (num * 3 + 1), &cp) < 0)
+               return -errno;
+
+       return write_class(index, dev->wanted_cod);
+}
+
+static void reset_discoverable_timeout(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       if (dev->discoverable_id > 0) {
+               g_source_remove(dev->discoverable_id);
+               dev->discoverable_id = 0;
+       }
+}
+
+static void set_discoverable_timeout(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       reset_discoverable_timeout(index);
+
+       if (dev->discoverable_timeout == 0) {
+               hciops_set_limited_discoverable(index, FALSE);
+               return;
+       }
+
+       /* Set limited discoverable if pairable and interval between 0 to 60
+          sec */
+       if (dev->pairable && dev->discoverable_timeout <= 60)
+               hciops_set_limited_discoverable(index, TRUE);
+       else
+               hciops_set_limited_discoverable(index, FALSE);
+
+       dev->discoverable_id = g_timeout_add_seconds(dev->discoverable_timeout,
+                                               discoverable_timeout_handler,
+                                               dev);
+}
+
+static void read_scan_complete(int index, uint8_t status, void *ptr)
+{
+       struct btd_adapter *adapter;
+       read_scan_enable_rp *rp = ptr;
+
+       DBG("hci%d status %u", index, status);
+
+       switch (rp->enable) {
+       case (SCAN_PAGE | SCAN_INQUIRY):
+       case SCAN_INQUIRY:
+               set_discoverable_timeout(index);
+               break;
+       default:
+               reset_discoverable_timeout(index);
+               hciops_set_limited_discoverable(index, FALSE);
+       }
+
+       adapter = manager_find_adapter_by_id(index);
+       if (!adapter) {
+               error("Unable to find matching adapter");
+               return;
+       }
+
+       adapter_mode_changed(adapter, rp->enable);
+}
+
+static void write_class_complete(int index, uint8_t status)
+{
+       struct dev_info *dev = &devs[index];
+       struct btd_adapter *adapter;
+
+       if (status)
+               return;
+
+       if (dev->pending_cod == 0)
+               return;
+
+       dev->current_cod = dev->pending_cod;
+       dev->pending_cod = 0;
+
+       adapter = manager_find_adapter(&dev->bdaddr);
+       if (adapter)
+               btd_adapter_class_changed(adapter, dev->current_cod);
+
+       update_ext_inquiry_response(index);
+
+       if (dev->wanted_cod == dev->current_cod)
+               return;
+
+       if (dev->wanted_cod & LIMITED_BIT &&
+                       !(dev->current_cod & LIMITED_BIT))
+               hciops_set_limited_discoverable(index, TRUE);
+       else if (!(dev->wanted_cod & LIMITED_BIT) &&
+                                       (dev->current_cod & LIMITED_BIT))
+               hciops_set_limited_discoverable(index, FALSE);
+       else
+               write_class(index, dev->wanted_cod);
+}
+
+static void read_local_oob_data_complete(int index, uint8_t status,
+                                               read_local_oob_data_rp *rp)
+{
+       struct btd_adapter *adapter = manager_find_adapter_by_id(index);
+
+       if (!adapter)
+               return;
+
+       if (status)
+               oob_read_local_data_complete(adapter, NULL, NULL);
+       else
+               oob_read_local_data_complete(adapter, rp->hash, rp->randomizer);
+}
+
+static inline void inquiry_complete_evt(int index, uint8_t status)
+{
+       int adapter_type;
+       struct btd_adapter *adapter;
+
+       if (status) {
+               error("Inquiry Failed with status 0x%02x", status);
+               return;
+       }
+
+       adapter = manager_find_adapter_by_id(index);
+       if (!adapter) {
+               error("No matching adapter found");
+               return;
+       }
+
+       adapter_type = get_adapter_type(index);
+
+       if (adapter_type == BR_EDR_LE &&
+                       start_scanning(index, TIMEOUT_BR_LE_SCAN) == 0)
+               return;
+
+       set_state(index, DISCOV_NAMES);
+}
+
+static inline void cc_inquiry_cancel(int index, uint8_t status)
+{
+       if (status) {
+               error("Inquiry Cancel Failed with status 0x%02x", status);
+               return;
+       }
+
+       set_state(index, DISCOV_HALTED);
+}
+
+static inline void cc_le_set_scan_enable(int index, uint8_t status)
+{
+       struct dev_info *info = &devs[index];
+
+       if (status) {
+               error("LE Set Scan Enable Failed with status 0x%02x", status);
+               return;
+       }
+
+       if (info->discov_state == DISCOV_SCAN)
+               set_state(index, DISCOV_HALTED);
+       else
+               set_state(index, DISCOV_SCAN);
+}
+
+static inline void cmd_complete(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_cmd_complete *evt = ptr;
+       uint16_t opcode = btohs(evt->opcode);
+       uint8_t status = *((uint8_t *) ptr + EVT_CMD_COMPLETE_SIZE);
+
+       switch (opcode) {
+       case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION):
+               ptr += sizeof(evt_cmd_complete);
+               read_local_version_complete(index, ptr);
+               break;
+       case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES):
+               ptr += sizeof(evt_cmd_complete);
+               read_local_features_complete(index, ptr);
+               break;
+       case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_LOCAL_EXT_FEATURES):
+               ptr += sizeof(evt_cmd_complete);
+               read_local_ext_features_complete(index, ptr);
+               break;
+       case cmd_opcode_pack(OGF_INFO_PARAM, OCF_READ_BD_ADDR):
+               ptr += sizeof(evt_cmd_complete);
+               read_bd_addr_complete(index, ptr);
+               break;
+       case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL):
+               cc_inquiry_cancel(index, status);
+               break;
+       case cmd_opcode_pack(OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE):
+               cc_le_set_scan_enable(index, status);
+               break;
+       case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME):
+               if (!status)
+                       hci_send_cmd(dev->sk, OGF_HOST_CTL,
+                                               OCF_READ_LOCAL_NAME, 0, 0);
+               break;
+       case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE):
+               hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_SCAN_ENABLE,
+                                                               0, NULL);
+               break;
+       case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SCAN_ENABLE):
+               ptr += sizeof(evt_cmd_complete);
+               read_scan_complete(index, status, ptr);
+               break;
+       case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV):
+               write_class_complete(index, status);
+               break;
+       case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_SIMPLE_PAIRING_MODE):
+               if (!status)
+                       hci_send_cmd(dev->sk, OGF_HOST_CTL,
+                                       OCF_READ_SIMPLE_PAIRING_MODE, 0, NULL);
+               break;
+       case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_SIMPLE_PAIRING_MODE):
+               ptr += sizeof(evt_cmd_complete);
+               read_simple_pairing_mode_complete(index, ptr);
+               break;
+       case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_NAME):
+               ptr += sizeof(evt_cmd_complete);
+               read_local_name_complete(index, ptr);
+               break;
+       case cmd_opcode_pack(OGF_HOST_CTL,
+                                       OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL):
+               ptr += sizeof(evt_cmd_complete);
+               read_tx_power_complete(index, ptr);
+               break;
+       case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_OOB_DATA):
+               ptr += sizeof(evt_cmd_complete);
+               read_local_oob_data_complete(index, status, ptr);
+               break;
+       };
+}
+
+static inline void remote_name_information(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_remote_name_req_complete *evt = ptr;
+       struct btd_adapter *adapter;
+       char name[MAX_NAME_LENGTH + 1];
+       struct found_dev *found;
+
+       GSList *match;
+
+       DBG("hci%d status %u", index, evt->status);
+
+       memset(name, 0, sizeof(name));
+
+       if (evt->status == 0) {
+               memcpy(name, evt->name, MAX_NAME_LENGTH);
+               btd_event_remote_name(&dev->bdaddr, &evt->bdaddr, name);
+       }
+
+       adapter = manager_find_adapter_by_id(index);
+       if (!adapter) {
+               error("No matching adapter found");
+               return;
+       }
+
+       match = g_slist_find_custom(dev->need_name, &evt->bdaddr,
+                                                       found_dev_bda_cmp);
+       if (match == NULL)
+               return;
+
+       found = match->data;
+       found->name_state = NAME_NOT_NEEDED;
+
+       dev->need_name = g_slist_remove_link(dev->need_name, match);
+
+       match->next = dev->found_devs;
+       dev->found_devs = match;
+       dev->found_devs = g_slist_sort(dev->found_devs, found_dev_rssi_cmp);
+
+       if (resolve_names(dev, adapter) < 0)
+               set_state(index, DISCOV_HALTED);
+}
+
+static inline void remote_version_information(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_read_remote_version_complete *evt = ptr;
+       struct bt_conn *conn;
+
+       DBG("hci%d status %u", index, evt->status);
+
+       if (evt->status)
+               return;
+
+       conn = find_conn_by_handle(dev, btohs(evt->handle));
+       if (conn == NULL)
+               return;
+
+       write_version_info(&dev->bdaddr, &conn->bdaddr,
+                               btohs(evt->manufacturer), evt->lmp_ver,
+                               btohs(evt->lmp_subver));
+}
+
+static void dev_found(struct dev_info *info, bdaddr_t *dba, uint8_t bdaddr_type,
+                               uint8_t *cod, int8_t rssi, uint8_t cfm_name,
+                               uint8_t *eir, size_t eir_len)
+{
+       struct found_dev *dev;
+       GSList *match;
+
+       match = g_slist_find_custom(info->found_devs, dba, found_dev_bda_cmp);
+       if (match != NULL) {
+               cfm_name = 0;
+               goto event;
+       }
+
+       dev = g_new0(struct found_dev, 1);
+       bacpy(&dev->bdaddr, dba);
+       dev->rssi = rssi;
+       if (cfm_name)
+               dev->name_state = NAME_UNKNOWN;
+       else
+               dev->name_state = NAME_NOT_NEEDED;
+
+       if (cod && !eir_has_data_type(eir, eir_len, EIR_CLASS_OF_DEV))
+               eir_len = eir_append_data(eir, eir_len, EIR_CLASS_OF_DEV,
+                                                               cod, 3);
+
+       info->found_devs = g_slist_prepend(info->found_devs, dev);
+
+event:
+       btd_event_device_found(&info->bdaddr, dba, bdaddr_type, rssi, cfm_name,
+                                                               eir, eir_len);
+}
+
+static inline void inquiry_result(int index, int plen, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       uint8_t num = *(uint8_t *) ptr++;
+       int i;
+
+       for (i = 0; i < num; i++) {
+               inquiry_info *info = ptr;
+               uint8_t eir[5];
+
+               memset(eir, 0, sizeof(eir));
+               dev_found(dev, &info->bdaddr, BDADDR_BREDR, info->dev_class,
+                                                               0, 1, eir, 0);
+               ptr += INQUIRY_INFO_SIZE;
+       }
+}
+
+static inline void inquiry_result_with_rssi(int index, int plen, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       uint8_t num = *(uint8_t *) ptr++;
+       uint8_t eir[5];
+       int i;
+
+       if (!num)
+               return;
+
+       if ((plen - 1) / num == INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE) {
+               for (i = 0; i < num; i++) {
+                       inquiry_info_with_rssi_and_pscan_mode *info = ptr;
+
+                       memset(eir, 0, sizeof(eir));
+                       dev_found(dev, &info->bdaddr, BDADDR_BREDR,
+                                               info->dev_class, info->rssi,
+                                               1, eir, 0);
+                       ptr += INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE;
+               }
+       } else {
+               for (i = 0; i < num; i++) {
+                       inquiry_info_with_rssi *info = ptr;
+
+                       memset(eir, 0, sizeof(eir));
+                       dev_found(dev, &info->bdaddr, BDADDR_BREDR,
+                                               info->dev_class, info->rssi,
+                                               1, eir, 0);
+                       ptr += INQUIRY_INFO_WITH_RSSI_SIZE;
+               }
+       }
+}
+
+static inline void extended_inquiry_result(int index, int plen, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       uint8_t num = *(uint8_t *) ptr++;
+       int i;
+
+       for (i = 0; i < num; i++) {
+               extended_inquiry_info *info = ptr;
+               uint8_t eir[sizeof(info->data) + 5];
+               gboolean cfm_name;
+               size_t eir_len;
+
+               eir_len = eir_length(info->data, sizeof(info->data));
+
+               memset(eir, 0, sizeof(eir));
+               memcpy(eir, info->data, eir_len);
+
+               if (eir_has_data_type(eir, eir_len, EIR_NAME_COMPLETE))
+                       cfm_name = FALSE;
+               else
+                       cfm_name = TRUE;
+
+               dev_found(dev, &info->bdaddr, BDADDR_BREDR, info->dev_class,
+                                       info->rssi, cfm_name, eir, eir_len);
+               ptr += EXTENDED_INQUIRY_INFO_SIZE;
+       }
+}
+
+static inline void remote_features_information(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_read_remote_features_complete *evt = ptr;
+       struct bt_conn *conn;
+
+       DBG("hci%d status %u", index, evt->status);
+
+       if (evt->status)
+               return;
+
+       conn = find_conn_by_handle(dev, btohs(evt->handle));
+       if (conn == NULL)
+               return;
+
+       write_features_info(&dev->bdaddr, &conn->bdaddr, evt->features, NULL);
+}
+
+struct remote_version_req {
+       int index;
+       uint16_t handle;
+};
+
+static gboolean __get_remote_version(gpointer user_data)
+{
+       struct remote_version_req *req = user_data;
+       struct dev_info *dev = &devs[req->index];
+       read_remote_version_cp cp;
+
+       DBG("hci%d handle %u", req->index, req->handle);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = htobs(req->handle);
+
+       hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_READ_REMOTE_VERSION,
+                                       READ_REMOTE_VERSION_CP_SIZE, &cp);
+
+       return FALSE;
+}
+
+static void get_remote_version(int index, uint16_t handle)
+{
+       struct remote_version_req *req;
+
+       req = g_new0(struct remote_version_req, 1);
+       req->handle = handle;
+       req->index = index;
+
+       g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1, __get_remote_version,
+                                                               req, g_free);
+}
+
+static void conn_free(struct bt_conn *conn)
+{
+       if (conn->io != NULL) {
+               g_io_channel_shutdown(conn->io, TRUE, NULL);
+               g_io_channel_unref(conn->io);
+       }
+
+       g_free(conn);
+}
+
+static inline void conn_failed(int index, bdaddr_t *bdaddr, uint8_t status)
+{
+       struct dev_info *dev = &devs[index];
+       struct bt_conn *conn;
+
+       btd_event_conn_failed(&dev->bdaddr, bdaddr, status);
+
+       conn = find_connection(dev, bdaddr);
+       if (conn == NULL)
+               return;
+
+       bonding_complete(dev, conn, status);
+
+       dev->connections = g_slist_remove(dev->connections, conn);
+       conn_free(conn);
+}
+
+static inline void conn_complete(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_conn_complete *evt = ptr;
+       char filename[PATH_MAX];
+       char local_addr[18], peer_addr[18], *str;
+       struct bt_conn *conn;
+
+       if (evt->link_type != ACL_LINK)
+               return;
+
+       DBG("status 0x%02x", evt->status);
+
+       if (evt->status != 0) {
+               conn_failed(index, &evt->bdaddr, evt->status);
+               return;
+       }
+
+       conn = get_connection(dev, &evt->bdaddr);
+       conn->handle = btohs(evt->handle);
+
+       btd_event_conn_complete(&dev->bdaddr, &evt->bdaddr, BDADDR_BREDR,
+                                                               NULL, NULL);
+
+       if (conn->secmode3)
+               bonding_complete(dev, conn, 0);
+
+       /* check if the remote version needs be requested */
+       ba2str(&dev->bdaddr, local_addr);
+       ba2str(&evt->bdaddr, peer_addr);
+
+       create_name(filename, sizeof(filename), STORAGEDIR, local_addr,
+                                                       "manufacturers");
+
+       str = textfile_get(filename, peer_addr);
+       if (!str)
+               get_remote_version(index, btohs(evt->handle));
+       else
+               free(str);
+}
+
+static inline uint8_t le_addr_type(uint8_t bdaddr_type)
+{
+       switch (bdaddr_type) {
+       case LE_RANDOM_ADDRESS:
+               return BDADDR_LE_RANDOM;
+       case LE_PUBLIC_ADDRESS:
+       default:
+               return BDADDR_LE_PUBLIC;
+       }
+}
+
+static inline void le_conn_complete(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_le_connection_complete *evt = ptr;
+       char filename[PATH_MAX];
+       char local_addr[18], peer_addr[18], *str;
+       struct bt_conn *conn;
+       uint8_t bdaddr_type;
+
+       if (evt->status) {
+               btd_event_conn_failed(&dev->bdaddr, &evt->peer_bdaddr,
+                                                               evt->status);
+               return;
+       }
+
+       conn = get_connection(dev, &evt->peer_bdaddr);
+       conn->handle = btohs(evt->handle);
+
+       bdaddr_type = le_addr_type(evt->peer_bdaddr_type);
+       btd_event_conn_complete(&dev->bdaddr, &evt->peer_bdaddr, bdaddr_type,
+                                                               NULL, NULL);
+
+       /* check if the remote version needs be requested */
+       ba2str(&dev->bdaddr, local_addr);
+       ba2str(&evt->peer_bdaddr, peer_addr);
+
+       create_name(filename, sizeof(filename), STORAGEDIR, local_addr,
+                                                       "manufacturers");
+
+       str = textfile_get(filename, peer_addr);
+       if (!str)
+               get_remote_version(index, btohs(evt->handle));
+       else
+               free(str);
+}
+
+static inline void disconn_complete(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_disconn_complete *evt = ptr;
+       struct bt_conn *conn;
+
+       DBG("handle %u status 0x%02x", btohs(evt->handle), evt->status);
+
+       if (evt->status != 0)
+               return;
+
+       conn = find_conn_by_handle(dev, btohs(evt->handle));
+       if (conn == NULL)
+               return;
+
+       dev->connections = g_slist_remove(dev->connections, conn);
+
+       btd_event_disconn_complete(&dev->bdaddr, &conn->bdaddr);
+
+       conn_free(conn);
+}
+
+static inline void auth_complete(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_auth_complete *evt = ptr;
+       struct bt_conn *conn;
+
+       DBG("hci%d status %u", index, evt->status);
+
+       conn = find_conn_by_handle(dev, btohs(evt->handle));
+       if (conn == NULL)
+               return;
+
+       bonding_complete(dev, conn, evt->status);
+}
+
+static inline void simple_pairing_complete(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_simple_pairing_complete *evt = ptr;
+
+       DBG("hci%d status %u", index, evt->status);
+
+       btd_event_simple_pairing_complete(&dev->bdaddr, &evt->bdaddr,
+                                                               evt->status);
+}
+
+static inline void conn_request(int index, void *ptr)
+{
+       struct dev_info *dev = &devs[index];
+       evt_conn_request *evt = ptr;
+       uint32_t class = evt->dev_class[0] | (evt->dev_class[1] << 8)
+                               | (evt->dev_class[2] << 16);
+
+       btd_event_remote_class(&dev->bdaddr, &evt->bdaddr, class);
+}
+
+static inline void le_advertising_report(int index, evt_le_meta_event *meta)
+{
+       struct dev_info *dev = &devs[index];
+       le_advertising_info *info;
+       uint8_t num_reports, rssi;
+       const uint8_t RSSI_SIZE = 1;
+
+       num_reports = meta->data[0];
+
+       info = (le_advertising_info *) &meta->data[1];
+       rssi = *(info->data + info->length);
+
+       dev_found(dev, &info->bdaddr, le_addr_type(info->bdaddr_type), NULL,
+                                       rssi, 0, info->data, info->length);
+
+       num_reports--;
+
+       while (num_reports--) {
+               info = (le_advertising_info *) (info->data + info->length +
+                                                               RSSI_SIZE);
+               rssi = *(info->data + info->length);
+
+               dev_found(dev, &info->bdaddr, le_addr_type(info->bdaddr_type),
+                               NULL, rssi, 0, info->data, info->length);
+       }
+}
+
+static inline void le_metaevent(int index, void *ptr)
+{
+       evt_le_meta_event *meta = ptr;
+
+       DBG("hci%d LE Meta Event %u", index, meta->subevent);
+
+       switch (meta->subevent) {
+       case EVT_LE_ADVERTISING_REPORT:
+               le_advertising_report(index, meta);
+               break;
+
+       case EVT_LE_CONN_COMPLETE:
+               le_conn_complete(index, meta->data);
+               break;
+       }
+}
+
+static void stop_hci_dev(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       if (dev->sk < 0)
+               return;
+
+       info("Stopping hci%d event socket", index);
+
+       if (dev->watch_id > 0)
+               g_source_remove(dev->watch_id);
+
+       if (dev->stop_scan_id > 0)
+               g_source_remove(dev->stop_scan_id);
+
+       if (dev->io != NULL)
+               g_io_channel_unref(dev->io);
+
+       hci_close_dev(dev->sk);
+
+       g_slist_free_full(dev->keys, g_free);
+       g_slist_free_full(dev->uuids, g_free);
+       g_slist_free_full(dev->connections, g_free);
+
+       init_dev_info(index, -1, dev->registered, dev->already_up);
+}
+
+static gboolean io_security_event(GIOChannel *chan, GIOCondition cond,
+                                                               gpointer data)
+{
+       unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf;
+       int type, index = GPOINTER_TO_INT(data);
+       struct dev_info *dev = &devs[index];
+       struct hci_dev_info di;
+       ssize_t len;
+       hci_event_hdr *eh;
+       evt_cmd_status *evt;
+       int fd;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+               stop_hci_dev(index);
+               return FALSE;
+       }
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       len = read(fd, buf, sizeof(buf));
+       if (len < 0) {
+               if (errno == EAGAIN)
+                       return TRUE;
+               stop_hci_dev(index);
+               return FALSE;
+       }
+
+       type = *ptr++;
+
+       if (type != HCI_EVENT_PKT)
+               return TRUE;
+
+       eh = (hci_event_hdr *) ptr;
+       ptr += HCI_EVENT_HDR_SIZE;
+
+       memset(&di, 0, sizeof(di));
+       if (hci_devinfo(index, &di) == 0) {
+               bacpy(&dev->bdaddr, &di.bdaddr);
+
+               if (ignore_device(&di))
+                       return TRUE;
+       }
+
+       switch (eh->evt) {
+       case EVT_CMD_STATUS:
+               cmd_status(index, ptr);
+               break;
+
+       case EVT_CMD_COMPLETE:
+               cmd_complete(index, ptr);
+               break;
+
+       case EVT_REMOTE_NAME_REQ_COMPLETE:
+               remote_name_information(index, ptr);
+               break;
+
+       case EVT_READ_REMOTE_VERSION_COMPLETE:
+               remote_version_information(index, ptr);
+               break;
+
+       case EVT_READ_REMOTE_FEATURES_COMPLETE:
+               remote_features_information(index, ptr);
+               break;
+
+       case EVT_REMOTE_HOST_FEATURES_NOTIFY:
+               remote_features_notify(index, ptr);
+               break;
+
+       case EVT_INQUIRY_COMPLETE:
+               evt = (evt_cmd_status *) ptr;
+               inquiry_complete_evt(index, evt->status);
+               break;
+
+       case EVT_INQUIRY_RESULT:
+               inquiry_result(index, eh->plen, ptr);
+               break;
+
+       case EVT_INQUIRY_RESULT_WITH_RSSI:
+               inquiry_result_with_rssi(index, eh->plen, ptr);
+               break;
+
+       case EVT_EXTENDED_INQUIRY_RESULT:
+               extended_inquiry_result(index, eh->plen, ptr);
+               break;
+
+       case EVT_CONN_COMPLETE:
+               conn_complete(index, ptr);
+               break;
+
+       case EVT_DISCONN_COMPLETE:
+               disconn_complete(index, ptr);
+               break;
+
+       case EVT_AUTH_COMPLETE:
+               auth_complete(index, ptr);
+               break;
+
+       case EVT_SIMPLE_PAIRING_COMPLETE:
+               simple_pairing_complete(index, ptr);
+               break;
+
+       case EVT_CONN_REQUEST:
+               conn_request(index, ptr);
+               break;
+       case EVT_LE_META_EVENT:
+               le_metaevent(index, ptr);
+               break;
+       case EVT_PIN_CODE_REQ:
+               pin_code_request(index, (bdaddr_t *) ptr);
+               break;
+
+       case EVT_LINK_KEY_REQ:
+               link_key_request(index, (bdaddr_t *) ptr);
+               break;
+
+       case EVT_LINK_KEY_NOTIFY:
+               link_key_notify(index, ptr);
+               break;
+
+       case EVT_RETURN_LINK_KEYS:
+               return_link_keys(index, ptr);
+               break;
+
+       case EVT_IO_CAPABILITY_REQUEST:
+               io_capa_request(index, ptr);
+               break;
+
+       case EVT_IO_CAPABILITY_RESPONSE:
+               io_capa_response(index, ptr);
+               break;
+
+       case EVT_USER_CONFIRM_REQUEST:
+               user_confirm_request(index, ptr);
+               break;
+
+       case EVT_USER_PASSKEY_REQUEST:
+               user_passkey_request(index, ptr);
+               break;
+
+       case EVT_USER_PASSKEY_NOTIFY:
+               user_passkey_notify(index, ptr);
+               break;
+
+       case EVT_REMOTE_OOB_DATA_REQUEST:
+               remote_oob_data_request(index, (bdaddr_t *) ptr);
+               break;
+       }
+
+       return TRUE;
+}
+
+static void start_hci_dev(int index)
+{
+       struct dev_info *dev = &devs[index];
+       GIOChannel *chan = dev->io;
+       GIOCondition cond;
+       struct hci_filter flt;
+
+       if (chan)
+               return;
+
+       info("Listening for HCI events on hci%d", index);
+
+       /* Set filter */
+       hci_filter_clear(&flt);
+       hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+       hci_filter_set_event(EVT_CMD_STATUS, &flt);
+       hci_filter_set_event(EVT_CMD_COMPLETE, &flt);
+       hci_filter_set_event(EVT_PIN_CODE_REQ, &flt);
+       hci_filter_set_event(EVT_LINK_KEY_REQ, &flt);
+       hci_filter_set_event(EVT_LINK_KEY_NOTIFY, &flt);
+       hci_filter_set_event(EVT_RETURN_LINK_KEYS, &flt);
+       hci_filter_set_event(EVT_IO_CAPABILITY_REQUEST, &flt);
+       hci_filter_set_event(EVT_IO_CAPABILITY_RESPONSE, &flt);
+       hci_filter_set_event(EVT_USER_CONFIRM_REQUEST, &flt);
+       hci_filter_set_event(EVT_USER_PASSKEY_REQUEST, &flt);
+       hci_filter_set_event(EVT_REMOTE_OOB_DATA_REQUEST, &flt);
+       hci_filter_set_event(EVT_USER_PASSKEY_NOTIFY, &flt);
+       hci_filter_set_event(EVT_KEYPRESS_NOTIFY, &flt);
+       hci_filter_set_event(EVT_SIMPLE_PAIRING_COMPLETE, &flt);
+       hci_filter_set_event(EVT_AUTH_COMPLETE, &flt);
+       hci_filter_set_event(EVT_REMOTE_NAME_REQ_COMPLETE, &flt);
+       hci_filter_set_event(EVT_READ_REMOTE_VERSION_COMPLETE, &flt);
+       hci_filter_set_event(EVT_READ_REMOTE_FEATURES_COMPLETE, &flt);
+       hci_filter_set_event(EVT_REMOTE_HOST_FEATURES_NOTIFY, &flt);
+       hci_filter_set_event(EVT_INQUIRY_COMPLETE, &flt);
+       hci_filter_set_event(EVT_INQUIRY_RESULT, &flt);
+       hci_filter_set_event(EVT_INQUIRY_RESULT_WITH_RSSI, &flt);
+       hci_filter_set_event(EVT_EXTENDED_INQUIRY_RESULT, &flt);
+       hci_filter_set_event(EVT_CONN_REQUEST, &flt);
+       hci_filter_set_event(EVT_CONN_COMPLETE, &flt);
+       hci_filter_set_event(EVT_DISCONN_COMPLETE, &flt);
+       hci_filter_set_event(EVT_LE_META_EVENT, &flt);
+       if (setsockopt(dev->sk, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+               error("Can't set filter on hci%d: %s (%d)",
+                                               index, strerror(errno), errno);
+               return;
+       }
+
+       chan = g_io_channel_unix_new(dev->sk);
+       cond = G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR;
+       dev->watch_id = g_io_add_watch_full(chan, G_PRIORITY_LOW, cond,
+                                               io_security_event,
+                                               GINT_TO_POINTER(index), NULL);
+       dev->io = chan;
+       dev->pin_length = 0;
+
+}
+
+/* End of HCI event callbacks */
+
+static gboolean child_exit(GIOChannel *io, GIOCondition cond, void *user_data)
+{
+       int status, fd = g_io_channel_unix_get_fd(io);
+       pid_t child_pid;
+
+       if (read(fd, &child_pid, sizeof(child_pid)) != sizeof(child_pid)) {
+               error("child_exit: unable to read child pid from pipe");
+               return TRUE;
+       }
+
+       if (waitpid(child_pid, &status, 0) != child_pid)
+               error("waitpid(%d) failed", child_pid);
+       else
+               DBG("child %d exited", child_pid);
+
+       return TRUE;
+}
+
+static void at_child_exit(void)
+{
+       pid_t pid = getpid();
+
+       if (write(child_pipe[1], &pid, sizeof(pid)) != sizeof(pid))
+               error("unable to write to child pipe");
+}
+
+static void device_devup_setup(int index)
+{
+       struct dev_info *dev = &devs[index];
+       struct hci_dev_info di;
+       read_stored_link_key_cp cp;
+
+       DBG("hci%d", index);
+
+       if (hci_devinfo(index, &di) < 0)
+               return;
+
+       if (ignore_device(&di))
+               return;
+
+       bacpy(&dev->bdaddr, &di.bdaddr);
+       memcpy(dev->features, di.features, 8);
+
+       if (dev->features[7] & LMP_EXT_FEAT) {
+               uint8_t page_num = 0x01;
+
+               hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+                               OCF_READ_LOCAL_EXT_FEATURES, 1, &page_num);
+       }
+
+       /* Set page timeout */
+       if ((main_opts.flags & (1 << HCID_SET_PAGETO))) {
+               write_page_timeout_cp cp;
+
+               cp.timeout = htobs(main_opts.pageto);
+               hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_PAGE_TIMEOUT,
+                                       WRITE_PAGE_TIMEOUT_CP_SIZE, &cp);
+       }
+
+       bacpy(&cp.bdaddr, BDADDR_ANY);
+       cp.read_all = 1;
+       hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_STORED_LINK_KEY,
+                                       READ_STORED_LINK_KEY_CP_SIZE, &cp);
+
+       if (!dev->pending) {
+               init_adapter(index);
+               return;
+       }
+
+       /* Even though it shouldn't happen (assuming the kernel behaves
+        * properly) it seems like we might miss the very first
+        * initialization commands that the kernel sends. So check for
+        * it here and resend the ones we haven't seen their results yet */
+
+       if (hci_test_bit(PENDING_FEATURES, &dev->pending))
+               hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+                                       OCF_READ_LOCAL_FEATURES, 0, NULL);
+
+       if (hci_test_bit(PENDING_VERSION, &dev->pending))
+               hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+                                       OCF_READ_LOCAL_VERSION, 0, NULL);
+
+       if (hci_test_bit(PENDING_NAME, &dev->pending))
+               hci_send_cmd(dev->sk, OGF_HOST_CTL,
+                                       OCF_READ_LOCAL_NAME, 0, 0);
+
+       if (hci_test_bit(PENDING_BDADDR, &dev->pending))
+               hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+                                       OCF_READ_BD_ADDR, 0, NULL);
+}
+
+static void init_pending(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       hci_set_bit(PENDING_BDADDR, &dev->pending);
+       hci_set_bit(PENDING_VERSION, &dev->pending);
+       hci_set_bit(PENDING_FEATURES, &dev->pending);
+       hci_set_bit(PENDING_NAME, &dev->pending);
+}
+
+static struct dev_info *init_device(int index, gboolean already_up)
+{
+       struct dev_info *dev;
+       struct hci_dev_req dr;
+       int dd;
+       pid_t pid;
+
+       DBG("hci%d", index);
+
+       dd = hci_open_dev(index);
+       if (dd < 0) {
+               error("Unable to open hci%d: %s (%d)", index,
+                                               strerror(errno), errno);
+               return NULL;
+       }
+
+       if (index > max_dev) {
+               max_dev = index;
+               devs = g_realloc(devs, sizeof(devs[0]) * (max_dev + 1));
+       }
+
+       dev = init_dev_info(index, dd, FALSE, already_up);
+       init_pending(index);
+       start_hci_dev(index);
+
+       /* Avoid forking if nothing else has to be done */
+       if (already_up)
+               return dev;
+
+       /* Do initialization in the separate process */
+       pid = fork();
+       switch (pid) {
+               case 0:
+                       atexit(at_child_exit);
+                       break;
+               case -1:
+                       error("Fork failed. Can't init device hci%d: %s (%d)",
+                                       index, strerror(errno), errno);
+               default:
+                       DBG("child %d forked", pid);
+                       return dev;
+       }
+
+       memset(&dr, 0, sizeof(dr));
+       dr.dev_id = index;
+
+       /* Set link mode */
+       dr.dev_opt = main_opts.link_mode;
+       if (ioctl(dd, HCISETLINKMODE, (unsigned long) &dr) < 0)
+               error("Can't set link mode on hci%d: %s (%d)",
+                                               index, strerror(errno), errno);
+
+       /* Start HCI device */
+       if (ioctl(dd, HCIDEVUP, index) < 0 && errno != EALREADY) {
+               error("Can't init device hci%d: %s (%d)",
+                                       index, strerror(errno), errno);
+               goto fail;
+       }
+
+       hci_close_dev(dd);
+       exit(0);
+
+fail:
+       hci_close_dev(dd);
+       exit(1);
+}
+
+static void init_conn_list(int index)
+{
+       struct dev_info *dev = &devs[index];
+       struct hci_conn_list_req *cl;
+       struct hci_conn_info *ci;
+       int i;
+
+       DBG("hci%d", index);
+
+       cl = g_malloc0(10 * sizeof(*ci) + sizeof(*cl));
+
+       cl->dev_id = index;
+       cl->conn_num = 10;
+       ci = cl->conn_info;
+
+       if (ioctl(dev->sk, HCIGETCONNLIST, cl) < 0) {
+               error("Unable to get connection list: %s (%d)",
+                                               strerror(errno), errno);
+               goto failed;
+       }
+
+       for (i = 0; i < cl->conn_num; i++, ci++) {
+               struct bt_conn *conn;
+
+               if (ci->type != ACL_LINK)
+                       continue;
+
+               conn = get_connection(dev, &ci->bdaddr);
+               conn->handle = ci->handle;
+       }
+
+failed:
+       g_free(cl);
+}
+
+static void device_event(int event, int index)
+{
+       switch (event) {
+       case HCI_DEV_REG:
+               info("HCI dev %d registered", index);
+               init_device(index, FALSE);
+               break;
+
+       case HCI_DEV_UNREG:
+               info("HCI dev %d unregistered", index);
+               stop_hci_dev(index);
+               if (devs[index].registered)
+                       btd_manager_unregister_adapter(index);
+               break;
+
+       case HCI_DEV_UP:
+               info("HCI dev %d up", index);
+               devs[index].up = TRUE;
+               device_devup_setup(index);
+               break;
+
+       case HCI_DEV_DOWN:
+               info("HCI dev %d down", index);
+               devs[index].up = FALSE;
+               devs[index].pending_cod = 0;
+               devs[index].cache_enable = TRUE;
+               devs[index].discov_state = DISCOV_HALTED;
+               reset_discoverable_timeout(index);
+               if (!devs[index].pending) {
+                       struct btd_adapter *adapter;
+
+                       adapter = manager_find_adapter_by_id(index);
+                       if (adapter)
+                               btd_adapter_stop(adapter);
+
+                       init_pending(index);
+               }
+               break;
+       }
+}
+
+static gboolean init_known_adapters(gpointer user_data)
+{
+       struct hci_dev_list_req *dl;
+       struct hci_dev_req *dr;
+       int i, err, ctl = GPOINTER_TO_INT(user_data);
+       size_t req_size;
+
+       DBG("");
+
+       req_size = HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t);
+
+       dl = g_try_malloc0(req_size);
+       if (!dl) {
+               error("Can't allocate devlist buffer");
+               return FALSE;
+       }
+
+       dl->dev_num = HCI_MAX_DEV;
+       dr = dl->dev_req;
+
+       if (ioctl(ctl, HCIGETDEVLIST, dl) < 0) {
+               err = -errno;
+               error("Can't get device list: %s (%d)", strerror(-err), -err);
+               g_free(dl);
+               return FALSE;
+       }
+
+       for (i = 0; i < dl->dev_num; i++, dr++) {
+               struct dev_info *dev;
+               gboolean already_up;
+
+               already_up = hci_test_bit(HCI_UP, &dr->dev_opt);
+
+               dev = init_device(dr->dev_id, already_up);
+               if (dev == NULL)
+                       continue;
+
+               if (!dev->already_up)
+                       continue;
+
+               init_conn_list(dr->dev_id);
+
+               dev->pending = 0;
+               hci_set_bit(PENDING_VERSION, &dev->pending);
+               hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+                                       OCF_READ_LOCAL_VERSION, 0, NULL);
+               device_event(HCI_DEV_UP, dr->dev_id);
+       }
+
+       g_free(dl);
+
+       return FALSE;
+}
+
+static gboolean io_stack_event(GIOChannel *chan, GIOCondition cond,
+                                                               gpointer data)
+{
+       unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
+       evt_stack_internal *si;
+       evt_si_device *sd;
+       hci_event_hdr *eh;
+       int type, fd;
+       ssize_t len;
+
+       ptr = buf;
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       len = read(fd, buf, sizeof(buf));
+       if (len < 0) {
+               if (errno == EAGAIN)
+                       return TRUE;
+
+               error("Read from control socket failed: %s (%d)",
+                                               strerror(errno), errno);
+               return FALSE;
+       }
+
+       type = *ptr++;
+
+       if (type != HCI_EVENT_PKT)
+               return TRUE;
+
+       eh = (hci_event_hdr *) ptr;
+       if (eh->evt != EVT_STACK_INTERNAL)
+               return TRUE;
+
+       ptr += HCI_EVENT_HDR_SIZE;
+
+       si = (evt_stack_internal *) ptr;
+       switch (si->type) {
+       case EVT_SI_DEVICE:
+               sd = (void *) &si->data;
+               device_event(sd->event, sd->dev_id);
+               break;
+       }
+
+       return TRUE;
+}
+
+static int hciops_setup(void)
+{
+       struct sockaddr_hci addr;
+       struct hci_filter flt;
+       GIOChannel *ctl_io, *child_io;
+       int sock, err;
+
+       DBG("");
+
+       if (child_pipe[0] != -1)
+               return -EALREADY;
+
+       if (pipe(child_pipe) < 0) {
+               err = -errno;
+               error("pipe(): %s (%d)", strerror(-err), -err);
+               return err;
+       }
+
+       child_io = g_io_channel_unix_new(child_pipe[0]);
+       g_io_channel_set_close_on_unref(child_io, TRUE);
+       child_io_id = g_io_add_watch(child_io,
+                               G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                               child_exit, NULL);
+       g_io_channel_unref(child_io);
+
+       /* Create and bind HCI socket */
+       sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       if (sock < 0) {
+               err = -errno;
+               error("Can't open HCI socket: %s (%d)", strerror(-err),
+                                                               -err);
+               return err;
+       }
+
+       /* Set filter */
+       hci_filter_clear(&flt);
+       hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+       hci_filter_set_event(EVT_STACK_INTERNAL, &flt);
+       if (setsockopt(sock, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+               err = -errno;
+               error("Can't set filter: %s (%d)", strerror(-err), -err);
+               return err;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.hci_family = AF_BLUETOOTH;
+       addr.hci_dev = HCI_DEV_NONE;
+       if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               err = -errno;
+               error("Can't bind HCI socket: %s (%d)", strerror(-err), -err);
+               return err;
+       }
+
+       ctl_io = g_io_channel_unix_new(sock);
+       g_io_channel_set_close_on_unref(ctl_io, TRUE);
+
+       ctl_io_id = g_io_add_watch(ctl_io, G_IO_IN, io_stack_event, NULL);
+
+       g_io_channel_unref(ctl_io);
+
+       g_idle_add(init_known_adapters, GINT_TO_POINTER(sock));
+
+       return 0;
+}
+
+static void hciops_cleanup(void)
+{
+       int i;
+
+       DBG("");
+
+       for (i = 0; i <= max_dev; i++)
+               stop_hci_dev(i);
+
+       g_free(devs);
+       devs = NULL;
+       max_dev = -1;
+
+       if (child_io_id) {
+               g_source_remove(child_io_id);
+               child_io_id = 0;
+       }
+
+       if (ctl_io_id) {
+               g_source_remove(ctl_io_id);
+               ctl_io_id = 0;
+       }
+
+       if (child_pipe[0] >= 0) {
+               close(child_pipe[0]);
+               child_pipe[0] = -1;
+       }
+
+       if (child_pipe[1] >= 0) {
+               close(child_pipe[1]);
+               child_pipe[1] = -1;
+       }
+}
+
+static int hciops_set_powered(int index, gboolean powered)
+{
+       struct dev_info *dev = &devs[index];
+       int err;
+
+       DBG("hci%d powered %d", index, powered);
+
+       if (powered == FALSE)
+               return hciops_power_off(index);
+
+       if (ioctl(dev->sk, HCIDEVUP, index) == 0)
+               return 0;
+
+       if (errno == EALREADY)
+               return 0;
+
+       err = -errno;
+       error("Can't init device hci%d: %s (%d)",
+                                       index, strerror(-err), -err);
+
+       return err;
+}
+
+static int start_inquiry(int index, uint8_t length)
+{
+       struct dev_info *dev = &devs[index];
+       uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+       inquiry_cp inq_cp;
+
+       DBG("hci%d length %u", index, length);
+
+       memset(&inq_cp, 0, sizeof(inq_cp));
+       memcpy(&inq_cp.lap, lap, 3);
+       inq_cp.length = length;
+       inq_cp.num_rsp = 0x00;
+
+       if (hci_send_cmd(dev->sk, OGF_LINK_CTL,
+                       OCF_INQUIRY, INQUIRY_CP_SIZE, &inq_cp) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int le_set_scan_enable(int index, uint8_t enable)
+{
+       struct dev_info *dev = &devs[index];
+       le_set_scan_enable_cp cp;
+
+       DBG("hci%d enable %u", index, enable);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.enable = enable;
+       cp.filter_dup = 0;
+
+       if (hci_send_cmd(dev->sk, OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE,
+                               LE_SET_SCAN_ENABLE_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static gboolean stop_le_scan_cb(gpointer user_data)
+{
+       struct dev_info *dev = user_data;
+       int err;
+
+       err = le_set_scan_enable(dev->id, 0);
+       if (err < 0)
+               return TRUE;
+
+       dev->stop_scan_id = 0;
+
+       return FALSE;
+}
+
+static int start_scanning(int index, int timeout)
+{
+       struct dev_info *dev = &devs[index];
+       le_set_scan_parameters_cp cp;
+       int err;
+
+       DBG("hci%d", index);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.type = 0x01;                 /* Active scanning */
+       /* The recommended value for scan interval and window is 11.25 msec.
+        * It is calculated by: time = n * 0.625 msec */
+       cp.interval = htobs(0x0012);
+       cp.window = htobs(0x0012);
+       cp.own_bdaddr_type = 0;         /* Public address */
+       cp.filter = 0;                  /* Accept all adv packets */
+
+       if (hci_send_cmd(dev->sk, OGF_LE_CTL, OCF_LE_SET_SCAN_PARAMETERS,
+                               LE_SET_SCAN_PARAMETERS_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       err = le_set_scan_enable(index, 1);
+       if (err < 0)
+               return err;
+
+       /* Schedule a le scan disable in 'timeout' milliseconds */
+       dev->stop_scan_id = g_timeout_add(timeout, stop_le_scan_cb, dev);
+
+       return 0;
+}
+
+static int hciops_stop_scanning(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       DBG("hci%d", index);
+
+       if (dev->stop_scan_id > 0) {
+               g_source_remove(dev->stop_scan_id);
+               dev->stop_scan_id = 0;
+       }
+
+       return le_set_scan_enable(index, 0);
+}
+
+static int cancel_resolve_name(int index)
+{
+       struct dev_info *info = &devs[index];
+       struct found_dev *dev;
+       remote_name_req_cancel_cp cp;
+       struct btd_adapter *adapter;
+
+       DBG("hci%d", index);
+
+       if (g_slist_length(info->need_name) == 0)
+               return 0;
+
+       dev = info->need_name->data;
+       if (dev->name_state != NAME_PENDING)
+               return 0;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, &dev->bdaddr);
+
+       adapter = manager_find_adapter_by_id(index);
+       if (adapter)
+               adapter_set_discovering(adapter, FALSE);
+
+       found_dev_cleanup(info);
+
+       if (hci_send_cmd(info->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL,
+                               REMOTE_NAME_REQ_CANCEL_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int hciops_start_discovery(int index)
+{
+       int adapter_type = get_adapter_type(index);
+
+       DBG("hci%u", index);
+
+       switch (adapter_type) {
+       case BR_EDR_LE:
+               return start_inquiry(index, LENGTH_BR_LE_INQ);
+       case BR_EDR:
+               return start_inquiry(index, LENGTH_BR_INQ);
+       case LE_ONLY:
+               return start_scanning(index, TIMEOUT_LE_SCAN);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int hciops_stop_discovery(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       DBG("index %d", index);
+
+       switch (dev->discov_state) {
+       case DISCOV_INQ:
+               return hciops_stop_inquiry(index);
+       case DISCOV_SCAN:
+               return hciops_stop_scanning(index);
+       case DISCOV_NAMES:
+               cancel_resolve_name(index);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int hciops_set_fast_connectable(int index, gboolean enable)
+{
+       struct dev_info *dev = &devs[index];
+       write_page_activity_cp cp;
+       uint8_t type;
+
+       DBG("hci%d enable %d", index, enable);
+
+       if (enable) {
+               type = PAGE_SCAN_TYPE_INTERLACED;
+               cp.interval = 0x0024;   /* 22.5 msec page scan interval */
+       } else {
+               type = PAGE_SCAN_TYPE_STANDARD; /* default */
+               cp.interval = 0x0800;   /* default 1.28 sec page scan */
+       }
+
+       cp.window = 0x0012;     /* default 11.25 msec page scan window */
+
+       if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_PAGE_ACTIVITY,
+                                       WRITE_PAGE_ACTIVITY_CP_SIZE, &cp) < 0)
+               return -errno;
+       else if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
+                               OCF_WRITE_PAGE_SCAN_TYPE, 1, &type) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int hciops_read_clock(int index, bdaddr_t *bdaddr, int which,
+                                               int timeout, uint32_t *clock,
+                                               uint16_t *accuracy)
+{
+       struct dev_info *dev = &devs[index];
+       uint16_t handle = 0;
+       char addr[18];
+       int ret;
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d addr %s which %d timeout %d", index, addr, which, timeout);
+
+       ret = get_handle(index, bdaddr, &handle);
+       if (ret < 0)
+               return ret;
+
+       if (hci_read_clock(dev->sk, htobs(handle), which, clock, accuracy,
+                                                               timeout) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int hciops_read_bdaddr(int index, bdaddr_t *bdaddr)
+{
+       struct dev_info *dev = &devs[index];
+
+       DBG("hci%d", index);
+
+       bacpy(bdaddr, &dev->bdaddr);
+
+       return 0;
+}
+
+static int hciops_block_device(int index, bdaddr_t *bdaddr,
+                                               uint8_t bdaddr_type)
+{
+       struct dev_info *dev = &devs[index];
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d dba %s", index, addr);
+
+       if (ioctl(dev->sk, HCIBLOCKADDR, bdaddr) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int hciops_unblock_device(int index, bdaddr_t *bdaddr,
+                                               uint8_t bdaddr_type)
+{
+       struct dev_info *dev = &devs[index];
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d dba %s", index, addr);
+
+       if (ioctl(dev->sk, HCIUNBLOCKADDR, bdaddr) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int hciops_get_conn_list(int index, GSList **conns)
+{
+       struct dev_info *dev = &devs[index];
+       GSList *l;
+
+       DBG("hci%d", index);
+
+       *conns = NULL;
+
+       for (l = dev->connections; l != NULL; l = g_slist_next(l)) {
+               struct bt_conn *conn = l->data;
+
+               *conns = g_slist_append(*conns,
+                               g_memdup(&conn->bdaddr, sizeof(bdaddr_t)));
+       }
+
+       return 0;
+}
+
+static int hciops_disconnect(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+       DBG("hci%d", index);
+
+       return disconnect_addr(index, bdaddr, HCI_OE_USER_ENDED_CONNECTION);
+}
+
+static int hciops_remove_bonding(int index, bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type)
+{
+       struct dev_info *dev = &devs[index];
+       delete_stored_link_key_cp cp;
+       GSList *match;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d dba %s", index, addr);
+
+       match = g_slist_find_custom(dev->keys, bdaddr, (GCompareFunc) bacmp);
+       if (match) {
+               g_free(match->data);
+               dev->keys = g_slist_delete_link(dev->keys, match);
+       }
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+
+       /* Delete the link key from the Bluetooth chip */
+       if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_DELETE_STORED_LINK_KEY,
+                               DELETE_STORED_LINK_KEY_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int hciops_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin,
+                                                               size_t pin_len)
+{
+       struct dev_info *dev = &devs[index];
+       char addr[18];
+       int err;
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d dba %s", index, addr);
+
+       if (pin) {
+               pin_code_reply_cp pr;
+
+               dev->pin_length = pin_len;
+
+               memset(&pr, 0, sizeof(pr));
+               bacpy(&pr.bdaddr, bdaddr);
+               memcpy(pr.pin_code, pin, pin_len);
+               pr.pin_len = pin_len;
+               err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+                                               OCF_PIN_CODE_REPLY,
+                                               PIN_CODE_REPLY_CP_SIZE, &pr);
+       } else
+               err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+                                       OCF_PIN_CODE_NEG_REPLY, 6, bdaddr);
+
+       if (err < 0)
+               err = -errno;
+
+       return err;
+}
+
+static int hciops_passkey_reply(int index, bdaddr_t *bdaddr,
+                                       uint8_t bdaddr_type, uint32_t passkey)
+{
+       struct dev_info *dev = &devs[index];
+       char addr[18];
+       int err;
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d dba %s", index, addr);
+
+       if (passkey != INVALID_PASSKEY) {
+               user_passkey_reply_cp cp;
+
+               memset(&cp, 0, sizeof(cp));
+               bacpy(&cp.bdaddr, bdaddr);
+               cp.passkey = passkey;
+
+               err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+                                       OCF_USER_PASSKEY_REPLY,
+                                       USER_PASSKEY_REPLY_CP_SIZE, &cp);
+       } else
+               err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
+                                       OCF_USER_PASSKEY_NEG_REPLY, 6, bdaddr);
+
+       if (err < 0)
+               err = -errno;
+
+       return err;
+}
+
+static uint8_t generate_service_class(int index)
+{
+       struct dev_info *dev = &devs[index];
+       GSList *l;
+       uint8_t val = 0;
+
+       for (l = dev->uuids; l != NULL; l = g_slist_next(l)) {
+               struct uuid_info *uuid = l->data;
+
+               val |= uuid->svc_hint;
+       }
+
+       return val;
+}
+
+static int update_service_classes(int index)
+{
+       struct dev_info *dev = &devs[index];
+       uint8_t value;
+       int err;
+
+       value = generate_service_class(index);
+
+       DBG("hci%d value %u", index, value);
+
+       /* Update only the service class, keep the limited bit,
+        * major/minor class bits intact */
+       dev->wanted_cod &= 0x00ffff;
+       dev->wanted_cod |= (value << 16);
+
+       /* If the cache is enabled or an existing CoD write is in progress
+        * just bail out */
+       if (dev->cache_enable || dev->pending_cod)
+               return 0;
+
+       /* If we already have the CoD we want, update EIR and return */
+       if (dev->current_cod == dev->wanted_cod) {
+               update_ext_inquiry_response(index);
+               return 0;
+       }
+
+       DBG("Changing service classes to 0x%06x", dev->wanted_cod);
+
+       err = write_class(index, dev->wanted_cod);
+       if (err < 0)
+               error("Adapter class update failed: %s (%d)",
+                                               strerror(-err), -err);
+
+       return err;
+}
+
+static int hciops_add_uuid(int index, uuid_t *uuid, uint8_t svc_hint)
+{
+       struct dev_info *dev = &devs[index];
+       struct uuid_info *info;
+
+       DBG("hci%d", index);
+
+       info = g_new0(struct uuid_info, 1);
+       memcpy(&info->uuid, uuid, sizeof(*uuid));
+       info->svc_hint = svc_hint;
+
+       dev->uuids = g_slist_append(dev->uuids, info);
+
+       return update_service_classes(index);
+}
+
+static int hciops_remove_uuid(int index, uuid_t *uuid)
+{
+       struct dev_info *dev = &devs[index];
+       GSList *match;
+
+       match = g_slist_find_custom(dev->uuids, uuid, sdp_uuid_cmp);
+       if (match) {
+               g_free(match->data);
+               dev->uuids = g_slist_delete_link(dev->uuids, match);
+       }
+
+       DBG("hci%d", index);
+
+       return update_service_classes(index);
+}
+
+static int hciops_disable_cod_cache(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       DBG("hci%d cache_enable %d", index, dev->cache_enable);
+
+       if (!dev->cache_enable)
+               return 0;
+
+       DBG("hci%d current_cod 0x%06x wanted_cod 0x%06x", index,
+                                       dev->current_cod, dev->wanted_cod);
+
+       /* Disable and flush svc cache. All successive service class
+        * updates * will be written to the device */
+       dev->cache_enable = FALSE;
+
+       if (dev->current_cod == dev->wanted_cod) {
+               update_ext_inquiry_response(index);
+               return 0;
+       }
+
+       return write_class(index, dev->wanted_cod);
+}
+
+static int hciops_restore_powered(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       if (!dev->already_up && dev->up)
+               return hciops_power_off(index);
+
+       return 0;
+}
+
+static int hciops_load_keys(int index, GSList *keys, gboolean debug_keys)
+{
+       struct dev_info *dev = &devs[index];
+       GSList *l;
+
+       DBG("hci%d keys %d debug_keys %d", index, g_slist_length(keys),
+                                                               debug_keys);
+
+       if (dev->keys != NULL)
+               return -EEXIST;
+
+       for (l = keys; l; l = l->next) {
+               struct link_key_info *orig, *dup;
+
+               orig = l->data;
+
+               dup = g_memdup(orig, sizeof(*orig));
+
+               dev->keys = g_slist_prepend(dev->keys, dup);
+       }
+
+       dev->debug_keys = debug_keys;
+
+       return 0;
+}
+
+static int hciops_set_io_capability(int index, uint8_t io_capability)
+{
+       struct dev_info *dev = &devs[index];
+
+       /* hciops is not to be used for SMP pairing for LE devices. So
+        * change the IO capability from KeyboardDisplay to DisplayYesNo
+        * in case it is set. */
+       dev->io_capability = (io_capability == 0x04) ? 0x01 : io_capability;
+
+       return 0;
+}
+
+static int request_authentication(int index, bdaddr_t *bdaddr)
+{
+       struct dev_info *dev = &devs[index];
+       auth_requested_cp cp;
+       uint16_t handle;
+       int err;
+
+       DBG("hci%d", index);
+
+       err = get_handle(index, bdaddr, &handle);
+       if (err < 0)
+               return err;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = htobs(handle);
+
+       if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
+                                       AUTH_REQUESTED_CP_SIZE, &cp) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static void bonding_connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+       struct bt_conn *conn = user_data;
+       struct dev_info *dev = conn->dev;
+
+       if (!conn->io) {
+               if (!err)
+                       g_io_channel_shutdown(io, TRUE, NULL);
+               return;
+       }
+
+       if (err)
+               /* Wait proper error to be propagated by bonding complete */
+               return;
+
+       if (request_authentication(dev->id, &conn->bdaddr) < 0)
+               goto failed;
+
+       return;
+
+failed:
+       bonding_complete(dev, conn, HCI_UNSPECIFIED_ERROR);
+}
+
+static int hciops_create_bonding(int index, bdaddr_t *bdaddr,
+                                       uint8_t bdaddr_type, uint8_t io_cap)
+{
+       struct dev_info *dev = &devs[index];
+       BtIOSecLevel sec_level;
+       struct bt_conn *conn;
+       GError *err = NULL;
+
+       conn = get_connection(dev, bdaddr);
+
+       if (conn->io != NULL)
+               return -EBUSY;
+
+       /* hciops is not to be used for SMP pairing for LE devices. So
+        * change the IO capability from KeyboardDisplay to DisplayYesNo
+        * in case it is set. */
+       conn->loc_cap = (io_cap == 0x04 ? 0x01 : io_cap);
+
+       /* If our IO capability is NoInputNoOutput use medium security
+        * level (i.e. don't require MITM protection) else use high
+        * security level */
+       if (io_cap == 0x03)
+               sec_level = BT_IO_SEC_MEDIUM;
+       else
+               sec_level = BT_IO_SEC_HIGH;
+
+       conn->io = bt_io_connect(BT_IO_L2RAW, bonding_connect_cb, conn,
+                                       NULL, &err,
+                                       BT_IO_OPT_SOURCE_BDADDR, &dev->bdaddr,
+                                       BT_IO_OPT_DEST_BDADDR, bdaddr,
+                                       BT_IO_OPT_SEC_LEVEL, sec_level,
+                                       BT_IO_OPT_INVALID);
+       if (conn->io == NULL) {
+               error("bt_io_connect: %s", err->message);
+               g_error_free(err);
+               return -EIO;
+       }
+
+       conn->bonding_initiator = TRUE;
+
+       return 0;
+}
+
+static int hciops_cancel_bonding(int index, bdaddr_t *bdaddr)
+{
+       struct dev_info *dev = &devs[index];
+       struct bt_conn *conn;
+
+       DBG("hci%d", index);
+
+       conn = find_connection(dev, bdaddr);
+       if (conn == NULL || conn->io == NULL)
+               return -ENOTCONN;
+
+       g_io_channel_shutdown(conn->io, TRUE, NULL);
+       g_io_channel_unref(conn->io);
+       conn->io = NULL;
+
+       return 0;
+}
+
+static int hciops_read_local_oob_data(int index)
+{
+       struct dev_info *dev = &devs[index];
+
+       DBG("hci%d", index);
+
+       if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_LOCAL_OOB_DATA, 0, 0)
+                                                                       < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int hciops_add_remote_oob_data(int index, bdaddr_t *bdaddr,
+                                       uint8_t *hash, uint8_t *randomizer)
+{
+       char addr[18];
+       struct dev_info *dev = &devs[index];
+       GSList *match;
+       struct oob_data *data;
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d bdaddr %s", index, addr);
+
+       match = g_slist_find_custom(dev->oob_data, bdaddr, oob_bdaddr_cmp);
+
+       if (match) {
+               data = match->data;
+       } else {
+               data = g_new(struct oob_data, 1);
+               bacpy(&data->bdaddr, bdaddr);
+               dev->oob_data = g_slist_prepend(dev->oob_data, data);
+       }
+
+       memcpy(data->hash, hash, sizeof(data->hash));
+       memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
+
+       return 0;
+}
+
+static int hciops_remove_remote_oob_data(int index, bdaddr_t *bdaddr)
+{
+       char addr[18];
+       struct dev_info *dev = &devs[index];
+       GSList *match;
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d bdaddr %s", index, addr);
+
+       match = g_slist_find_custom(dev->oob_data, bdaddr, oob_bdaddr_cmp);
+
+       if (!match)
+               return -ENOENT;
+
+       g_free(match->data);
+       dev->oob_data = g_slist_delete_link(dev->oob_data, match);
+
+       return 0;
+}
+
+static int hciops_confirm_name(int index, bdaddr_t *bdaddr,
+                               uint8_t bdaddr_type, gboolean name_known)
+{
+       struct dev_info *info = &devs[index];
+       struct found_dev *dev;
+       GSList *match;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%u %s name_known %u", index, addr, name_known);
+
+       match = g_slist_find_custom(info->found_devs, bdaddr,
+                                               found_dev_bda_cmp);
+       if (match == NULL)
+               return -ENOENT;
+
+       dev = match->data;
+
+       if (name_known) {
+               dev->name_state = NAME_NOT_NEEDED;
+               info->found_devs = g_slist_sort(info->found_devs,
+                                                       found_dev_rssi_cmp);
+               return 0;
+       }
+
+       dev->name_state = NAME_NEEDED;
+       info->found_devs = g_slist_remove_link(info->found_devs, match);
+
+       match->next = info->need_name;
+       info->need_name = match;
+       info->need_name = g_slist_sort(info->need_name, found_dev_rssi_cmp);
+
+       return 0;
+}
+
+static int hciops_load_ltks(int index, GSList *keys)
+{
+       return -ENOSYS;
+}
+
+static struct btd_adapter_ops hci_ops = {
+       .setup = hciops_setup,
+       .cleanup = hciops_cleanup,
+       .set_powered = hciops_set_powered,
+       .set_discoverable = hciops_set_discoverable,
+       .set_pairable = hciops_set_pairable,
+       .start_discovery = hciops_start_discovery,
+       .stop_discovery = hciops_stop_discovery,
+       .set_name = hciops_set_name,
+       .set_dev_class = hciops_set_dev_class,
+       .set_fast_connectable = hciops_set_fast_connectable,
+       .read_clock = hciops_read_clock,
+       .read_bdaddr = hciops_read_bdaddr,
+       .block_device = hciops_block_device,
+       .unblock_device = hciops_unblock_device,
+       .get_conn_list = hciops_get_conn_list,
+       .disconnect = hciops_disconnect,
+       .remove_bonding = hciops_remove_bonding,
+       .pincode_reply = hciops_pincode_reply,
+       .confirm_reply = hciops_confirm_reply,
+       .passkey_reply = hciops_passkey_reply,
+       .encrypt_link = hciops_encrypt_link,
+       .set_did = hciops_set_did,
+       .add_uuid = hciops_add_uuid,
+       .remove_uuid = hciops_remove_uuid,
+       .disable_cod_cache = hciops_disable_cod_cache,
+       .restore_powered = hciops_restore_powered,
+       .load_keys = hciops_load_keys,
+       .set_io_capability = hciops_set_io_capability,
+       .create_bonding = hciops_create_bonding,
+       .cancel_bonding = hciops_cancel_bonding,
+       .read_local_oob_data = hciops_read_local_oob_data,
+       .add_remote_oob_data = hciops_add_remote_oob_data,
+       .remove_remote_oob_data = hciops_remove_remote_oob_data,
+       .confirm_name = hciops_confirm_name,
+       .load_ltks = hciops_load_ltks,
+};
+
+static int hciops_init(void)
+{
+       DBG("");
+       return btd_register_adapter_ops(&hci_ops, FALSE);
+}
+
+static void hciops_exit(void)
+{
+       DBG("");
+       btd_adapter_cleanup_ops(&hci_ops);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(hciops, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_LOW, hciops_init, hciops_exit)
diff --git a/plugins/maemo6.c b/plugins/maemo6.c
new file mode 100644 (file)
index 0000000..4819af4
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "adapter.h"
+#include "plugin.h"
+#include "log.h"
+#include "gdbus.h"
+
+/* from mce/mode-names.h */
+#define MCE_RADIO_STATE_BLUETOOTH      (1 << 3)
+
+/* from mce/dbus-names.h */
+#define MCE_SERVICE                    "com.nokia.mce"
+#define MCE_REQUEST_IF                 "com.nokia.mce.request"
+#define MCE_SIGNAL_IF                  "com.nokia.mce.signal"
+#define MCE_REQUEST_PATH               "/com/nokia/mce/request"
+#define MCE_SIGNAL_PATH                        "/com/nokia/mce/signal"
+#define MCE_RADIO_STATES_CHANGE_REQ    "req_radio_states_change"
+#define MCE_RADIO_STATES_GET           "get_radio_states"
+#define MCE_RADIO_STATES_SIG           "radio_states_ind"
+#define MCE_TKLOCK_MODE_SIG            "tklock_mode_ind"
+
+static guint watch_id;
+static guint tklock_watch_id;
+static DBusConnection *conn = NULL;
+static gboolean mce_bt_set = FALSE;
+static gboolean mce_bt_on = FALSE;
+
+static gboolean mce_tklock_mode_cb(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       DBusMessageIter args;
+       const char *sigvalue;
+
+       if (!dbus_message_iter_init(message, &args)) {
+               error("message has no arguments");
+       } else if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
+               error("argument is not string");
+       } else {
+
+               dbus_message_iter_get_basic(&args, &sigvalue);
+               DBG("got signal with value %s", sigvalue);
+
+               if (g_strcmp0("unlocked", sigvalue) == 0 && mce_bt_on)
+                       btd_adapter_enable_auto_connect(adapter);
+       }
+
+       return TRUE;
+}
+
+static gboolean mce_signal_callback(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       DBusMessageIter args;
+       uint32_t sigvalue;
+       struct btd_adapter *adapter = user_data;
+       int err;
+
+       DBG("received mce signal");
+
+       if (!dbus_message_iter_init(message, &args))
+               error("message has no arguments");
+       else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args))
+               error("argument is not uint32");
+       else {
+               dbus_message_iter_get_basic(&args, &sigvalue);
+               DBG("got signal with value %u", sigvalue);
+
+               /* set the adapter according to the mce signal
+                  and remember the value */
+               mce_bt_on = sigvalue & MCE_RADIO_STATE_BLUETOOTH ? TRUE : FALSE;
+
+               if (mce_bt_on)
+                       err = btd_adapter_switch_online(adapter);
+               else
+                       err = btd_adapter_switch_offline(adapter);
+
+               if (err == 0)
+                       mce_bt_set = TRUE;
+
+       }
+
+       return TRUE;
+}
+
+static void read_radio_states_cb(DBusPendingCall *call, void *user_data)
+{
+       DBusError derr;
+       DBusMessage *reply;
+       dbus_uint32_t radio_states;
+       struct btd_adapter *adapter = user_data;
+       int err;
+
+       reply = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&derr);
+       if (dbus_set_error_from_message(&derr, reply)) {
+               error("mce replied with an error: %s, %s",
+                               derr.name, derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       if (dbus_message_get_args(reply, &derr,
+                               DBUS_TYPE_UINT32, &radio_states,
+                               DBUS_TYPE_INVALID) == FALSE) {
+               error("unable to parse get_radio_states reply: %s, %s",
+                                                       derr.name, derr.message);
+               dbus_error_free(&derr);
+               goto done;
+       }
+
+       DBG("radio_states: %d", radio_states);
+
+       mce_bt_on = radio_states & MCE_RADIO_STATE_BLUETOOTH ? TRUE : FALSE;
+
+       if (mce_bt_on)
+               err = btd_adapter_switch_online(adapter);
+       else
+               err = btd_adapter_switch_offline(adapter);
+
+       if (err == 0)
+               mce_bt_set = TRUE;
+
+done:
+       dbus_message_unref(reply);
+}
+
+static void adapter_powered(struct btd_adapter *adapter, gboolean powered)
+{
+       DBusMessage *msg;
+       static gboolean startup = TRUE;
+
+       DBG("adapter_powered called with %d", powered);
+
+       if (startup) {
+               DBusPendingCall *call;
+
+               /* Initialization: sync adapter state and MCE radio state */
+
+               DBG("Startup: reading MCE Bluetooth radio state...");
+               startup = FALSE;
+
+               msg = dbus_message_new_method_call(MCE_SERVICE,
+                                       MCE_REQUEST_PATH, MCE_REQUEST_IF,
+                                       MCE_RADIO_STATES_GET);
+
+               if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) {
+                       error("calling %s failed", MCE_RADIO_STATES_GET);
+                       dbus_message_unref(msg);
+                       return;
+               }
+
+               dbus_pending_call_set_notify(call, read_radio_states_cb,
+                                                               adapter, NULL);
+               dbus_pending_call_unref(call);
+               dbus_message_unref(msg);
+               return;
+       }
+
+       /* MCE initiated operation */
+       if (mce_bt_set == TRUE) {
+               mce_bt_set = FALSE;
+               return;
+       }
+
+       /* Non MCE operation: set MCE according to adapter state */
+       if (mce_bt_on != powered) {
+               dbus_uint32_t radio_states;
+               dbus_uint32_t radio_mask = MCE_RADIO_STATE_BLUETOOTH;
+
+               msg = dbus_message_new_method_call(MCE_SERVICE,
+                                       MCE_REQUEST_PATH, MCE_REQUEST_IF,
+                                       MCE_RADIO_STATES_CHANGE_REQ);
+
+               radio_states = (powered ? MCE_RADIO_STATE_BLUETOOTH : 0);
+
+               DBG("Changing MCE Bluetooth radio state to: %d", radio_states);
+
+               dbus_message_append_args(msg, DBUS_TYPE_UINT32, &radio_states,
+                                       DBUS_TYPE_UINT32, &radio_mask,
+                                       DBUS_TYPE_INVALID);
+
+               if (dbus_connection_send(conn, msg, NULL))
+                       mce_bt_on = powered;
+               else
+                       error("calling %s failed", MCE_RADIO_STATES_CHANGE_REQ);
+
+               dbus_message_unref(msg);
+       }
+}
+
+static int mce_probe(struct btd_adapter *adapter)
+{
+
+       DBG("path %s", adapter_get_path(adapter));
+
+       watch_id = g_dbus_add_signal_watch(conn, NULL, MCE_SIGNAL_PATH,
+                                       MCE_SIGNAL_IF, MCE_RADIO_STATES_SIG,
+                                       mce_signal_callback, adapter, NULL);
+
+       tklock_watch_id = g_dbus_add_signal_watch(conn, NULL, MCE_SIGNAL_PATH,
+                                       MCE_SIGNAL_IF, MCE_TKLOCK_MODE_SIG,
+                                       mce_tklock_mode_cb, adapter, NULL);
+
+       btd_adapter_register_powered_callback(adapter, adapter_powered);
+
+       return 0;
+}
+
+static void mce_remove(struct btd_adapter *adapter)
+{
+       DBG("path %s", adapter_get_path(adapter));
+
+       if (watch_id > 0)
+               g_dbus_remove_watch(conn, watch_id);
+
+       if (tklock_watch_id > 0)
+               g_dbus_remove_watch(conn, tklock_watch_id);
+
+       btd_adapter_unregister_powered_callback(adapter, adapter_powered);
+}
+
+static struct btd_adapter_driver mce_driver = {
+       .name   = "mce",
+       .probe  = mce_probe,
+       .remove = mce_remove,
+};
+
+static int maemo6_init(void)
+{
+       DBG("init maemo6 plugin");
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (conn == NULL) {
+               error("Unable to connect to D-Bus");
+               return -1;
+       }
+
+       return btd_register_adapter_driver(&mce_driver);
+}
+
+static void maemo6_exit(void)
+{
+       DBG("exit maemo6 plugin");
+
+       if (conn != NULL)
+               dbus_connection_unref(conn);
+
+       btd_unregister_adapter_driver(&mce_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(maemo6, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, maemo6_init, maemo6_exit)
diff --git a/plugins/mgmtops.c b/plugins/mgmtops.c
new file mode 100644 (file)
index 0000000..16a97c9
--- /dev/null
@@ -0,0 +1,2523 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/mgmt.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "adapter.h"
+#include "manager.h"
+#include "device.h"
+#include "event.h"
+#include "oob.h"
+#include "eir.h"
+
+#define MGMT_BUF_SIZE 1024
+
+struct pending_uuid {
+       uuid_t uuid;
+       uint8_t svc_hint;
+};
+
+static int max_index = -1;
+static struct controller_info {
+       gboolean valid;
+       gboolean notified;
+       bdaddr_t bdaddr;
+       uint8_t version;
+       uint16_t manufacturer;
+       uint32_t supported_settings;
+       uint32_t current_settings;
+       uint8_t dev_class[3];
+       GSList *connections;
+       uint8_t discov_type;
+
+       gboolean pending_uuid;
+       GSList *pending_uuids;
+
+       gboolean pending_class;
+       uint8_t major;
+       uint8_t minor;
+
+       gboolean pending_powered;
+       gboolean pending_cod_change;
+} *controllers = NULL;
+
+static int mgmt_sock = -1;
+static guint mgmt_watch = 0;
+
+static uint8_t mgmt_version = 0;
+static uint16_t mgmt_revision = 0;
+
+static void read_version_complete(int sk, void *buf, size_t len)
+{
+       struct mgmt_hdr hdr;
+       struct mgmt_rp_read_version *rp = buf;
+
+       if (len < sizeof(*rp)) {
+               error("Too small read version complete event"
+                               " (probably an old kernel)");
+               abort();
+       }
+
+       mgmt_revision = btohs(bt_get_unaligned(&rp->revision));
+       mgmt_version = rp->version;
+
+       DBG("version %u revision %u", mgmt_version, mgmt_revision);
+
+       if (mgmt_version < 1) {
+               error("Version 1 of mgmt needed (kernel has version %u)",
+                                                               mgmt_version);
+               abort();
+       }
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.opcode = htobs(MGMT_OP_READ_INDEX_LIST);
+       hdr.index = htobs(MGMT_INDEX_NONE);
+       if (write(sk, &hdr, sizeof(hdr)) < 0)
+               error("Unable to read controller index list: %s (%d)",
+                                               strerror(errno), errno);
+}
+
+static void add_controller(uint16_t index)
+{
+       struct controller_info *info;
+
+       if (index > max_index) {
+               size_t size = sizeof(struct controller_info) * (index + 1);
+               max_index = index;
+               controllers = g_realloc(controllers, size);
+       }
+
+       info = &controllers[index];
+
+       memset(info, 0, sizeof(*info));
+
+       info->valid = TRUE;
+
+       DBG("Added controller %u", index);
+}
+
+static void read_info(int sk, uint16_t index)
+{
+       struct mgmt_hdr hdr;
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.opcode = htobs(MGMT_OP_READ_INFO);
+       hdr.index = htobs(index);
+
+       if (write(sk, &hdr, sizeof(hdr)) < 0)
+               error("Unable to send read_info command: %s (%d)",
+                                               strerror(errno), errno);
+}
+
+static void get_connections(int sk, uint16_t index)
+{
+       struct mgmt_hdr hdr;
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.opcode = htobs(MGMT_OP_GET_CONNECTIONS);
+       hdr.index = htobs(index);
+
+       if (write(sk, &hdr, sizeof(hdr)) < 0)
+               error("Unable to send get_connections command: %s (%d)",
+                                               strerror(errno), errno);
+}
+
+static void mgmt_index_added(int sk, uint16_t index)
+{
+       add_controller(index);
+       read_info(sk, index);
+}
+
+static void remove_controller(uint16_t index)
+{
+       if (index > max_index)
+               return;
+
+       if (!controllers[index].valid)
+               return;
+
+       btd_manager_unregister_adapter(index);
+
+       g_slist_free_full(controllers[index].pending_uuids, g_free);
+       controllers[index].pending_uuids = NULL;
+
+       memset(&controllers[index], 0, sizeof(struct controller_info));
+
+       DBG("Removed controller %u", index);
+}
+
+static void mgmt_index_removed(int sk, uint16_t index)
+{
+       remove_controller(index);
+}
+
+static int mgmt_set_mode(int index, uint16_t opcode, uint8_t val)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_mode)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_mode *cp = (void *) &buf[sizeof(*hdr)];
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(opcode);
+       hdr->index = htobs(index);
+       hdr->len = htobs(sizeof(*cp));
+
+       cp->val = val;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_set_connectable(int index, gboolean connectable)
+{
+       DBG("index %d connectable %d", index, connectable);
+       return mgmt_set_mode(index, MGMT_OP_SET_CONNECTABLE, connectable);
+}
+
+static int mgmt_set_discoverable(int index, gboolean discoverable,
+                                                       uint16_t timeout)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_discoverable)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_set_discoverable *cp = (void *) &buf[sizeof(*hdr)];
+
+       DBG("index %d discoverable %d", index, discoverable);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_SET_DISCOVERABLE);
+       hdr->index = htobs(index);
+       hdr->len = htobs(sizeof(*cp));
+
+       cp->val = discoverable;
+       cp->timeout = timeout;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_set_pairable(int index, gboolean pairable)
+{
+       DBG("index %d pairable %d", index, pairable);
+       return mgmt_set_mode(index, MGMT_OP_SET_PAIRABLE, pairable);
+}
+
+static inline int mgmt_powered(uint32_t settings)
+{
+       return (settings & MGMT_SETTING_POWERED) != 0;
+}
+
+static inline int mgmt_connectable(uint32_t settings)
+{
+       return (settings & MGMT_SETTING_CONNECTABLE) != 0;
+}
+
+static inline int mgmt_fast_connectable(uint32_t settings)
+{
+       return (settings & MGMT_SETTING_FAST_CONNECTABLE) != 0;
+}
+
+static inline int mgmt_discoverable(uint32_t settings)
+{
+       return (settings & MGMT_SETTING_DISCOVERABLE) != 0;
+}
+
+static inline int mgmt_pairable(uint32_t settings)
+{
+       return (settings & MGMT_SETTING_PAIRABLE) != 0;
+}
+
+static inline int mgmt_ssp(uint32_t settings)
+{
+       return (settings & MGMT_SETTING_SSP) != 0;
+}
+
+static inline int mgmt_bredr(uint32_t settings)
+{
+       return (settings & MGMT_SETTING_BREDR) != 0;
+}
+
+static inline int mgmt_high_speed(uint32_t settings)
+{
+       return (settings & MGMT_SETTING_HS) != 0;
+}
+
+static inline int mgmt_low_energy(uint32_t settings)
+{
+       return (settings & MGMT_SETTING_LE) != 0;
+}
+
+static uint8_t create_mode(uint32_t settings)
+{
+       uint8_t mode = 0;
+
+       if (mgmt_connectable(settings))
+               mode |= SCAN_PAGE;
+
+       if (mgmt_discoverable(settings))
+               mode |= SCAN_INQUIRY;
+
+       return mode;
+}
+
+static void update_settings(struct btd_adapter *adapter, uint32_t settings)
+{
+       struct controller_info *info;
+       gboolean pairable;
+       uint8_t on_mode;
+       uint16_t index, discoverable_timeout;
+
+       DBG("new settings %x", settings);
+
+       btd_adapter_get_mode(adapter, NULL, &on_mode, &discoverable_timeout,
+                                                               &pairable);
+
+       index = adapter_get_dev_id(adapter);
+
+       info = &controllers[index];
+
+       if (on_mode == MODE_DISCOVERABLE && !mgmt_discoverable(settings)) {
+               if(!mgmt_connectable(settings))
+                       mgmt_set_connectable(index, TRUE);
+               mgmt_set_discoverable(index, TRUE, discoverable_timeout);
+       } else if (on_mode == MODE_CONNECTABLE && !mgmt_connectable(settings)) {
+               mgmt_set_connectable(index, TRUE);
+       } else if (mgmt_powered(settings)) {
+               adapter_mode_changed(adapter, create_mode(settings));
+       }
+
+       if (mgmt_pairable(settings) != pairable)
+               mgmt_set_pairable(index, pairable);
+
+       if (mgmt_ssp(info->supported_settings) && !mgmt_ssp(settings))
+               mgmt_set_mode(index, MGMT_OP_SET_SSP, 1);
+
+       if (mgmt_low_energy(info->supported_settings) &&
+                                               !mgmt_low_energy(settings))
+               mgmt_set_mode(index, MGMT_OP_SET_LE, 1);
+}
+
+static int mgmt_update_powered(struct btd_adapter *adapter,
+                                               struct controller_info *info,
+                                               uint32_t settings)
+{
+       if (!mgmt_powered(settings)) {
+               btd_adapter_stop(adapter);
+               g_slist_free_full(info->pending_uuids, g_free);
+               info->pending_uuids = NULL;
+               info->pending_uuid = FALSE;
+               info->pending_class = FALSE;
+               info->pending_cod_change = FALSE;
+               return 0;
+       }
+
+       btd_adapter_start(adapter);
+
+       update_settings(adapter, settings);
+
+       return 0;
+}
+
+static int mode_changed(uint32_t s1, uint32_t s2)
+{
+       if (mgmt_connectable(s1) != mgmt_connectable(s2))
+               return 1;
+
+       if (mgmt_discoverable(s1) != mgmt_discoverable(s2))
+               return 1;
+
+       return 0;
+}
+
+static void mgmt_new_settings(int sk, uint16_t index, void *buf, size_t len)
+{
+       uint32_t settings, *ev = buf;
+       struct controller_info *info;
+       struct btd_adapter *adapter;
+       gboolean old_power, new_power, old_pairable, new_pairable;
+
+       if (len < sizeof(*ev)) {
+               error("Too small new settings event");
+               return;
+       }
+
+       DBG("hci%u new settings", index);
+
+       if (index > max_index) {
+               error("Unexpected index %u in new_settings event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       adapter = manager_find_adapter(&info->bdaddr);
+       if (adapter == NULL) {
+               DBG("Adapter not found");
+               return;
+       }
+
+       settings = bt_get_le32(ev);
+
+       old_power = mgmt_powered(info->current_settings);
+       new_power = mgmt_powered(settings);
+
+       if (new_power != old_power)
+               mgmt_update_powered(adapter, info, settings);
+       else if (new_power && mode_changed(settings, info->current_settings))
+               adapter_mode_changed(adapter, create_mode(settings));
+
+       old_pairable = mgmt_pairable(info->current_settings);
+       new_pairable = mgmt_pairable(settings);
+
+       /* Check for pairable change, except when powered went from True
+        * to False (in which case we always get all settings as False) */
+       if ((!old_power || new_power) && new_pairable != old_pairable)
+               btd_adapter_pairable_changed(adapter, mgmt_pairable(settings));
+
+       info->current_settings = settings;
+}
+
+static void bonding_complete(struct controller_info *info, bdaddr_t *bdaddr,
+                                                               uint8_t status)
+{
+       struct btd_adapter *adapter;
+
+       adapter = manager_find_adapter(&info->bdaddr);
+       if (adapter != NULL)
+               adapter_bonding_complete(adapter, bdaddr, status);
+}
+
+static void mgmt_new_link_key(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_new_link_key *ev = buf;
+       struct controller_info *info;
+
+       if (len != sizeof(*ev)) {
+               error("mgmt_new_link_key event size mismatch (%zu != %zu)",
+                                                       len, sizeof(*ev));
+               return;
+       }
+
+       DBG("Controller %u new key of type %u pin_len %u", index,
+                                       ev->key.type, ev->key.pin_len);
+
+       if (index > max_index) {
+               error("Unexpected index %u in new_key event", index);
+               return;
+       }
+
+       if (ev->key.pin_len > 16) {
+               error("Invalid PIN length (%u) in new_key event",
+                                                       ev->key.pin_len);
+               return;
+       }
+
+       info = &controllers[index];
+
+       if (ev->store_hint)
+               btd_event_link_key_notify(&info->bdaddr, &ev->key.addr.bdaddr,
+                                               ev->key.val, ev->key.type,
+                                               ev->key.pin_len);
+
+       bonding_complete(info, &ev->key.addr.bdaddr, 0);
+}
+
+static void mgmt_device_connected(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_device_connected *ev = buf;
+       struct eir_data eir_data;
+       struct controller_info *info;
+       uint16_t eir_len;
+       char addr[18];
+
+       if (len < sizeof(*ev)) {
+               error("Too small device_connected event");
+               return;
+       }
+
+       eir_len = bt_get_le16(&ev->eir_len);
+       if (len < sizeof(*ev) + eir_len) {
+               error("Too small device_connected event");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+
+       DBG("hci%u device %s connected eir_len %u", index, addr, eir_len);
+
+       if (index > max_index) {
+               error("Unexpected index %u in device_connected event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       memset(&eir_data, 0, sizeof(eir_data));
+       if (eir_len > 0)
+               eir_parse(&eir_data, ev->eir, eir_len);
+
+       btd_event_conn_complete(&info->bdaddr, &ev->addr.bdaddr,
+                                               ev->addr.type,
+                                               eir_data.name,
+                                               eir_data.dev_class);
+
+       eir_data_free(&eir_data);
+}
+
+static void mgmt_device_disconnected(int sk, uint16_t index, void *buf,
+                                                               size_t len)
+{
+       struct mgmt_addr_info *ev = buf;
+       struct controller_info *info;
+       char addr[18];
+
+       if (len < sizeof(*ev)) {
+               error("Too small device_disconnected event");
+               return;
+       }
+
+       ba2str(&ev->bdaddr, addr);
+
+       DBG("hci%u device %s disconnected", index, addr);
+
+       if (index > max_index) {
+               error("Unexpected index %u in device_disconnected event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       btd_event_disconn_complete(&info->bdaddr, &ev->bdaddr);
+}
+
+static void mgmt_connect_failed(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_connect_failed *ev = buf;
+       struct controller_info *info;
+       char addr[18];
+
+       if (len < sizeof(*ev)) {
+               error("Too small connect_failed event");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+
+       DBG("hci%u %s status %u", index, addr, ev->status);
+
+       if (index > max_index) {
+               error("Unexpected index %u in connect_failed event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       btd_event_conn_failed(&info->bdaddr, &ev->addr.bdaddr, ev->status);
+
+       /* In the case of security mode 3 devices */
+       bonding_complete(info, &ev->addr.bdaddr, ev->status);
+}
+
+static int mgmt_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin,
+                                                               size_t pin_len)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_pin_code_reply)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       size_t buf_len;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("index %d addr %s pinlen %zu", index, addr, pin_len);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (pin == NULL) {
+               struct mgmt_cp_pin_code_neg_reply *cp;
+
+               hdr->opcode = htobs(MGMT_OP_PIN_CODE_NEG_REPLY);
+               hdr->len = htobs(sizeof(*cp));
+               hdr->index = htobs(index);
+
+               cp = (void *) &buf[sizeof(*hdr)];
+               bacpy(&cp->addr.bdaddr, bdaddr);
+               cp->addr.type = BDADDR_BREDR;
+
+               buf_len = sizeof(*hdr) + sizeof(*cp);
+       } else {
+               struct mgmt_cp_pin_code_reply *cp;
+
+               if (pin_len > 16)
+                       return -EINVAL;
+
+               hdr->opcode = htobs(MGMT_OP_PIN_CODE_REPLY);
+               hdr->len = htobs(sizeof(*cp));
+               hdr->index = htobs(index);
+
+               cp = (void *) &buf[sizeof(*hdr)];
+               bacpy(&cp->addr.bdaddr, bdaddr);
+               cp->addr.type = BDADDR_BREDR;
+               cp->pin_len = pin_len;
+               memcpy(cp->pin_code, pin, pin_len);
+
+               buf_len = sizeof(*hdr) + sizeof(*cp);
+       }
+
+       if (write(mgmt_sock, buf, buf_len) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static void mgmt_pin_code_request(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_pin_code_request *ev = buf;
+       struct controller_info *info;
+       char addr[18];
+       int err;
+
+       if (len < sizeof(*ev)) {
+               error("Too small pin_code_request event");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+
+       DBG("hci%u %s", index, addr);
+
+       if (index > max_index) {
+               error("Unexpected index %u in pin_code_request event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       err = btd_event_request_pin(&info->bdaddr, &ev->addr.bdaddr,
+                                                               ev->secure);
+       if (err < 0) {
+               error("btd_event_request_pin: %s", strerror(-err));
+               mgmt_pincode_reply(index, &ev->addr.bdaddr, NULL, 0);
+       }
+}
+
+static int mgmt_confirm_reply(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                                                       gboolean success)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_user_confirm_reply)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_user_confirm_reply *cp;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("index %d addr %s success %d", index, addr, success);
+
+       memset(buf, 0, sizeof(buf));
+
+       if (success)
+               hdr->opcode = htobs(MGMT_OP_USER_CONFIRM_REPLY);
+       else
+               hdr->opcode = htobs(MGMT_OP_USER_CONFIRM_NEG_REPLY);
+
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       cp = (void *) &buf[sizeof(*hdr)];
+       bacpy(&cp->addr.bdaddr, bdaddr);
+       cp->addr.type = bdaddr_type;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_passkey_reply(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                                                       uint32_t passkey)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_user_passkey_reply)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       size_t buf_len;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("index %d addr %s passkey %06u", index, addr, passkey);
+
+       memset(buf, 0, sizeof(buf));
+
+       hdr->index = htobs(index);
+       if (passkey == INVALID_PASSKEY) {
+               struct mgmt_cp_user_passkey_neg_reply *cp;
+
+               hdr->opcode = htobs(MGMT_OP_USER_PASSKEY_NEG_REPLY);
+               hdr->len = htobs(sizeof(*cp));
+
+               cp = (void *) &buf[sizeof(*hdr)];
+               bacpy(&cp->addr.bdaddr, bdaddr);
+               cp->addr.type = bdaddr_type;
+
+               buf_len = sizeof(*hdr) + sizeof(*cp);
+       } else {
+               struct mgmt_cp_user_passkey_reply *cp;
+
+               hdr->opcode = htobs(MGMT_OP_USER_PASSKEY_REPLY);
+               hdr->len = htobs(sizeof(*cp));
+
+               cp = (void *) &buf[sizeof(*hdr)];
+               bacpy(&cp->addr.bdaddr, bdaddr);
+               cp->addr.type = bdaddr_type;
+               cp->passkey = htobl(passkey);
+
+               buf_len = sizeof(*hdr) + sizeof(*cp);
+       }
+
+       if (write(mgmt_sock, buf, buf_len) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static void mgmt_passkey_request(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_user_passkey_request *ev = buf;
+       struct controller_info *info;
+       char addr[18];
+       int err;
+
+       if (len < sizeof(*ev)) {
+               error("Too small passkey_request event");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+
+       DBG("hci%u %s", index, addr);
+
+       if (index > max_index) {
+               error("Unexpected index %u in passkey_request event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       err = btd_event_user_passkey(&info->bdaddr, &ev->addr.bdaddr);
+       if (err < 0) {
+               error("btd_event_user_passkey: %s", strerror(-err));
+               mgmt_passkey_reply(index, &ev->addr.bdaddr, ev->addr.type,
+                                                       INVALID_PASSKEY);
+       }
+}
+
+struct confirm_data {
+       int index;
+       bdaddr_t bdaddr;
+       uint8_t type;
+};
+
+static gboolean confirm_accept(gpointer user_data)
+{
+       struct confirm_data *data = user_data;
+       struct controller_info *info = &controllers[data->index];
+
+       DBG("auto-accepting incoming pairing request");
+
+       if (data->index > max_index || !info->valid)
+               return FALSE;
+
+       mgmt_confirm_reply(data->index, &data->bdaddr, data->type, TRUE);
+
+       return FALSE;
+}
+
+static void mgmt_user_confirm_request(int sk, uint16_t index, void *buf,
+                                                               size_t len)
+{
+       struct mgmt_ev_user_confirm_request *ev = buf;
+       struct controller_info *info;
+       char addr[18];
+       int err;
+
+       if (len < sizeof(*ev)) {
+               error("Too small user_confirm_request event");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+
+       DBG("hci%u %s confirm_hint %u", index, addr, ev->confirm_hint);
+
+       if (index > max_index) {
+               error("Unexpected index %u in user_confirm_request event",
+                                                                       index);
+               return;
+       }
+
+       if (ev->confirm_hint) {
+               struct confirm_data *data;
+
+               data = g_new0(struct confirm_data, 1);
+               data->index = index;
+               bacpy(&data->bdaddr, &ev->addr.bdaddr);
+               data->type = ev->addr.type;
+
+               g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1,
+                                               confirm_accept, data, g_free);
+               return;
+       }
+
+       info = &controllers[index];
+
+       err = btd_event_user_confirm(&info->bdaddr, &ev->addr.bdaddr,
+                                                       btohl(ev->value));
+       if (err < 0) {
+               error("btd_event_user_confirm: %s", strerror(-err));
+               mgmt_confirm_reply(index, &ev->addr.bdaddr, ev->addr.type,
+                                                                       FALSE);
+       }
+}
+
+static void uuid_to_uuid128(uuid_t *uuid128, const uuid_t *uuid)
+{
+       if (uuid->type == SDP_UUID16)
+               sdp_uuid16_to_uuid128(uuid128, uuid);
+       else if (uuid->type == SDP_UUID32)
+               sdp_uuid32_to_uuid128(uuid128, uuid);
+       else
+               memcpy(uuid128, uuid, sizeof(*uuid));
+}
+
+static int mgmt_add_uuid(int index, uuid_t *uuid, uint8_t svc_hint)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_add_uuid)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_add_uuid *cp = (void *) &buf[sizeof(*hdr)];
+       struct controller_info *info = &controllers[index];
+       uuid_t uuid128;
+       uint128_t uint128;
+
+       DBG("index %d", index);
+
+       if (info->pending_uuid) {
+               struct pending_uuid *pending = g_new0(struct pending_uuid, 1);
+
+               memcpy(&pending->uuid, uuid, sizeof(*uuid));
+               pending->svc_hint = svc_hint;
+
+               info->pending_uuids = g_slist_append(info->pending_uuids,
+                                                               pending);
+               return 0;
+       }
+
+       uuid_to_uuid128(&uuid128, uuid);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_ADD_UUID);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+       htob128(&uint128, (uint128_t *) cp->uuid);
+
+       cp->svc_hint = svc_hint;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       info->pending_uuid = TRUE;
+
+       return 0;
+}
+
+static int mgmt_remove_uuid(int index, uuid_t *uuid)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_uuid)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_remove_uuid *cp = (void *) &buf[sizeof(*hdr)];
+       uuid_t uuid128;
+       uint128_t uint128;
+
+       DBG("index %d", index);
+
+       uuid_to_uuid128(&uuid128, uuid);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_REMOVE_UUID);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+       htob128(&uint128, (uint128_t *) cp->uuid);
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int clear_uuids(int index)
+{
+       uuid_t uuid_any;
+
+       memset(&uuid_any, 0, sizeof(uuid_any));
+       uuid_any.type = SDP_UUID128;
+
+       return mgmt_remove_uuid(index, &uuid_any);
+}
+
+static void read_index_list_complete(int sk, void *buf, size_t len)
+{
+       struct mgmt_rp_read_index_list *rp = buf;
+       uint16_t num;
+       int i;
+
+       if (len < sizeof(*rp)) {
+               error("Too small read index list complete event");
+               return;
+       }
+
+       num = btohs(bt_get_unaligned(&rp->num_controllers));
+
+       if (num * sizeof(uint16_t) + sizeof(*rp) != len) {
+               error("Incorrect packet size for index list event");
+               return;
+       }
+
+       for (i = 0; i < num; i++) {
+               uint16_t index;
+
+               index = btohs(bt_get_unaligned(&rp->index[i]));
+
+               add_controller(index);
+               read_info(sk, index);
+       }
+}
+
+static int mgmt_set_powered(int index, gboolean powered)
+{
+       struct controller_info *info = &controllers[index];
+
+       DBG("index %d powered %d pending_uuid %u", index, powered,
+                                                       info->pending_uuid);
+
+       if (powered) {
+               if (info->pending_uuid) {
+                       info->pending_powered = TRUE;
+                       return 0;
+               }
+       } else {
+               info->pending_powered = FALSE;
+       }
+
+       return mgmt_set_mode(index, MGMT_OP_SET_POWERED, powered);
+}
+
+static int mgmt_set_name(int index, const char *name)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_local_name)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_set_local_name *cp = (void *) &buf[sizeof(*hdr)];
+
+       DBG("index %d, name %s", index, name);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_SET_LOCAL_NAME);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       strncpy((char *) cp->name, name, sizeof(cp->name) - 1);
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_set_dev_class(int index, uint8_t major, uint8_t minor)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_dev_class)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_set_dev_class *cp = (void *) &buf[sizeof(*hdr)];
+       struct controller_info *info = &controllers[index];
+
+       DBG("index %d major %u minor %u", index, major, minor);
+
+       if (info->pending_uuid) {
+               info->major = major;
+               info->minor = minor;
+               info->pending_class = TRUE;
+               return 0;
+       }
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_SET_DEV_CLASS);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       cp->major = major;
+       cp->minor = minor;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static void read_info_complete(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_rp_read_info *rp = buf;
+       struct controller_info *info;
+       struct btd_adapter *adapter;
+       const char *name;
+       uint8_t mode, major, minor;
+       char addr[18];
+
+       if (len < sizeof(*rp)) {
+               error("Too small read info complete event");
+               return;
+       }
+
+       if (index > max_index) {
+               error("Unexpected index %u in read info complete", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       bacpy(&info->bdaddr, &rp->bdaddr);
+       info->version = rp->version;
+       info->manufacturer = btohs(bt_get_unaligned(&rp->manufacturer));
+
+       memcpy(&info->supported_settings, &rp->supported_settings,
+                                       sizeof(info->supported_settings));
+       memcpy(&info->current_settings, &rp->current_settings,
+                                       sizeof(info->current_settings));
+
+       memcpy(info->dev_class, rp->dev_class, sizeof(info->dev_class));
+
+       ba2str(&info->bdaddr, addr);
+       DBG("hci%u addr %s version %u manufacturer %u class 0x%02x%02x%02x\n",
+               index, addr, info->version, info->manufacturer,
+               info->dev_class[2], info->dev_class[1], info->dev_class[0]);
+       DBG("hci%u settings", index);
+       DBG("hci%u name %s", index, (char *) rp->name);
+       DBG("hci%u short name %s", index, (char *) rp->short_name);
+
+       clear_uuids(index);
+
+       adapter = btd_manager_register_adapter(index,
+                                       mgmt_powered(info->current_settings));
+       if (adapter == NULL) {
+               error("mgmtops: unable to register adapter");
+               return;
+       }
+
+       update_settings(adapter, info->current_settings);
+
+       name = btd_adapter_get_name(adapter);
+
+       DBG("mgmtops setting name %s", name);
+
+       if (name)
+               mgmt_set_name(index, name);
+       else
+               adapter_name_changed(adapter, (char *) rp->name);
+
+       btd_adapter_get_class(adapter, &major, &minor);
+       mgmt_set_dev_class(index, major, minor);
+
+       btd_adapter_get_mode(adapter, &mode, NULL, NULL, NULL);
+       if (mode == MODE_OFF && mgmt_powered(info->current_settings)) {
+               mgmt_set_powered(index, FALSE);
+               return;
+       }
+
+       if (mode != MODE_OFF && !mgmt_powered(info->current_settings))
+               mgmt_set_powered(index, TRUE);
+       else {
+               get_connections(sk, index);
+               btd_adapter_start(adapter);
+       }
+
+       btd_adapter_unref(adapter);
+}
+
+static void disconnect_complete(int sk, uint16_t index, uint8_t status,
+                                                       void *buf, size_t len)
+{
+       struct mgmt_rp_disconnect *rp = buf;
+       struct controller_info *info;
+       char addr[18];
+
+       if (len < sizeof(*rp)) {
+               error("Too small disconnect complete event");
+               return;
+       }
+
+       ba2str(&rp->addr.bdaddr, addr);
+
+       if (status != 0) {
+               error("Disconnecting %s failed with status %u", addr, status);
+               return;
+       }
+
+       DBG("hci%d %s disconnected", index, addr);
+
+       if (index > max_index) {
+               error("Unexpected index %u in disconnect complete", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       btd_event_disconn_complete(&info->bdaddr, &rp->addr.bdaddr);
+
+       bonding_complete(info, &rp->addr.bdaddr, HCI_CONNECTION_TERMINATED);
+}
+
+static void pair_device_complete(int sk, uint16_t index, uint8_t status,
+                                                       void *buf, size_t len)
+{
+       struct mgmt_rp_pair_device *rp = buf;
+       struct controller_info *info;
+       char addr[18];
+
+       if (len < sizeof(*rp)) {
+               error("Too small pair_device complete event");
+               return;
+       }
+
+       ba2str(&rp->addr.bdaddr, addr);
+
+       DBG("hci%d %s pairing complete status %u", index, addr, status);
+
+       if (index > max_index) {
+               error("Unexpected index %u in pair_device complete", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       bonding_complete(info, &rp->addr.bdaddr, status);
+}
+
+static void get_connections_complete(int sk, uint16_t index, void *buf,
+                                                               size_t len)
+{
+       struct mgmt_rp_get_connections *rp = buf;
+       struct controller_info *info;
+       int i;
+
+       if (len < sizeof(*rp)) {
+               error("Too small get_connections complete event");
+               return;
+       }
+
+       if (len < (sizeof(*rp) + (rp->conn_count * sizeof(bdaddr_t)))) {
+               error("Too small get_connections complete event");
+               return;
+       }
+
+       if (index > max_index) {
+               error("Unexpected index %u in get_connections complete",
+                                                               index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       for (i = 0; i < rp->conn_count; i++) {
+               bdaddr_t *bdaddr = g_memdup(&rp->addr[i], sizeof(bdaddr_t));
+               info->connections = g_slist_append(info->connections, bdaddr);
+       }
+}
+
+static void set_local_name_complete(int sk, uint16_t index, void *buf,
+                                                               size_t len)
+{
+       struct mgmt_cp_set_local_name *rp = buf;
+       struct controller_info *info;
+       struct btd_adapter *adapter;
+
+       if (len < sizeof(*rp)) {
+               error("Too small set_local_name complete event");
+               return;
+       }
+
+       DBG("hci%d name %s", index, (char *) rp->name);
+
+       if (index > max_index) {
+               error("Unexpected index %u in set_local_name complete", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       adapter = manager_find_adapter(&info->bdaddr);
+       if (adapter == NULL) {
+               DBG("Adapter not found");
+               return;
+       }
+
+       adapter_name_changed(adapter, (char *) rp->name);
+}
+
+static void read_local_oob_data_complete(int sk, uint16_t index, void *buf,
+                                                               size_t len)
+{
+       struct mgmt_rp_read_local_oob_data *rp = buf;
+       struct btd_adapter *adapter;
+
+       if (len != sizeof(*rp)) {
+               error("read_local_oob_data_complete event size mismatch "
+                                       "(%zu != %zu)", len, sizeof(*rp));
+               return;
+       }
+
+       if (index > max_index) {
+               error("Unexpected index %u in read_local_oob_data_complete",
+                                                               index);
+               return;
+       }
+
+       DBG("hci%u", index);
+
+       adapter = manager_find_adapter_by_id(index);
+
+       if (adapter)
+               oob_read_local_data_complete(adapter, rp->hash, rp->randomizer);
+}
+
+static void start_discovery_complete(int sk, uint16_t index, uint8_t status,
+                                                    void *buf, size_t len)
+{
+       uint8_t *type = buf;
+       struct btd_adapter *adapter;
+
+       if (len != sizeof(*type)) {
+               error("start_discovery_complete event size mismatch "
+                                       "(%zu != %zu)", len, sizeof(*type));
+               return;
+       }
+
+       DBG("hci%u type %u status %u", index, *type, status);
+
+       if (index > max_index) {
+               error("Invalid index %u in start_discovery_complete", index);
+               return;
+       }
+
+       if (!status)
+               return;
+
+       adapter = manager_find_adapter_by_id(index);
+       if (adapter)
+               /* Start discovery failed, inform upper layers. */
+               adapter_set_discovering(adapter, FALSE);
+}
+
+static void read_local_oob_data_failed(int sk, uint16_t index)
+{
+       struct btd_adapter *adapter;
+
+       if (index > max_index) {
+               error("Unexpected index %u in read_local_oob_data_failed",
+                                                               index);
+               return;
+       }
+
+       DBG("hci%u", index);
+
+       adapter = manager_find_adapter_by_id(index);
+
+       if (adapter)
+               oob_read_local_data_complete(adapter, NULL, NULL);
+}
+
+static void handle_pending_uuids(uint16_t index)
+{
+       struct controller_info *info;
+       struct pending_uuid *pending;
+
+       DBG("index %d", index);
+
+       info = &controllers[index];
+
+       info->pending_uuid = FALSE;
+
+       if (g_slist_length(info->pending_uuids) == 0) {
+               if (info->pending_class) {
+                       info->pending_class = FALSE;
+                       mgmt_set_dev_class(index, info->major, info->minor);
+               }
+
+               if (info->pending_powered) {
+                       info->pending_powered = FALSE;
+                       mgmt_set_powered(index, TRUE);
+               }
+
+               return;
+       }
+
+       pending = info->pending_uuids->data;
+
+       mgmt_add_uuid(index, &pending->uuid, pending->svc_hint);
+
+       info->pending_uuids = g_slist_remove(info->pending_uuids, pending);
+       g_free(pending);
+}
+
+static void mgmt_add_uuid_complete(int sk, uint16_t index, void *buf,
+                                                               size_t len)
+{
+       DBG("index %d", index);
+
+       if (index > max_index) {
+               error("Unexpected index %u in add_uuid_complete event", index);
+               return;
+       }
+
+       handle_pending_uuids(index);
+}
+
+static void mgmt_cmd_complete(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_cmd_complete *ev = buf;
+       uint16_t opcode;
+
+       DBG("");
+
+       if (len < sizeof(*ev)) {
+               error("Too small management command complete event packet");
+               return;
+       }
+
+       opcode = btohs(bt_get_unaligned(&ev->opcode));
+
+       len -= sizeof(*ev);
+
+       switch (opcode) {
+       case MGMT_OP_READ_VERSION:
+               read_version_complete(sk, ev->data, len);
+               break;
+       case MGMT_OP_READ_INDEX_LIST:
+               read_index_list_complete(sk, ev->data, len);
+               break;
+       case MGMT_OP_READ_INFO:
+               read_info_complete(sk, index, ev->data, len);
+               break;
+       case MGMT_OP_SET_POWERED:
+               mgmt_new_settings(sk, index, ev->data, len);
+               break;
+       case MGMT_OP_SET_DISCOVERABLE:
+               mgmt_new_settings(sk, index, ev->data, len);
+               break;
+       case MGMT_OP_SET_CONNECTABLE:
+               mgmt_new_settings(sk, index, ev->data, len);
+               break;
+       case MGMT_OP_SET_PAIRABLE:
+               mgmt_new_settings(sk, index, ev->data, len);
+               break;
+       case MGMT_OP_SET_SSP:
+               DBG("set_ssp complete");
+               break;
+       case MGMT_OP_SET_LE:
+               DBG("set_le complete");
+               break;
+       case MGMT_OP_ADD_UUID:
+               mgmt_add_uuid_complete(sk, index, ev->data, len);
+               break;
+       case MGMT_OP_REMOVE_UUID:
+               DBG("remove_uuid complete");
+               break;
+       case MGMT_OP_SET_DEV_CLASS:
+               DBG("set_dev_class complete");
+               break;
+       case MGMT_OP_LOAD_LINK_KEYS:
+               DBG("load_link_keys complete");
+               break;
+       case MGMT_OP_CANCEL_PAIR_DEVICE:
+               DBG("cancel_pair_device complete");
+               break;
+       case MGMT_OP_UNPAIR_DEVICE:
+               DBG("unpair_device complete");
+               break;
+       case MGMT_OP_DISCONNECT:
+               DBG("disconnect complete");
+               disconnect_complete(sk, index, ev->status, ev->data, len);
+               break;
+       case MGMT_OP_GET_CONNECTIONS:
+               get_connections_complete(sk, index, ev->data, len);
+               break;
+       case MGMT_OP_PIN_CODE_REPLY:
+               DBG("pin_code_reply complete");
+               break;
+       case MGMT_OP_PIN_CODE_NEG_REPLY:
+               DBG("pin_code_neg_reply complete");
+               break;
+       case MGMT_OP_SET_IO_CAPABILITY:
+               DBG("set_io_capability complete");
+               break;
+       case MGMT_OP_PAIR_DEVICE:
+               pair_device_complete(sk, index, ev->status, ev->data, len);
+               break;
+       case MGMT_OP_USER_CONFIRM_REPLY:
+               DBG("user_confirm_reply complete");
+               break;
+       case MGMT_OP_USER_CONFIRM_NEG_REPLY:
+               DBG("user_confirm_net_reply complete");
+               break;
+       case MGMT_OP_SET_LOCAL_NAME:
+               set_local_name_complete(sk, index, ev->data, len);
+               break;
+       case MGMT_OP_READ_LOCAL_OOB_DATA:
+               read_local_oob_data_complete(sk, index, ev->data, len);
+               break;
+       case MGMT_OP_ADD_REMOTE_OOB_DATA:
+               DBG("add_remote_oob_data complete");
+               break;
+       case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
+               DBG("remove_remote_oob_data complete");
+               break;
+       case MGMT_OP_BLOCK_DEVICE:
+               DBG("block_device complete");
+               break;
+       case MGMT_OP_UNBLOCK_DEVICE:
+               DBG("unblock_device complete");
+               break;
+       case MGMT_OP_SET_FAST_CONNECTABLE:
+               DBG("set_fast_connectable complete");
+               break;
+       case MGMT_OP_START_DISCOVERY:
+               start_discovery_complete(sk, index, ev->status, ev->data, len);
+               break;
+       case MGMT_OP_STOP_DISCOVERY:
+               DBG("stop_discovery complete");
+               break;
+       case MGMT_OP_SET_DEVICE_ID:
+               DBG("set_did complete");
+               break;
+       default:
+               error("Unknown command complete for opcode %u", opcode);
+               break;
+       }
+}
+
+static void mgmt_add_uuid_busy(int sk, uint16_t index)
+{
+       struct controller_info *info;
+
+       DBG("index %d", index);
+
+       info = &controllers[index];
+       info->pending_cod_change = TRUE;
+}
+
+static void mgmt_cmd_status(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_cmd_status *ev = buf;
+       uint16_t opcode;
+
+       if (len < sizeof(*ev)) {
+               error("Too small management command status event packet");
+               return;
+       }
+
+       opcode = btohs(bt_get_unaligned(&ev->opcode));
+
+       if (!ev->status) {
+               DBG("%s (0x%04x) cmd_status %u", mgmt_opstr(opcode), opcode,
+                                                               ev->status);
+               return;
+       }
+
+       switch (opcode) {
+       case MGMT_OP_READ_LOCAL_OOB_DATA:
+               read_local_oob_data_failed(sk, index);
+               break;
+       case MGMT_OP_ADD_UUID:
+               if (ev->status == MGMT_STATUS_BUSY) {
+                       mgmt_add_uuid_busy(sk, index);
+                       return;
+               }
+               break;
+       }
+
+       error("hci%u: %s (0x%04x) failed: %s (0x%02x)", index,
+                       mgmt_opstr(opcode), opcode, mgmt_errstr(ev->status),
+                       ev->status);
+}
+
+static void mgmt_controller_error(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_controller_error *ev = buf;
+
+       if (len < sizeof(*ev)) {
+               error("Too small management controller error event packet");
+               return;
+       }
+
+       DBG("index %u error_code %u", index, ev->error_code);
+}
+
+static void mgmt_auth_failed(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct controller_info *info;
+       struct mgmt_ev_auth_failed *ev = buf;
+
+       if (len < sizeof(*ev)) {
+               error("Too small mgmt_auth_failed event packet");
+               return;
+       }
+
+       DBG("hci%u auth failed status %u", index, ev->status);
+
+       if (index > max_index) {
+               error("Unexpected index %u in auth_failed event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       bonding_complete(info, &ev->addr.bdaddr, ev->status);
+}
+
+static void mgmt_local_name_changed(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_cp_set_local_name *ev = buf;
+       struct controller_info *info;
+       struct btd_adapter *adapter;
+
+       if (len < sizeof(*ev)) {
+               error("Too small mgmt_local_name_changed event packet");
+               return;
+       }
+
+       DBG("hci%u local name changed: %s", index, (char *) ev->name);
+
+       if (index > max_index) {
+               error("Unexpected index %u in name_changed event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       adapter = manager_find_adapter(&info->bdaddr);
+       if (adapter)
+               adapter_name_changed(adapter, (char *) ev->name);
+}
+
+static void mgmt_device_found(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_device_found *ev = buf;
+       struct controller_info *info;
+       char addr[18];
+       uint32_t flags;
+       uint16_t eir_len;
+       uint8_t *eir;
+       gboolean confirm_name;
+
+       if (len < sizeof(*ev)) {
+               error("mgmt_device_found too short (%zu bytes)", len);
+               return;
+       }
+
+       eir_len = bt_get_le16(&ev->eir_len);
+       if (len != sizeof(*ev) + eir_len) {
+               error("mgmt_device_found event size mismatch (%zu != %zu)",
+                                               len, sizeof(*ev) + eir_len);
+               return;
+       }
+
+       if (index > max_index) {
+               error("Unexpected index %u in device_found event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       if (eir_len == 0)
+               eir = NULL;
+       else
+               eir = ev->eir;
+
+       flags = btohl(ev->flags);
+
+       ba2str(&ev->addr.bdaddr, addr);
+       DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u",
+                       index, addr, ev->rssi, flags, eir_len);
+
+       if (flags & MGMT_DEV_FOUND_LEGACY_PAIRING)
+               btd_event_set_legacy_pairing(&info->bdaddr, &ev->addr.bdaddr,
+                                                                       TRUE);
+       else
+               btd_event_set_legacy_pairing(&info->bdaddr, &ev->addr.bdaddr,
+                                                                       FALSE);
+
+       confirm_name = (flags & MGMT_DEV_FOUND_CONFIRM_NAME);
+
+       btd_event_device_found(&info->bdaddr, &ev->addr.bdaddr,
+                                               ev->addr.type,
+                                               ev->rssi, confirm_name,
+                                               eir, eir_len);
+}
+
+static void mgmt_discovering(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_discovering *ev = buf;
+       struct controller_info *info;
+       struct btd_adapter *adapter;
+
+       if (len < sizeof(*ev)) {
+               error("Too small discovering event");
+               return;
+       }
+
+       DBG("Controller %u type %u discovering %u", index,
+                                       ev->type, ev->discovering);
+
+       if (index > max_index) {
+               error("Unexpected index %u in discovering event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       adapter = manager_find_adapter(&info->bdaddr);
+       if (!adapter)
+               return;
+
+       adapter_set_discovering(adapter, ev->discovering);
+}
+
+static void mgmt_device_blocked(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct controller_info *info;
+       struct mgmt_ev_device_blocked *ev = buf;
+       char addr[18];
+
+       if (len < sizeof(*ev)) {
+               error("Too small mgmt_device_blocked event packet");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+       DBG("Device blocked, index %u, addr %s", index, addr);
+
+       if (index > max_index) {
+               error("Unexpected index %u in device_blocked event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       btd_event_device_blocked(&info->bdaddr, &ev->addr.bdaddr);
+}
+
+static void mgmt_device_unblocked(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct controller_info *info;
+       struct mgmt_ev_device_unblocked *ev = buf;
+       char addr[18];
+
+       if (len < sizeof(*ev)) {
+               error("Too small mgmt_device_unblocked event packet");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+       DBG("Device unblocked, index %u, addr %s", index, addr);
+
+       if (index > max_index) {
+               error("Unexpected index %u in device_unblocked event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       btd_event_device_unblocked(&info->bdaddr, &ev->addr.bdaddr);
+}
+
+static void mgmt_device_unpaired(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct controller_info *info;
+       struct mgmt_ev_device_unpaired *ev = buf;
+       char addr[18];
+
+       if (len < sizeof(*ev)) {
+               error("Too small mgmt_device_unpaired event packet");
+               return;
+       }
+
+       ba2str(&ev->addr.bdaddr, addr);
+       DBG("Device upaired, index %u, addr %s", index, addr);
+
+       if (index > max_index) {
+               error("Unexpected index %u in device_unpaired event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       btd_event_device_unpaired(&info->bdaddr, &ev->addr.bdaddr);
+}
+
+static void mgmt_new_ltk(int sk, uint16_t index, void *buf, size_t len)
+{
+       struct mgmt_ev_new_long_term_key *ev = buf;
+       struct controller_info *info;
+
+       if (len != sizeof(*ev)) {
+               error("mgmt_new_ltk event size mismatch (%zu != %zu)",
+                                                       len, sizeof(*ev));
+               return;
+       }
+
+       DBG("Controller %u new LTK authenticated %u enc_size %u", index,
+                               ev->key.authenticated, ev->key.enc_size);
+
+       if (index > max_index) {
+               error("Unexpected index %u in new_key event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       if (ev->store_hint) {
+               btd_event_ltk_notify(&info->bdaddr, &ev->key.addr.bdaddr,
+                               ev->key.addr.type, ev->key.val, ev->key.master,
+                               ev->key.authenticated, ev->key.enc_size,
+                               ev->key.ediv, ev->key.rand);
+       }
+
+       if (ev->key.master)
+               bonding_complete(info, &ev->key.addr.bdaddr, 0);
+}
+
+static void mgmt_cod_changed(int sk, uint16_t index)
+{
+       struct controller_info *info;
+
+       DBG("index %d", index);
+
+       if (index > max_index) {
+               error("Unexpected index %u in mgmt_cod_changed event", index);
+               return;
+       }
+
+       info = &controllers[index];
+
+       if (info->pending_cod_change) {
+               info->pending_cod_change = FALSE;
+               handle_pending_uuids(index);
+       }
+}
+
+static gboolean mgmt_event(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+       char buf[MGMT_BUF_SIZE];
+       struct mgmt_hdr *hdr = (void *) buf;
+       int sk;
+       ssize_t ret;
+       uint16_t len, opcode, index;
+
+       DBG("cond %d", cond);
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       sk = g_io_channel_unix_get_fd(io);
+
+       if (cond & (G_IO_ERR | G_IO_HUP)) {
+               error("Error on management socket");
+               return FALSE;
+       }
+
+       ret = read(sk, buf, sizeof(buf));
+       if (ret < 0) {
+               error("Unable to read from management socket: %s (%d)",
+                                               strerror(errno), errno);
+               return TRUE;
+       }
+
+       DBG("Received %zd bytes from management socket", ret);
+
+       if (ret < MGMT_HDR_SIZE) {
+               error("Too small Management packet");
+               return TRUE;
+       }
+
+       opcode = btohs(bt_get_unaligned(&hdr->opcode));
+       len = btohs(bt_get_unaligned(&hdr->len));
+       index = btohs(bt_get_unaligned(&hdr->index));
+
+       if (ret != MGMT_HDR_SIZE + len) {
+               error("Packet length mismatch. ret %zd len %u", ret, len);
+               return TRUE;
+       }
+
+       switch (opcode) {
+       case MGMT_EV_CMD_COMPLETE:
+               mgmt_cmd_complete(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_CMD_STATUS:
+               mgmt_cmd_status(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_CONTROLLER_ERROR:
+               mgmt_controller_error(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_INDEX_ADDED:
+               mgmt_index_added(sk, index);
+               break;
+       case MGMT_EV_INDEX_REMOVED:
+               mgmt_index_removed(sk, index);
+               break;
+       case MGMT_EV_NEW_SETTINGS:
+               mgmt_new_settings(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_CLASS_OF_DEV_CHANGED:
+               mgmt_cod_changed(sk, index);
+               break;
+       case MGMT_EV_NEW_LINK_KEY:
+               mgmt_new_link_key(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_DEVICE_CONNECTED:
+               mgmt_device_connected(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_DEVICE_DISCONNECTED:
+               mgmt_device_disconnected(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_CONNECT_FAILED:
+               mgmt_connect_failed(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_PIN_CODE_REQUEST:
+               mgmt_pin_code_request(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_USER_CONFIRM_REQUEST:
+               mgmt_user_confirm_request(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_AUTH_FAILED:
+               mgmt_auth_failed(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_LOCAL_NAME_CHANGED:
+               mgmt_local_name_changed(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_DEVICE_FOUND:
+               mgmt_device_found(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_DISCOVERING:
+               mgmt_discovering(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_DEVICE_BLOCKED:
+               mgmt_device_blocked(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_DEVICE_UNBLOCKED:
+               mgmt_device_unblocked(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_DEVICE_UNPAIRED:
+               mgmt_device_unpaired(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_USER_PASSKEY_REQUEST:
+               mgmt_passkey_request(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       case MGMT_EV_NEW_LONG_TERM_KEY:
+               mgmt_new_ltk(sk, index, buf + MGMT_HDR_SIZE, len);
+               break;
+       default:
+               error("Unknown Management opcode %u (index %u)", opcode, index);
+               break;
+       }
+
+       return TRUE;
+}
+
+static int mgmt_setup(void)
+{
+       struct mgmt_hdr hdr;
+       struct sockaddr_hci addr;
+       GIOChannel *io;
+       GIOCondition condition;
+       int dd, err;
+
+       dd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+       if (dd < 0)
+               return -errno;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.hci_family = AF_BLUETOOTH;
+       addr.hci_dev = HCI_DEV_NONE;
+       addr.hci_channel = HCI_CHANNEL_CONTROL;
+
+       if (bind(dd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               err = -errno;
+               goto fail;
+       }
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.opcode = htobs(MGMT_OP_READ_VERSION);
+       hdr.index = htobs(MGMT_INDEX_NONE);
+       if (write(dd, &hdr, sizeof(hdr)) < 0) {
+               err = -errno;
+               goto fail;
+       }
+
+       io = g_io_channel_unix_new(dd);
+       condition = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+       mgmt_watch = g_io_add_watch(io, condition, mgmt_event, NULL);
+       g_io_channel_unref(io);
+
+       mgmt_sock = dd;
+
+       info("Bluetooth Management interface initialized");
+
+       return 0;
+
+fail:
+       close(dd);
+       return err;
+}
+
+static void mgmt_cleanup(void)
+{
+       g_free(controllers);
+       controllers = NULL;
+       max_index = -1;
+
+       if (mgmt_sock >= 0) {
+               close(mgmt_sock);
+               mgmt_sock = -1;
+       }
+
+       if (mgmt_watch > 0) {
+               g_source_remove(mgmt_watch);
+               mgmt_watch = 0;
+       }
+}
+
+static int mgmt_start_discovery(int index)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_start_discovery)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_start_discovery *cp = (void *) &buf[sizeof(*hdr)];
+       struct controller_info *info = &controllers[index];
+
+       DBG("index %d", index);
+
+       info->discov_type = 0;
+
+       if (mgmt_bredr(info->current_settings))
+               hci_set_bit(BDADDR_BREDR, &info->discov_type);
+
+       if (mgmt_low_energy(info->current_settings)) {
+               hci_set_bit(BDADDR_LE_PUBLIC, &info->discov_type);
+               hci_set_bit(BDADDR_LE_RANDOM, &info->discov_type);
+       }
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_START_DISCOVERY);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       cp->type = info->discov_type;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_stop_discovery(int index)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_start_discovery)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_start_discovery *cp = (void *) &buf[sizeof(*hdr)];
+       struct controller_info *info = &controllers[index];
+
+       DBG("index %d", index);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_STOP_DISCOVERY);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       cp->type = info->discov_type;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_set_fast_connectable(int index, gboolean enable)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_mode)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_mode *cp = (void *) &buf[sizeof(*hdr)];
+
+       DBG("index %d enable %d", index, enable);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_SET_FAST_CONNECTABLE);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       cp->val = enable;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_read_clock(int index, bdaddr_t *bdaddr, int which, int timeout,
+                                       uint32_t *clock, uint16_t *accuracy)
+{
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("index %d addr %s which %d timeout %d", index, addr, which,
+                                                               timeout);
+
+       return -ENOSYS;
+}
+
+static int mgmt_read_bdaddr(int index, bdaddr_t *bdaddr)
+{
+       char addr[18];
+       struct controller_info *info = &controllers[index];
+
+       ba2str(&info->bdaddr, addr);
+       DBG("index %d addr %s", index, addr);
+
+       if (!info->valid)
+               return -ENODEV;
+
+       bacpy(bdaddr, &info->bdaddr);
+
+       return 0;
+}
+
+static int mgmt_block_device(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_block_device)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_block_device *cp;
+       size_t buf_len;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("index %d addr %s", index, addr);
+
+       memset(buf, 0, sizeof(buf));
+
+       hdr->opcode = htobs(MGMT_OP_BLOCK_DEVICE);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       cp = (void *) &buf[sizeof(*hdr)];
+       bacpy(&cp->addr.bdaddr, bdaddr);
+       cp->addr.type = bdaddr_type;
+
+       buf_len = sizeof(*hdr) + sizeof(*cp);
+
+       if (write(mgmt_sock, buf, buf_len) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_unblock_device(int index, bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_unblock_device)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_unblock_device *cp;
+       size_t buf_len;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("index %d addr %s", index, addr);
+
+       memset(buf, 0, sizeof(buf));
+
+       hdr->opcode = htobs(MGMT_OP_UNBLOCK_DEVICE);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       cp = (void *) &buf[sizeof(*hdr)];
+       bacpy(&cp->addr.bdaddr, bdaddr);
+       cp->addr.type = bdaddr_type;
+
+       buf_len = sizeof(*hdr) + sizeof(*cp);
+
+       if (write(mgmt_sock, buf, buf_len) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_get_conn_list(int index, GSList **conns)
+{
+       struct controller_info *info = &controllers[index];
+
+       DBG("index %d", index);
+
+       *conns = info->connections;
+       info->connections = NULL;
+
+       return 0;
+}
+
+static int mgmt_disconnect(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_disconnect)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_disconnect *cp = (void *) &buf[sizeof(*hdr)];
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("index %d %s", index, addr);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_DISCONNECT);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       bacpy(&cp->addr.bdaddr, bdaddr);
+       cp->addr.type = bdaddr_type;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               error("write: %s (%d)", strerror(errno), errno);
+
+       return 0;
+}
+
+static int mgmt_unpair_device(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_unpair_device)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_unpair_device *cp = (void *) &buf[sizeof(*hdr)];
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("index %d addr %s", index, addr);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_UNPAIR_DEVICE);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       bacpy(&cp->addr.bdaddr, bdaddr);
+       cp->addr.type = bdaddr_type;
+       cp->disconnect = 1;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_encrypt_link(int index, bdaddr_t *dst, bt_hci_result_t cb,
+                                                       gpointer user_data)
+{
+       char addr[18];
+
+       ba2str(dst, addr);
+       DBG("index %d addr %s", index, addr);
+
+       return -ENOSYS;
+}
+
+static int mgmt_set_did(int index, uint16_t vendor, uint16_t product,
+                                       uint16_t version, uint16_t source)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_device_id)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_set_device_id *cp = (void *) &buf[sizeof(*hdr)];
+
+       DBG("index %d source %x vendor %x product %x version %x",
+                               index, source, vendor, product, version);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_SET_DEVICE_ID);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       cp->source = htobs(source);
+       cp->vendor = htobs(vendor);
+       cp->product = htobs(product);
+       cp->version = htobs(version);
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_disable_cod_cache(int index)
+{
+       DBG("index %d", index);
+
+       /* The cache control is handled automatically for mgmt */
+       return 0;
+}
+
+static int mgmt_restore_powered(int index)
+{
+       DBG("index %d", index);
+       return -ENOSYS;
+}
+
+static int mgmt_load_link_keys(int index, GSList *keys, gboolean debug_keys)
+{
+       char *buf;
+       struct mgmt_hdr *hdr;
+       struct mgmt_cp_load_link_keys *cp;
+       struct mgmt_link_key_info *key;
+       size_t key_count, cp_size;
+       GSList *l;
+       int err;
+
+       key_count = g_slist_length(keys);
+
+       DBG("index %d keys %zu debug_keys %d", index, key_count, debug_keys);
+
+       cp_size = sizeof(*cp) + (key_count * sizeof(*key));
+
+       buf = g_try_malloc0(sizeof(*hdr) + cp_size);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       hdr = (void *) buf;
+       hdr->opcode = htobs(MGMT_OP_LOAD_LINK_KEYS);
+       hdr->len = htobs(cp_size);
+       hdr->index = htobs(index);
+
+       cp = (void *) (buf + sizeof(*hdr));
+       cp->debug_keys = debug_keys;
+       cp->key_count = htobs(key_count);
+
+       for (l = keys, key = cp->keys; l != NULL; l = g_slist_next(l), key++) {
+               struct link_key_info *info = l->data;
+
+               bacpy(&key->addr.bdaddr, &info->bdaddr);
+               key->addr.type = BDADDR_BREDR;
+               key->type = info->type;
+               memcpy(key->val, info->key, 16);
+               key->pin_len = info->pin_len;
+       }
+
+       if (write(mgmt_sock, buf, sizeof(*hdr) + cp_size) < 0)
+               err = -errno;
+       else
+               err = 0;
+
+       g_free(buf);
+
+       return err;
+}
+
+static int mgmt_set_io_capability(int index, uint8_t io_capability)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_io_capability)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_set_io_capability *cp = (void *) &buf[sizeof(*hdr)];
+
+       DBG("hci%d io_capability 0x%02x", index, io_capability);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_SET_IO_CAPABILITY);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       cp->io_capability = io_capability;
+
+       if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_create_bonding(int index, bdaddr_t *bdaddr, uint8_t addr_type, uint8_t io_cap)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_pair_device)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_pair_device *cp = (void *) &buf[sizeof(*hdr)];
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d bdaddr %s io_cap 0x%02x", index, addr, io_cap);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_PAIR_DEVICE);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       bacpy(&cp->addr.bdaddr, bdaddr);
+       cp->addr.type = addr_type;
+       cp->io_cap = io_cap;
+
+       if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_cancel_bonding(int index, bdaddr_t *bdaddr)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_addr_info)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_addr_info *cp = (void *) &buf[sizeof(*hdr)];
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d bdaddr %s", index, addr);
+
+       memset(buf, 0, sizeof(buf));
+       hdr->opcode = htobs(MGMT_OP_CANCEL_PAIR_DEVICE);
+       hdr->len = htobs(sizeof(*cp));
+       hdr->index = htobs(index);
+
+       bacpy(&cp->bdaddr, bdaddr);
+
+       if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_read_local_oob_data(int index)
+{
+       struct mgmt_hdr hdr;
+
+       DBG("hci%d", index);
+
+       hdr.opcode = htobs(MGMT_OP_READ_LOCAL_OOB_DATA);
+       hdr.len = 0;
+       hdr.index = htobs(index);
+
+       if (write(mgmt_sock, &hdr, sizeof(hdr)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_add_remote_oob_data(int index, bdaddr_t *bdaddr,
+                                       uint8_t *hash, uint8_t *randomizer)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_add_remote_oob_data)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_add_remote_oob_data *cp = (void *) &buf[sizeof(*hdr)];
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d bdaddr %s", index, addr);
+
+       memset(buf, 0, sizeof(buf));
+
+       hdr->opcode = htobs(MGMT_OP_ADD_REMOTE_OOB_DATA);
+       hdr->index = htobs(index);
+       hdr->len = htobs(sizeof(*cp));
+
+       bacpy(&cp->addr.bdaddr, bdaddr);
+       memcpy(cp->hash, hash, 16);
+       memcpy(cp->randomizer, randomizer, 16);
+
+       if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_remove_remote_oob_data(int index, bdaddr_t *bdaddr)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_remote_oob_data)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_remove_remote_oob_data *cp = (void *) &buf[sizeof(*hdr)];
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d bdaddr %s", index, addr);
+
+       memset(buf, 0, sizeof(buf));
+
+       hdr->opcode = htobs(MGMT_OP_REMOVE_REMOTE_OOB_DATA);
+       hdr->index = htobs(index);
+       hdr->len = htobs(sizeof(*cp));
+
+       bacpy(&cp->addr.bdaddr, bdaddr);
+
+       if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmt_confirm_name(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                                                       gboolean name_known)
+{
+       char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_confirm_name)];
+       struct mgmt_hdr *hdr = (void *) buf;
+       struct mgmt_cp_confirm_name *cp = (void *) &buf[sizeof(*hdr)];
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("hci%d bdaddr %s name_known %u", index, addr, name_known);
+
+       memset(buf, 0, sizeof(buf));
+
+       hdr->opcode = htobs(MGMT_OP_CONFIRM_NAME);
+       hdr->index = htobs(index);
+       hdr->len = htobs(sizeof(*cp));
+
+       bacpy(&cp->addr.bdaddr, bdaddr);
+       cp->addr.type = bdaddr_type;
+       cp->name_known = name_known;
+
+       if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static int mgmtops_load_ltks(int index, GSList *keys)
+{
+       char *buf;
+       struct mgmt_hdr *hdr;
+       struct mgmt_cp_load_long_term_keys *cp;
+       struct mgmt_ltk_info *key;
+       size_t key_count, cp_size;
+       GSList *l;
+       int err;
+
+       key_count = g_slist_length(keys);
+
+       DBG("index %d keys %zu", index, key_count);
+
+       cp_size = sizeof(*cp) + (key_count * sizeof(*key));
+
+       buf = g_try_malloc0(sizeof(*hdr) + cp_size);
+       if (buf == NULL)
+               return -ENOMEM;
+
+       hdr = (void *) buf;
+       hdr->opcode = htobs(MGMT_OP_LOAD_LONG_TERM_KEYS);
+       hdr->len = htobs(cp_size);
+       hdr->index = htobs(index);
+
+       cp = (void *) (buf + sizeof(*hdr));
+       cp->key_count = htobs(key_count);
+
+       for (l = keys, key = cp->keys; l != NULL; l = g_slist_next(l), key++) {
+               struct smp_ltk_info *info = l->data;
+
+               bacpy(&key->addr.bdaddr, &info->bdaddr);
+               key->addr.type = info->bdaddr_type;
+               memcpy(key->val, info->val, sizeof(info->val));
+               memcpy(key->rand, info->rand, sizeof(info->rand));
+               memcpy(&key->ediv, &info->ediv, sizeof(key->ediv));
+               key->authenticated = info->authenticated;
+               key->master = info->master;
+               key->enc_size = info->enc_size;
+       }
+
+       if (write(mgmt_sock, buf, sizeof(*hdr) + cp_size) < 0)
+               err = -errno;
+       else
+               err = 0;
+
+       g_free(buf);
+
+       return err;
+}
+
+static struct btd_adapter_ops mgmt_ops = {
+       .setup = mgmt_setup,
+       .cleanup = mgmt_cleanup,
+       .set_powered = mgmt_set_powered,
+       .set_discoverable = mgmt_set_discoverable,
+       .set_pairable = mgmt_set_pairable,
+       .start_discovery = mgmt_start_discovery,
+       .stop_discovery = mgmt_stop_discovery,
+       .set_name = mgmt_set_name,
+       .set_dev_class = mgmt_set_dev_class,
+       .set_fast_connectable = mgmt_set_fast_connectable,
+       .read_clock = mgmt_read_clock,
+       .read_bdaddr = mgmt_read_bdaddr,
+       .block_device = mgmt_block_device,
+       .unblock_device = mgmt_unblock_device,
+       .get_conn_list = mgmt_get_conn_list,
+       .disconnect = mgmt_disconnect,
+       .remove_bonding = mgmt_unpair_device,
+       .pincode_reply = mgmt_pincode_reply,
+       .confirm_reply = mgmt_confirm_reply,
+       .passkey_reply = mgmt_passkey_reply,
+       .encrypt_link = mgmt_encrypt_link,
+       .set_did = mgmt_set_did,
+       .add_uuid = mgmt_add_uuid,
+       .remove_uuid = mgmt_remove_uuid,
+       .disable_cod_cache = mgmt_disable_cod_cache,
+       .restore_powered = mgmt_restore_powered,
+       .load_keys = mgmt_load_link_keys,
+       .set_io_capability = mgmt_set_io_capability,
+       .create_bonding = mgmt_create_bonding,
+       .cancel_bonding = mgmt_cancel_bonding,
+       .read_local_oob_data = mgmt_read_local_oob_data,
+       .add_remote_oob_data = mgmt_add_remote_oob_data,
+       .remove_remote_oob_data = mgmt_remove_remote_oob_data,
+       .confirm_name = mgmt_confirm_name,
+       .load_ltks = mgmtops_load_ltks,
+};
+
+static int mgmt_init(void)
+{
+       return btd_register_adapter_ops(&mgmt_ops, TRUE);
+}
+
+static void mgmt_exit(void)
+{
+       btd_adapter_cleanup_ops(&mgmt_ops);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(mgmtops, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_LOW, mgmt_init, mgmt_exit)
diff --git a/plugins/pnat.c b/plugins/pnat.c
new file mode 100644 (file)
index 0000000..3c611a9
--- /dev/null
@@ -0,0 +1,526 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "sdpd.h"
+#include "btio.h"
+#include "adapter.h"
+#include "log.h"
+
+/* FIXME: This location should be build-time configurable */
+#define PNATD "/usr/bin/phonet-at"
+
+#define DUN_CHANNEL 1
+
+#define TTY_TIMEOUT 100
+#define TTY_TRIES 10
+
+struct dun_client {
+       bdaddr_t bda;
+
+       GIOChannel *io; /* Client socket */
+       guint io_watch; /* Client IO watch id */
+
+       guint tty_timer;
+       int tty_tries;
+       gboolean tty_open;
+       int tty_id;
+       char tty_name[PATH_MAX];
+
+       GPid pnatd_pid;
+};
+
+struct dun_server {
+       bdaddr_t bda;           /* Local adapter address */
+
+       uint32_t record_handle; /* Local SDP record handle */
+       GIOChannel *server;     /* Server socket */
+
+       int rfcomm_ctl;
+
+       struct dun_client client;
+};
+
+static GSList *servers = NULL;
+
+static void disconnect(struct dun_server *server)
+{
+       struct dun_client *client = &server->client;
+
+       if (!client->io)
+               return;
+
+       if (client->io_watch > 0) {
+               g_source_remove(client->io_watch);
+               client->io_watch = 0;
+       }
+
+       g_io_channel_shutdown(client->io, TRUE, NULL);
+       g_io_channel_unref(client->io);
+       client->io = NULL;
+
+       if (client->pnatd_pid > 0) {
+               kill(client->pnatd_pid, SIGTERM);
+               client->pnatd_pid = 0;
+       }
+
+       if (client->tty_timer > 0) {
+               g_source_remove(client->tty_timer);
+               client->tty_timer = 0;
+       }
+
+       if (client->tty_id >= 0) {
+               struct rfcomm_dev_req req;
+
+               memset(&req, 0, sizeof(req));
+               req.dev_id = client->tty_id;
+               req.flags = (1 << RFCOMM_HANGUP_NOW);
+               ioctl(server->rfcomm_ctl, RFCOMMRELEASEDEV, &req);
+
+               client->tty_name[0] = '\0';
+               client->tty_open = FALSE;
+               client->tty_id = -1;
+       }
+}
+
+static gboolean client_event(GIOChannel *chan,
+                                       GIOCondition cond, gpointer data)
+{
+       struct dun_server *server = data;
+       struct dun_client *client = &server->client;
+       char addr[18];
+
+       ba2str(&client->bda, addr);
+
+       DBG("Disconnected DUN from %s (%s)", addr, client->tty_name);
+
+       client->io_watch = 0;
+       disconnect(server);
+
+       return FALSE;
+}
+
+static void pnatd_exit(GPid pid, gint status, gpointer user_data)
+{
+       struct dun_server *server = user_data;
+       struct dun_client *client = &server->client;
+
+        if (WIFEXITED(status))
+                DBG("pnatd (%d) exited with status %d", pid,
+                                                       WEXITSTATUS(status));
+        else
+                DBG("pnatd (%d) was killed by signal %d", pid,
+                                                       WTERMSIG(status));
+       g_spawn_close_pid(pid);
+
+       if (pid != client->pnatd_pid)
+               return;
+
+       /* So disconnect() doesn't send SIGTERM to a non-existing process */
+       client->pnatd_pid = 0;
+
+       disconnect(server);
+}
+
+static gboolean start_pnatd(struct dun_server *server)
+{
+       struct dun_client *client = &server->client;
+       GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH;
+       char *argv[] = { PNATD, client->tty_name, NULL };
+       GError *err = NULL;
+       GPid pid;
+
+       g_spawn_async(NULL, argv, NULL, flags, NULL, NULL, &pid, &err);
+       if (err != NULL) {
+               error("Unable to spawn pnatd: %s", err->message);
+               g_error_free(err);
+               return FALSE;
+       }
+
+       DBG("pnatd started for %s with pid %d", client->tty_name, pid);
+
+       client->pnatd_pid = pid;
+
+       /* We do not store the GSource id since g_remove_source doesn't
+        * make sense for a child watch. If the callback gets removed
+        * waitpid won't be called and the child remains as a zombie)
+        */
+       g_child_watch_add(pid, pnatd_exit, server);
+
+       return TRUE;
+}
+
+static gboolean tty_try_open(gpointer user_data)
+{
+       struct dun_server *server = user_data;
+       struct dun_client *client = &server->client;
+       int tty_fd;
+
+       tty_fd = open(client->tty_name, O_RDONLY | O_NOCTTY);
+       if (tty_fd < 0) {
+               if (errno == EACCES)
+                       goto disconnect;
+
+               client->tty_tries--;
+
+               if (client->tty_tries <= 0)
+                       goto disconnect;
+
+               return TRUE;
+       }
+
+       DBG("%s created for DUN", client->tty_name);
+
+       client->tty_open = TRUE;
+       client->tty_timer = 0;
+
+       g_io_channel_unref(client->io);
+       g_source_remove(client->io_watch);
+
+       client->io = g_io_channel_unix_new(tty_fd);
+       client->io_watch = g_io_add_watch(client->io,
+                                       G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                       client_event, server);
+
+       if (!start_pnatd(server))
+               goto disconnect;
+
+       return FALSE;
+
+disconnect:
+       client->tty_timer = 0;
+       disconnect(server);
+       return FALSE;
+}
+
+static gboolean create_tty(struct dun_server *server)
+{
+       struct dun_client *client = &server->client;
+       struct rfcomm_dev_req req;
+       int sk = g_io_channel_unix_get_fd(client->io);
+
+       memset(&req, 0, sizeof(req));
+       req.dev_id = -1;
+       req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+
+       bacpy(&req.src, &server->bda);
+       bacpy(&req.dst, &client->bda);
+
+       bt_io_get(client->io, BT_IO_RFCOMM, NULL,
+                       BT_IO_OPT_DEST_CHANNEL, &req.channel,
+                       BT_IO_OPT_INVALID);
+
+       client->tty_id = ioctl(sk, RFCOMMCREATEDEV, &req);
+       if (client->tty_id < 0) {
+               error("Can't create RFCOMM TTY: %s", strerror(errno));
+               return FALSE;
+       }
+
+       snprintf(client->tty_name, PATH_MAX - 1, "/dev/rfcomm%d",
+                                                       client->tty_id);
+
+       client->tty_tries = TTY_TRIES;
+
+       tty_try_open(server);
+       if (!client->tty_open && client->tty_tries > 0)
+               client->tty_timer = g_timeout_add(TTY_TIMEOUT,
+                                                       tty_try_open, server);
+
+       return TRUE;
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+       struct dun_server *server = user_data;
+
+       if (err) {
+               error("Accepting DUN connection failed: %s", err->message);
+               disconnect(server);
+               return;
+       }
+
+       if (!create_tty(server)) {
+               error("Device creation failed");
+               disconnect(server);
+       }
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+       struct dun_server *server = user_data;
+       struct dun_client *client = &server->client;
+       GError *err = NULL;
+
+       if (derr && dbus_error_is_set(derr)) {
+               error("DUN access denied: %s", derr->message);
+               goto drop;
+       }
+
+       if (!bt_io_accept(client->io, connect_cb, server, NULL, &err)) {
+               error("bt_io_accept: %s", err->message);
+               g_error_free(err);
+               goto drop;
+       }
+
+       return;
+
+drop:
+       disconnect(server);
+}
+
+static gboolean auth_watch(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct dun_server *server = data;
+       struct dun_client *client = &server->client;
+
+       error("DUN client disconnected while waiting for authorization");
+
+       btd_cancel_authorization(&server->bda, &client->bda);
+
+       disconnect(server);
+
+       return FALSE;
+}
+
+static void confirm_cb(GIOChannel *io, gpointer user_data)
+{
+       struct dun_server *server = user_data;
+       struct dun_client *client = &server->client;
+       GError *err = NULL;
+
+       if (client->io) {
+               error("Rejecting DUN connection since one already exists");
+               return;
+       }
+
+       bt_io_get(io, BT_IO_RFCOMM, &err,
+                       BT_IO_OPT_DEST_BDADDR, &client->bda,
+                       BT_IO_OPT_INVALID);
+       if (err != NULL) {
+               error("Unable to get DUN source and dest address: %s",
+                                                               err->message);
+               g_error_free(err);
+               return;
+       }
+
+       if (btd_request_authorization(&server->bda, &client->bda, DUN_GW_UUID,
+                                               auth_cb, user_data) < 0) {
+               error("Requesting DUN authorization failed");
+               return;
+       }
+
+       client->io_watch = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                               (GIOFunc) auth_watch, server);
+       client->io = g_io_channel_ref(io);
+}
+
+static sdp_record_t *dun_record(uint8_t ch)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root, *aproto;
+       uuid_t root_uuid, dun, gn, l2cap, rfcomm;
+       sdp_profile_desc_t profile;
+       sdp_list_t *proto[2];
+       sdp_record_t *record;
+       sdp_data_t *channel;
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(record, root);
+
+       sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
+       svclass_id = sdp_list_append(NULL, &dun);
+       sdp_uuid16_create(&gn,  GENERIC_NETWORKING_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &gn);
+       sdp_set_service_classes(record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, DIALUP_NET_PROFILE_ID);
+       profile.version = 0x0100;
+       pfseq = sdp_list_append(NULL, &profile);
+       sdp_set_profile_descs(record, pfseq);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(NULL, &l2cap);
+       apseq = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+       proto[1] = sdp_list_append(NULL, &rfcomm);
+       channel = sdp_data_alloc(SDP_UINT8, &ch);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_set_info_attr(record, "Dial-Up Networking", 0, 0);
+
+       sdp_data_free(channel);
+       sdp_list_free(root, NULL);
+       sdp_list_free(svclass_id, NULL);
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(pfseq, NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(aproto, NULL);
+
+       return record;
+}
+
+static gint server_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct dun_server *server = a;
+       const bdaddr_t *src = b;
+
+       return bacmp(src, &server->bda);
+}
+
+static int pnat_probe(struct btd_adapter *adapter)
+{
+       struct dun_server *server;
+       GIOChannel *io;
+       GError *err = NULL;
+       sdp_record_t *record;
+       bdaddr_t src;
+
+       adapter_get_address(adapter, &src);
+
+       server = g_new0(struct dun_server, 1);
+
+       io = bt_io_listen(BT_IO_RFCOMM, NULL, confirm_cb, server, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &src,
+                               BT_IO_OPT_CHANNEL, DUN_CHANNEL,
+                               BT_IO_OPT_INVALID);
+       if (err != NULL) {
+               error("Failed to start DUN server: %s", err->message);
+               g_error_free(err);
+               goto fail;
+       }
+
+       record = dun_record(DUN_CHANNEL);
+       if (!record) {
+               error("Unable to allocate new service record");
+               goto fail;
+       }
+
+       if (add_record_to_server(&src, record) < 0) {
+               error("Unable to register DUN service record");
+               goto fail;
+       }
+
+       server->rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+       if (server->rfcomm_ctl < 0) {
+               error("Unable to create RFCOMM control socket: %s (%d)",
+                                               strerror(errno), errno);
+               goto fail;
+       }
+
+       server->server = io;
+       server->record_handle = record->handle;
+       bacpy(&server->bda, &src);
+
+       servers = g_slist_append(servers, server);
+
+       return 0;
+
+fail:
+       if (io != NULL)
+               g_io_channel_unref(io);
+       g_free(server);
+       return -EIO;
+}
+
+static void pnat_remove(struct btd_adapter *adapter)
+{
+       struct dun_server *server;
+       GSList *match;
+       bdaddr_t src;
+
+       adapter_get_address(adapter, &src);
+
+       match = g_slist_find_custom(servers, &src, server_cmp);
+       if (match == NULL)
+               return;
+
+       server = match->data;
+
+       servers = g_slist_delete_link(servers, match);
+
+       disconnect(server);
+
+       remove_record_from_server(server->record_handle);
+       close(server->rfcomm_ctl);
+       g_io_channel_shutdown(server->server, TRUE, NULL);
+       g_io_channel_unref(server->server);
+       g_free(server);
+}
+
+static struct btd_adapter_driver pnat_server = {
+       .name   = "pnat-server",
+       .probe  = pnat_probe,
+       .remove = pnat_remove,
+};
+
+static int pnat_init(void)
+{
+       DBG("Setup Phonet AT (DUN) plugin");
+
+       return btd_register_adapter_driver(&pnat_server);
+}
+
+static void pnat_exit(void)
+{
+       DBG("Cleanup Phonet AT (DUN) plugin");
+
+       btd_unregister_adapter_driver(&pnat_server);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(pnat, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                       pnat_init, pnat_exit)
diff --git a/plugins/service.c b/plugins/service.c
new file mode 100644 (file)
index 0000000..288f849
--- /dev/null
@@ -0,0 +1,832 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <gdbus.h>
+
+#include "sdpd.h"
+#include "sdp-xml.h"
+#include "plugin.h"
+#include "adapter.h"
+#include "error.h"
+#include "log.h"
+
+#define SERVICE_INTERFACE "org.bluez.Service"
+
+static DBusConnection *connection;
+
+struct record_data {
+       uint32_t handle;
+       char *sender;
+       guint listener_id;
+       struct service_adapter *serv_adapter;
+};
+
+struct context_data {
+       sdp_record_t *record;
+       sdp_data_t attr_data;
+       struct sdp_xml_data *stack_head;
+       uint16_t attr_id;
+};
+
+struct pending_auth {
+       DBusConnection *conn;
+       DBusMessage *msg;
+       char *sender;
+       bdaddr_t dst;
+       char uuid[MAX_LEN_UUID_STR];
+};
+
+struct service_adapter {
+       struct btd_adapter *adapter;
+       GSList *pending_list;
+       GSList *records;
+};
+
+static struct service_adapter *serv_adapter_any = NULL;
+
+static int compute_seq_size(sdp_data_t *data)
+{
+       int unit_size = data->unitSize;
+       sdp_data_t *seq = data->val.dataseq;
+
+       for (; seq; seq = seq->next)
+               unit_size += seq->unitSize;
+
+       return unit_size;
+}
+
+static void element_start(GMarkupParseContext *context,
+               const gchar *element_name, const gchar **attribute_names,
+               const gchar **attribute_values, gpointer user_data, GError **err)
+{
+       struct context_data *ctx_data = user_data;
+
+       if (!strcmp(element_name, "record"))
+               return;
+
+       if (!strcmp(element_name, "attribute")) {
+               int i;
+               for (i = 0; attribute_names[i]; i++) {
+                       if (!strcmp(attribute_names[i], "id")) {
+                               ctx_data->attr_id = strtol(attribute_values[i], 0, 0);
+                               break;
+                       }
+               }
+               DBG("New attribute 0x%04x", ctx_data->attr_id);
+               return;
+       }
+
+       if (ctx_data->stack_head) {
+               struct sdp_xml_data *newelem = sdp_xml_data_alloc();
+               newelem->next = ctx_data->stack_head;
+               ctx_data->stack_head = newelem;
+       } else {
+               ctx_data->stack_head = sdp_xml_data_alloc();
+               ctx_data->stack_head->next = NULL;
+       }
+
+       if (!strcmp(element_name, "sequence"))
+               ctx_data->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL);
+       else if (!strcmp(element_name, "alternate"))
+               ctx_data->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL);
+       else {
+               int i;
+               /* Parse value, name, encoding */
+               for (i = 0; attribute_names[i]; i++) {
+                       if (!strcmp(attribute_names[i], "value")) {
+                               int curlen = strlen(ctx_data->stack_head->text);
+                               int attrlen = strlen(attribute_values[i]);
+
+                               /* Ensure we're big enough */
+                               while ((curlen + 1 + attrlen) > ctx_data->stack_head->size) {
+                                       sdp_xml_data_expand(ctx_data->stack_head);
+                               }
+
+                               memcpy(ctx_data->stack_head->text + curlen,
+                                               attribute_values[i], attrlen);
+                               ctx_data->stack_head->text[curlen + attrlen] = '\0';
+                       }
+
+                       if (!strcmp(attribute_names[i], "encoding")) {
+                               if (!strcmp(attribute_values[i], "hex"))
+                                       ctx_data->stack_head->type = 1;
+                       }
+
+                       if (!strcmp(attribute_names[i], "name")) {
+                               ctx_data->stack_head->name = strdup(attribute_values[i]);
+                       }
+               }
+
+               ctx_data->stack_head->data = sdp_xml_parse_datatype(element_name,
+                               ctx_data->stack_head, ctx_data->record);
+
+               if (ctx_data->stack_head->data == NULL)
+                       error("Can't parse element %s", element_name);
+       }
+}
+
+static void element_end(GMarkupParseContext *context,
+               const gchar *element_name, gpointer user_data, GError **err)
+{
+       struct context_data *ctx_data = user_data;
+       struct sdp_xml_data *elem;
+
+       if (!strcmp(element_name, "record"))
+               return;
+
+       if (!strcmp(element_name, "attribute")) {
+               if (ctx_data->stack_head && ctx_data->stack_head->data) {
+                       int ret = sdp_attr_add(ctx_data->record, ctx_data->attr_id,
+                                                       ctx_data->stack_head->data);
+                       if (ret == -1)
+                               DBG("Could not add attribute 0x%04x",
+                                                       ctx_data->attr_id);
+
+                       ctx_data->stack_head->data = NULL;
+                       sdp_xml_data_free(ctx_data->stack_head);
+                       ctx_data->stack_head = NULL;
+               } else {
+                       DBG("No data for attribute 0x%04x", ctx_data->attr_id);
+               }
+               return;
+       }
+
+       if (!strcmp(element_name, "sequence")) {
+               ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
+
+               if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
+                       ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
+                       ctx_data->stack_head->data->dtd = SDP_SEQ32;
+               } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
+                       ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
+                       ctx_data->stack_head->data->dtd = SDP_SEQ16;
+               } else {
+                       ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
+               }
+       } else if (!strcmp(element_name, "alternate")) {
+               ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
+
+               if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
+                       ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
+                       ctx_data->stack_head->data->dtd = SDP_ALT32;
+               } else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
+                       ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
+                       ctx_data->stack_head->data->dtd = SDP_ALT16;
+               } else {
+                       ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
+               }
+       }
+
+       if (ctx_data->stack_head->next && ctx_data->stack_head->data &&
+                                       ctx_data->stack_head->next->data) {
+               switch (ctx_data->stack_head->next->data->dtd) {
+               case SDP_SEQ8:
+               case SDP_SEQ16:
+               case SDP_SEQ32:
+               case SDP_ALT8:
+               case SDP_ALT16:
+               case SDP_ALT32:
+                       ctx_data->stack_head->next->data->val.dataseq =
+                               sdp_seq_append(ctx_data->stack_head->next->data->val.dataseq,
+                                                               ctx_data->stack_head->data);
+                       ctx_data->stack_head->data = NULL;
+                       break;
+               }
+
+               elem = ctx_data->stack_head;
+               ctx_data->stack_head = ctx_data->stack_head->next;
+
+               sdp_xml_data_free(elem);
+       }
+}
+
+static GMarkupParser parser = {
+       element_start, element_end, NULL, NULL, NULL
+};
+
+static sdp_record_t *sdp_xml_parse_record(const char *data, int size)
+{
+       GMarkupParseContext *ctx;
+       struct context_data *ctx_data;
+       sdp_record_t *record;
+
+       ctx_data = malloc(sizeof(*ctx_data));
+       if (!ctx_data)
+               return NULL;
+
+       record = sdp_record_alloc();
+       if (!record) {
+               free(ctx_data);
+               return NULL;
+       }
+
+       memset(ctx_data, 0, sizeof(*ctx_data));
+       ctx_data->record = record;
+
+       ctx = g_markup_parse_context_new(&parser, 0, ctx_data, NULL);
+
+       if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
+               error("XML parsing error");
+               g_markup_parse_context_free(ctx);
+               sdp_record_free(record);
+               free(ctx_data);
+               return NULL;
+       }
+
+       g_markup_parse_context_free(ctx);
+
+       free(ctx_data);
+
+       return record;
+}
+
+static struct record_data *find_record(struct service_adapter *serv_adapter,
+                                       uint32_t handle, const char *sender)
+{
+       GSList *list;
+
+       for (list = serv_adapter->records; list; list = list->next) {
+               struct record_data *data = list->data;
+               if (handle == data->handle && !strcmp(sender, data->sender))
+                       return data;
+       }
+
+       return NULL;
+}
+
+static struct pending_auth *next_pending(struct service_adapter *serv_adapter)
+{
+       GSList *l = serv_adapter->pending_list;
+
+       if (l) {
+               struct pending_auth *auth = l->data;
+               return auth;
+       }
+
+       return NULL;
+}
+
+static struct pending_auth *find_pending_by_sender(
+                       struct service_adapter *serv_adapter,
+                       const char *sender)
+{
+       GSList *l = serv_adapter->pending_list;
+
+       for (; l; l = l->next) {
+               struct pending_auth *auth = l->data;
+               if (g_str_equal(auth->sender, sender))
+                       return auth;
+       }
+
+       return NULL;
+}
+
+static void exit_callback(DBusConnection *conn, void *user_data)
+{
+       struct record_data *user_record = user_data;
+       struct service_adapter *serv_adapter = user_record->serv_adapter;
+       struct pending_auth *auth;
+
+       DBG("remove record");
+
+       serv_adapter->records = g_slist_remove(serv_adapter->records,
+                                               user_record);
+
+       auth = find_pending_by_sender(serv_adapter, user_record->sender);
+       if (auth) {
+               serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
+                                                       auth);
+               g_free(auth);
+       }
+
+       remove_record_from_server(user_record->handle);
+
+       g_free(user_record->sender);
+       g_free(user_record);
+}
+
+static int add_xml_record(DBusConnection *conn, const char *sender,
+                       struct service_adapter *serv_adapter,
+                       const char *record, dbus_uint32_t *handle)
+{
+       struct record_data *user_record;
+       sdp_record_t *sdp_record;
+       bdaddr_t src;
+
+       sdp_record = sdp_xml_parse_record(record, strlen(record));
+       if (!sdp_record) {
+               error("Parsing of XML service record failed");
+               return -EIO;
+       }
+
+       if (serv_adapter->adapter)
+               adapter_get_address(serv_adapter->adapter, &src);
+       else
+               bacpy(&src, BDADDR_ANY);
+
+       if (add_record_to_server(&src, sdp_record) < 0) {
+               error("Failed to register service record");
+               sdp_record_free(sdp_record);
+               return -EIO;
+       }
+
+       user_record = g_new0(struct record_data, 1);
+       user_record->handle = sdp_record->handle;
+       user_record->sender = g_strdup(sender);
+       user_record->serv_adapter = serv_adapter;
+       user_record->listener_id = g_dbus_add_disconnect_watch(conn, sender,
+                                       exit_callback, user_record, NULL);
+
+       serv_adapter->records = g_slist_append(serv_adapter->records,
+                                                               user_record);
+
+       DBG("listener_id %d", user_record->listener_id);
+
+       *handle = user_record->handle;
+
+       return 0;
+}
+
+static DBusMessage *update_record(DBusConnection *conn, DBusMessage *msg,
+               struct service_adapter *serv_adapter,
+               dbus_uint32_t handle, sdp_record_t *sdp_record)
+{
+       bdaddr_t src;
+       int err;
+
+       if (remove_record_from_server(handle) < 0) {
+               sdp_record_free(sdp_record);
+               return btd_error_not_available(msg);
+       }
+
+       if (serv_adapter->adapter)
+               adapter_get_address(serv_adapter->adapter, &src);
+       else
+               bacpy(&src, BDADDR_ANY);
+
+       sdp_record->handle = handle;
+       err = add_record_to_server(&src, sdp_record);
+       if (err < 0) {
+               sdp_record_free(sdp_record);
+               error("Failed to update the service record");
+               return btd_error_failed(msg, strerror(-err));
+       }
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *update_xml_record(DBusConnection *conn,
+                               DBusMessage *msg,
+                               struct service_adapter *serv_adapter)
+{
+       struct record_data *user_record;
+       sdp_record_t *sdp_record;
+       const char *record;
+       dbus_uint32_t handle;
+       int len;
+
+       if (dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_UINT32, &handle,
+                               DBUS_TYPE_STRING, &record,
+                               DBUS_TYPE_INVALID) == FALSE)
+               return NULL;
+
+       len = (record ? strlen(record) : 0);
+       if (len == 0)
+               return btd_error_invalid_args(msg);
+
+       user_record = find_record(serv_adapter, handle,
+                               dbus_message_get_sender(msg));
+       if (!user_record)
+               return btd_error_not_available(msg);
+
+       sdp_record = sdp_xml_parse_record(record, len);
+       if (!sdp_record) {
+               error("Parsing of XML service record failed");
+               return btd_error_failed(msg,
+                                       "Parsing of XML service record failed");
+       }
+
+       return update_record(conn, msg, serv_adapter, handle, sdp_record);
+}
+
+static int remove_record(DBusConnection *conn, const char *sender,
+                       struct service_adapter *serv_adapter,
+                       dbus_uint32_t handle)
+{
+       struct record_data *user_record;
+
+       DBG("remove record 0x%x", handle);
+
+       user_record = find_record(serv_adapter, handle, sender);
+       if (!user_record)
+               return -1;
+
+       DBG("listner_id %d", user_record->listener_id);
+
+       g_dbus_remove_watch(conn, user_record->listener_id);
+
+       exit_callback(conn, user_record);
+
+       return 0;
+}
+
+static DBusMessage *add_service_record(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct service_adapter *serv_adapter = data;
+       DBusMessage *reply;
+       const char *sender, *record;
+       dbus_uint32_t handle;
+       int err;
+
+       if (dbus_message_get_args(msg, NULL,
+                       DBUS_TYPE_STRING, &record, DBUS_TYPE_INVALID) == FALSE)
+               return NULL;
+
+       sender = dbus_message_get_sender(msg);
+       err = add_xml_record(conn, sender, serv_adapter, record, &handle);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_UINT32, &handle,
+                                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *update_service_record(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct service_adapter *serv_adapter = data;
+
+       return update_xml_record(conn, msg, serv_adapter);
+}
+
+static DBusMessage *remove_service_record(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct service_adapter *serv_adapter = data;
+       dbus_uint32_t handle;
+       const char *sender;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return NULL;
+
+       sender = dbus_message_get_sender(msg);
+
+       if (remove_record(conn, sender, serv_adapter, handle) < 0)
+               return btd_error_not_available(msg);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+       struct service_adapter *serv_adapter = user_data;
+       DBusMessage *reply;
+       struct pending_auth *auth;
+       bdaddr_t src;
+
+       auth = next_pending(serv_adapter);
+       if (auth == NULL) {
+               info("Authorization cancelled: Client exited");
+               return;
+       }
+
+       if (derr) {
+               error("Access denied: %s", derr->message);
+
+               reply = btd_error_not_authorized(auth->msg);
+               dbus_message_unref(auth->msg);
+               g_dbus_send_message(auth->conn, reply);
+               goto done;
+       }
+
+       g_dbus_send_reply(auth->conn, auth->msg,
+                       DBUS_TYPE_INVALID);
+
+done:
+       dbus_connection_unref(auth->conn);
+
+       serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
+                                                                       auth);
+       g_free(auth);
+
+       auth = next_pending(serv_adapter);
+       if (auth == NULL)
+               return;
+
+       if (serv_adapter->adapter)
+               adapter_get_address(serv_adapter->adapter, &src);
+       else
+               bacpy(&src, BDADDR_ANY);
+
+       btd_request_authorization(&src, &auth->dst,
+                                       auth->uuid, auth_cb, serv_adapter);
+}
+
+static DBusMessage *request_authorization(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct record_data *user_record;
+       struct service_adapter *serv_adapter = data;
+       sdp_record_t *record;
+       sdp_list_t *services;
+       const char *sender;
+       dbus_uint32_t handle;
+       const char *address;
+       struct pending_auth *auth;
+       char uuid_str[MAX_LEN_UUID_STR];
+       uuid_t *uuid, *uuid128;
+       bdaddr_t src;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+                                       DBUS_TYPE_UINT32, &handle,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return NULL;
+
+       sender = dbus_message_get_sender(msg);
+       if (find_pending_by_sender(serv_adapter, sender))
+               return btd_error_does_not_exist(msg);
+
+       user_record = find_record(serv_adapter, handle, sender);
+       if (!user_record) {
+               user_record = find_record(serv_adapter_any, handle, sender);
+               if (!user_record)
+                       return btd_error_not_authorized(msg);
+       }
+
+       record = sdp_record_find(user_record->handle);
+       if (record == NULL)
+               return btd_error_not_authorized(msg);
+
+       if (sdp_get_service_classes(record, &services) < 0) {
+               sdp_record_free(record);
+               return btd_error_not_authorized(msg);
+       }
+
+       if (services == NULL)
+               return btd_error_not_authorized(msg);
+
+       uuid = services->data;
+       uuid128 = sdp_uuid_to_uuid128(uuid);
+
+       sdp_list_free(services, bt_free);
+
+       if (sdp_uuid2strn(uuid128, uuid_str, MAX_LEN_UUID_STR) < 0) {
+               bt_free(uuid128);
+               return btd_error_not_authorized(msg);
+       }
+       bt_free(uuid128);
+
+       auth = g_new0(struct pending_auth, 1);
+       auth->msg = dbus_message_ref(msg);
+       auth->conn = dbus_connection_ref(connection);
+       auth->sender = user_record->sender;
+       memcpy(auth->uuid, uuid_str, MAX_LEN_UUID_STR);
+       str2ba(address, &auth->dst);
+
+       serv_adapter->pending_list = g_slist_append(serv_adapter->pending_list,
+                                                                       auth);
+
+       auth = next_pending(serv_adapter);
+       if (auth == NULL)
+               return btd_error_does_not_exist(msg);
+
+       if (serv_adapter->adapter)
+               adapter_get_address(serv_adapter->adapter, &src);
+       else
+               bacpy(&src, BDADDR_ANY);
+
+       if (btd_request_authorization(&src, &auth->dst, auth->uuid, auth_cb,
+                                                       serv_adapter) < 0) {
+               serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
+                                                                       auth);
+               g_free(auth);
+               return btd_error_not_authorized(msg);
+       }
+
+       return NULL;
+}
+
+static DBusMessage *cancel_authorization(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       struct service_adapter *serv_adapter = data;
+       struct pending_auth *auth;
+       const gchar *sender;
+       bdaddr_t src;
+
+       sender = dbus_message_get_sender(msg);
+
+       auth = find_pending_by_sender(serv_adapter, sender);
+       if (auth == NULL)
+               return btd_error_does_not_exist(msg);
+
+       if (serv_adapter->adapter)
+               adapter_get_address(serv_adapter->adapter, &src);
+       else
+               bacpy(&src, BDADDR_ANY);
+
+       btd_cancel_authorization(&src, &auth->dst);
+
+       reply = btd_error_not_authorized(auth->msg);
+       dbus_message_unref(auth->msg);
+       g_dbus_send_message(auth->conn, reply);
+
+       dbus_connection_unref(auth->conn);
+
+       serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
+                                                                       auth);
+       g_free(auth);
+
+       auth = next_pending(serv_adapter);
+       if (auth == NULL)
+               goto done;
+
+       if (serv_adapter->adapter)
+               adapter_get_address(serv_adapter->adapter, &src);
+       else
+               bacpy(&src, BDADDR_ANY);
+
+       btd_request_authorization(&src, &auth->dst,
+                                       auth->uuid, auth_cb, serv_adapter);
+
+done:
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable service_methods[] = {
+       { GDBUS_METHOD("AddRecord",
+               GDBUS_ARGS({ "record", "s" }),
+               GDBUS_ARGS({ "handle", "u" }),
+               add_service_record) },
+       { GDBUS_METHOD("UpdateRecord",
+               GDBUS_ARGS({ "handle", "u" }, { "record", "s" }), NULL,
+               update_service_record) },
+       { GDBUS_METHOD("RemoveRecord",
+               GDBUS_ARGS({ "handle", "u" }), NULL,
+               remove_service_record) },
+       { GDBUS_ASYNC_METHOD("RequestAuthorization",
+               GDBUS_ARGS({ "address", "s" }, { "handle", "u"}), NULL,
+               request_authorization) },
+       { GDBUS_METHOD("CancelAuthorization",
+               NULL, NULL, cancel_authorization) },
+       { }
+};
+
+static void path_unregister(void *data)
+{
+       struct service_adapter *serv_adapter = data;
+       GSList *l, *next = NULL;
+
+       for (l = serv_adapter->records; l != NULL; l = next) {
+               struct record_data *user_record = l->data;
+
+               next = l->next;
+
+               g_dbus_remove_watch(connection, user_record->listener_id);
+               exit_callback(connection, user_record);
+       }
+
+       g_free(serv_adapter);
+}
+
+static int register_interface(const char *path, struct btd_adapter *adapter)
+{
+       struct service_adapter *serv_adapter;
+
+       DBG("path %s", path);
+
+       serv_adapter = g_try_new0(struct service_adapter, 1);
+       if (serv_adapter == NULL)
+               return -ENOMEM;
+
+       serv_adapter->adapter = adapter;
+       serv_adapter->pending_list = NULL;
+
+       if (g_dbus_register_interface(connection, path, SERVICE_INTERFACE,
+                               service_methods, NULL, NULL, serv_adapter,
+                                               path_unregister) == FALSE) {
+               error("D-Bus failed to register %s interface",
+                                                       SERVICE_INTERFACE);
+               g_free(serv_adapter);
+               return -EIO;
+       }
+
+       DBG("Registered interface %s on path %s", SERVICE_INTERFACE, path);
+
+       if (serv_adapter->adapter == NULL)
+               serv_adapter_any = serv_adapter;
+
+       return 0;
+}
+
+static void unregister_interface(const char *path)
+{
+       DBG("path %s", path);
+
+       g_dbus_unregister_interface(connection, path, SERVICE_INTERFACE);
+}
+
+static int service_probe(struct btd_adapter *adapter)
+{
+       register_interface(adapter_get_path(adapter), adapter);
+
+       return 0;
+}
+
+static void service_remove(struct btd_adapter *adapter)
+{
+       unregister_interface(adapter_get_path(adapter));
+}
+
+static struct btd_adapter_driver service_driver = {
+       .name   = "service",
+       .probe  = service_probe,
+       .remove = service_remove,
+};
+
+static const char *any_path;
+
+static int service_init(void)
+{
+       int err;
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -EIO;
+
+       any_path = btd_adapter_any_request_path();
+       if (any_path != NULL) {
+               if (register_interface(any_path, NULL) < 0) {
+                       btd_adapter_any_release_path();
+                       any_path = NULL;
+               }
+       }
+
+       err = btd_register_adapter_driver(&service_driver);
+       if (err < 0) {
+               dbus_connection_unref(connection);
+               return err;
+       }
+
+       return 0;
+}
+
+static void service_exit(void)
+{
+       btd_unregister_adapter_driver(&service_driver);
+
+       if (any_path != NULL) {
+               unregister_interface(any_path);
+
+               btd_adapter_any_release_path();
+               any_path = NULL;
+       }
+
+       dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(service, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_HIGH, service_init, service_exit)
diff --git a/plugins/storage.c b/plugins/storage.c
new file mode 100644 (file)
index 0000000..04a02c7
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <bluetooth/bluetooth.h>
+
+#include "plugin.h"
+#include "log.h"
+
+static int storage_init(void)
+{
+       return 0;
+}
+
+static void storage_exit(void)
+{
+}
+
+BLUETOOTH_PLUGIN_DEFINE(storage, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, storage_init, storage_exit)
diff --git a/plugins/wiimote.c b/plugins/wiimote.c
new file mode 100644 (file)
index 0000000..9c69c6d
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  David Herrmann <dh.herrmann@googlemail.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <bluetooth/bluetooth.h>
+#include <glib.h>
+
+#include "plugin.h"
+#include "adapter.h"
+#include "device.h"
+#include "log.h"
+#include "storage.h"
+
+/*
+ * Nintendo Wii Remote devices require the bdaddr of the host as pin input for
+ * authentication. This plugin registers a pin-callback and forces this pin
+ * to be used for authentication.
+ *
+ * There are two ways to place the wiimote into discoverable mode.
+ *  - Pressing the red-sync button on the back of the wiimote. This module
+ *    supports pairing via this method. Auto-reconnect should be possible after
+ *    the device was paired once.
+ *  - Pressing the 1+2 buttons on the front of the wiimote. This module does
+ *    not support this method since this method never enables auto-reconnect.
+ *    Hence, pairing is not needed. Use it without pairing if you want.
+ * After connecting the wiimote you should immediately connect to the input
+ * service of the wiimote. If you don't, the wiimote will close the connection.
+ * The wiimote waits about 5 seconds until it turns off again.
+ * Auto-reconnect is only enabled when pairing with the wiimote via the red
+ * sync-button and then connecting to the input service. If you do not connect
+ * to the input service, then auto-reconnect is not enabled.
+ * If enabled, the wiimote connects to the host automatically when any button
+ * is pressed.
+ */
+
+static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device,
+                                               char *pinbuf, gboolean *display)
+{
+       uint16_t vendor, product;
+       bdaddr_t sba, dba;
+       char addr[18], name[25];
+
+       adapter_get_address(adapter, &sba);
+       device_get_address(device, &dba, NULL);
+       ba2str(&dba, addr);
+
+       vendor = btd_device_get_vendor(device);
+       product = btd_device_get_product(device);
+
+       device_get_name(device, name, sizeof(name));
+       name[sizeof(name) - 1] = 0;
+
+       if (g_str_equal(name, "Nintendo RVL-CNT-01") ||
+                               (vendor == 0x057e && product == 0x0306)) {
+               DBG("Forcing fixed pin on detected wiimote %s", addr);
+               memcpy(pinbuf, &sba, 6);
+               return 6;
+       }
+
+       return 0;
+}
+
+static int wii_probe(struct btd_adapter *adapter)
+{
+       btd_adapter_register_pin_cb(adapter, wii_pincb);
+
+       return 0;
+}
+
+static void wii_remove(struct btd_adapter *adapter)
+{
+       btd_adapter_unregister_pin_cb(adapter, wii_pincb);
+}
+
+static struct btd_adapter_driver wii_driver = {
+       .name   = "wiimote",
+       .probe  = wii_probe,
+       .remove = wii_remove,
+};
+
+static int wii_init(void)
+{
+       return btd_register_adapter_driver(&wii_driver);
+}
+
+static void wii_exit(void)
+{
+       btd_unregister_adapter_driver(&wii_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(wiimote, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_LOW, wii_init, wii_exit)
diff --git a/proximity/immalert.c b/proximity/immalert.c
new file mode 100644 (file)
index 0000000..1540b61
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
+#include "gatt-service.h"
+#include "attrib-server.h"
+#include "device.h"
+#include "attio.h"
+#include "dbus-common.h"
+#include "reporter.h"
+#include "immalert.h"
+
+struct imm_alert_adapter {
+       struct btd_adapter *adapter;
+       DBusConnection *conn;
+       GSList *connected_devices;
+};
+
+struct connected_device {
+       struct btd_device *device;
+       struct imm_alert_adapter *adapter;
+       uint8_t alert_level;
+       guint callback_id;
+};
+
+static GSList *imm_alert_adapters;
+
+static int imdevice_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct connected_device *condev = a;
+       const struct btd_device *device = b;
+
+       if (condev->device == device)
+               return 0;
+
+       return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct imm_alert_adapter *ia, struct btd_device *device)
+{
+       GSList *l = g_slist_find_custom(ia->connected_devices, device,
+                                                               imdevice_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static int imadapter_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct imm_alert_adapter *imadapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (imadapter->adapter == adapter)
+               return 0;
+
+       return -1;
+}
+
+static struct imm_alert_adapter *
+find_imm_alert_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(imm_alert_adapters, adapter,
+                                                               imadapter_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+const char *imm_alert_get_level(struct btd_device *device)
+{
+       struct imm_alert_adapter *imadapter;
+       struct connected_device *condev;
+
+       if (!device)
+               return get_alert_level_string(NO_ALERT);
+
+       imadapter = find_imm_alert_adapter(device_get_adapter(device));
+       if (!imadapter)
+               return get_alert_level_string(NO_ALERT);
+
+       condev = find_connected_device(imadapter, device);
+       if (!condev)
+               return get_alert_level_string(NO_ALERT);
+
+       return get_alert_level_string(condev->alert_level);
+}
+
+static void imm_alert_emit_alert_signal(struct connected_device *condev,
+                                                       uint8_t alert_level)
+{
+       struct imm_alert_adapter *adapter;
+       const char *path, *alert_level_str;
+
+       if (!condev)
+               return;
+
+       adapter = condev->adapter;
+       path = device_get_path(condev->device);
+       alert_level_str = get_alert_level_string(alert_level);
+
+       DBG("alert %s remote %s", alert_level_str, path);
+
+       emit_property_changed(adapter->conn, path,
+                       PROXIMITY_REPORTER_INTERFACE, "ImmediateAlertLevel",
+                       DBUS_TYPE_STRING, &alert_level_str);
+}
+
+static void imm_alert_remove_condev(struct connected_device *condev)
+{
+       struct imm_alert_adapter *ia;
+
+       if (!condev)
+               return;
+
+       ia = condev->adapter;
+
+       if (condev->callback_id && condev->device)
+               btd_device_remove_attio_callback(condev->device,
+                                                       condev->callback_id);
+
+       if (condev->device)
+               btd_device_unref(condev->device);
+
+       ia->connected_devices = g_slist_remove(ia->connected_devices, condev);
+       g_free(condev);
+}
+
+/* condev can be NULL */
+static void imm_alert_disc_cb(gpointer user_data)
+{
+       struct connected_device *condev = user_data;
+
+       if (!condev)
+               return;
+
+       DBG("immediate alert remove device %p", condev->device);
+
+       imm_alert_emit_alert_signal(condev, NO_ALERT);
+       imm_alert_remove_condev(condev);
+}
+
+static uint8_t imm_alert_alert_lvl_write(struct attribute *a,
+                               struct btd_device *device, gpointer user_data)
+{
+       uint8_t value;
+       struct imm_alert_adapter *ia = user_data;
+       struct connected_device *condev = NULL;
+
+       if (!device)
+               goto set_error;
+
+       condev = find_connected_device(ia, device);
+
+       if (a->len == 0) {
+               DBG("Illegal alert level length");
+               goto set_error;
+       }
+
+       value = a->data[0];
+       if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+               DBG("Illegal alert value");
+               goto set_error;
+       }
+
+       /* Register a disconnect cb if the alert level is non-zero */
+       if (value != NO_ALERT && !condev) {
+               condev = g_new0(struct connected_device, 1);
+               condev->device = btd_device_ref(device);
+               condev->adapter = ia;
+               condev->callback_id = btd_device_add_attio_callback(device,
+                                       NULL, imm_alert_disc_cb, condev);
+               ia->connected_devices = g_slist_append(ia->connected_devices,
+                                                               condev);
+               DBG("added connected dev %p", device);
+       }
+
+       if (value != NO_ALERT) {
+               condev->alert_level = value;
+               imm_alert_emit_alert_signal(condev, value);
+       }
+
+       /*
+        * Emit NO_ALERT if the alert level was non-zero before. This is
+        * guaranteed when there's a condev.
+        */
+       if (value == NO_ALERT && condev)
+               imm_alert_disc_cb(condev);
+
+       DBG("alert level set to %d by device %p", value, device);
+       return 0;
+
+set_error:
+       error("Set immediate alert level for dev %p", device);
+       /* remove alerts by erroneous devices */
+       imm_alert_disc_cb(condev);
+       return ATT_ECODE_IO;
+}
+
+void imm_alert_register(struct btd_adapter *adapter, DBusConnection *conn)
+{
+       gboolean svc_added;
+       bt_uuid_t uuid;
+       struct imm_alert_adapter *imadapter;
+
+       bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID);
+
+       imadapter = g_new0(struct imm_alert_adapter, 1);
+       imadapter->adapter = adapter;
+       imadapter->conn = dbus_connection_ref(conn);
+
+       imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter);
+
+       /* Immediate Alert Service */
+       svc_added = gatt_service_add(adapter,
+                               GATT_PRIM_SVC_UUID, &uuid,
+                               /* Alert level characteristic */
+                               GATT_OPT_CHR_UUID, ALERT_LEVEL_CHR_UUID,
+                               GATT_OPT_CHR_PROPS,
+                                       ATT_CHAR_PROPER_WRITE_WITHOUT_RESP,
+                               GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+                                       imm_alert_alert_lvl_write, imadapter,
+                               GATT_OPT_INVALID);
+
+       if (!svc_added) {
+               imm_alert_unregister(adapter);
+               return;
+       }
+
+       DBG("Immediate Alert service added");
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+       struct connected_device *condev = data;
+
+       imm_alert_remove_condev(condev);
+}
+
+void imm_alert_unregister(struct btd_adapter *adapter)
+{
+       struct imm_alert_adapter *imadapter;
+
+       imadapter = find_imm_alert_adapter(adapter);
+       if (!imadapter)
+               return;
+
+       g_slist_foreach(imadapter->connected_devices, remove_condev_list_item,
+                                                                       NULL);
+       dbus_connection_unref(imadapter->conn);
+
+       imm_alert_adapters = g_slist_remove(imm_alert_adapters, imadapter);
+       g_free(imadapter);
+}
diff --git a/proximity/immalert.h b/proximity/immalert.h
new file mode 100644 (file)
index 0000000..dd28eaa
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Texas Instruments Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void imm_alert_register(struct btd_adapter *adapter, DBusConnection *conn);
+void imm_alert_unregister(struct btd_adapter *adapter);
+const char *imm_alert_get_level(struct btd_device *device);
diff --git a/proximity/linkloss.c b/proximity/linkloss.c
new file mode 100644 (file)
index 0000000..14403cb
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "att-database.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "gatt-service.h"
+#include "attrib-server.h"
+#include "device.h"
+#include "attio.h"
+#include "dbus-common.h"
+#include "reporter.h"
+#include "linkloss.h"
+
+#define BLUEZ_SERVICE "org.bluez"
+
+struct link_loss_adapter {
+       struct btd_adapter *adapter;
+       uint16_t alert_lvl_value_handle;
+       DBusConnection *conn;
+       GSList *connected_devices;
+};
+
+struct connected_device {
+       struct btd_device *device;
+       struct link_loss_adapter *adapter;
+       uint8_t alert_level;
+       guint callback_id;
+       guint local_disc_id;
+};
+
+static GSList *link_loss_adapters;
+
+static int lldevice_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct connected_device *llcondev = a;
+       const struct btd_device *device = b;
+
+       if (llcondev->device == device)
+               return 0;
+
+       return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct link_loss_adapter *la, struct btd_device *device)
+{
+       GSList *l = g_slist_find_custom(la->connected_devices, device,
+                                                               lldevice_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static int lladapter_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct link_loss_adapter *lladapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (lladapter->adapter == adapter)
+               return 0;
+
+       return -1;
+}
+
+static struct link_loss_adapter *
+find_link_loss_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(link_loss_adapters, adapter,
+                                                       lladapter_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+const char *link_loss_get_alert_level(struct btd_device *device)
+{
+       struct link_loss_adapter *lladapter;
+       struct connected_device *condev;
+
+       if (!device)
+               return get_alert_level_string(NO_ALERT);
+
+       lladapter = find_link_loss_adapter(device_get_adapter(device));
+       if (!lladapter)
+               return get_alert_level_string(NO_ALERT);
+
+       condev = find_connected_device(lladapter, device);
+       if (!condev)
+               return get_alert_level_string(NO_ALERT);
+
+       return get_alert_level_string(condev->alert_level);
+}
+
+static void link_loss_emit_alert_signal(struct connected_device *condev)
+{
+       struct link_loss_adapter *adapter = condev->adapter;
+       const char *alert_level_str, *path;
+
+       if (!condev->device)
+               return;
+
+       path = device_get_path(condev->device);
+       alert_level_str = get_alert_level_string(condev->alert_level);
+
+       DBG("alert %s remote %s", alert_level_str, path);
+
+       emit_property_changed(adapter->conn, path,
+                       PROXIMITY_REPORTER_INTERFACE, "LinkLossAlertLevel",
+                       DBUS_TYPE_STRING, &alert_level_str);
+}
+
+static uint8_t link_loss_alert_lvl_read(struct attribute *a,
+                               struct btd_device *device, gpointer user_data)
+{
+       struct link_loss_adapter *la = user_data;
+       struct connected_device *condev;
+       uint8_t alert_level = NO_ALERT;
+
+       if (!device)
+               goto out;
+
+       condev = find_connected_device(la, device);
+       if (!condev)
+               goto out;
+
+       alert_level = condev->alert_level;
+
+out:
+       DBG("return alert level %d for dev %p", alert_level, device);
+
+       /* update the alert level according to the requesting device */
+       attrib_db_update(la->adapter, a->handle, NULL, &alert_level,
+                                               sizeof(alert_level), NULL);
+
+       return 0;
+}
+
+/* condev can be NULL */
+static void link_loss_remove_condev(struct connected_device *condev)
+{
+       struct link_loss_adapter *la;
+
+       if (!condev)
+               return;
+
+       la = condev->adapter;
+
+       if (condev->callback_id && condev->device)
+               btd_device_remove_attio_callback(condev->device,
+                                                       condev->callback_id);
+
+       if (condev->local_disc_id && condev->device)
+               device_remove_disconnect_watch(condev->device,
+                                                       condev->local_disc_id);
+
+       if (condev->device)
+               btd_device_unref(condev->device);
+
+       la->connected_devices = g_slist_remove(la->connected_devices, condev);
+       g_free(condev);
+}
+
+static void link_loss_disc_cb(gpointer user_data)
+{
+       struct connected_device *condev = user_data;
+
+       DBG("alert loss disconnect device %p", condev->device);
+
+       /* if an alert-level is set, emit a signal */
+       if (condev->alert_level != NO_ALERT)
+               link_loss_emit_alert_signal(condev);
+
+       /* we are open for more changes now */
+       link_loss_remove_condev(condev);
+}
+
+static void link_loss_local_disc(struct btd_device *device,
+                                       gboolean removal, void *user_data)
+{
+       struct connected_device *condev = user_data;
+
+       /* no need to alert on this device - we requested disconnection */
+       link_loss_remove_condev(condev);
+
+       DBG("alert level zeroed for locally disconnecting dev %p", device);
+}
+
+static uint8_t link_loss_alert_lvl_write(struct attribute *a,
+                               struct btd_device *device, gpointer user_data)
+{
+       uint8_t value;
+       struct link_loss_adapter *la = user_data;
+       struct connected_device *condev = NULL;
+
+       if (!device)
+               goto set_error;
+
+       /* condev might remain NULL here if nothing is found */
+       condev = find_connected_device(la, device);
+
+       if (a->len == 0) {
+               DBG("Illegal alert level length");
+               goto set_error;
+       }
+
+       value = a->data[0];
+       if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+               DBG("Illegal alert value");
+               goto set_error;
+       }
+
+       /* Register a disconnect cb if the alert level is non-zero */
+       if (value != NO_ALERT && !condev) {
+               condev = g_new0(struct connected_device, 1);
+               condev->device = btd_device_ref(device);
+               condev->adapter = la;
+               condev->callback_id = btd_device_add_attio_callback(device,
+                                       NULL, link_loss_disc_cb, condev);
+               condev->local_disc_id = device_add_disconnect_watch(device,
+                                       link_loss_local_disc, condev, NULL);
+
+               la->connected_devices = g_slist_append(la->connected_devices,
+                                                               condev);
+       } else if (value == NO_ALERT && condev) {
+               link_loss_remove_condev(condev);
+               condev = NULL;
+       }
+
+       DBG("alert level set to %d by device %p", value, device);
+
+       if (condev)
+               condev->alert_level = value;
+
+       return 0;
+
+set_error:
+       error("Set link loss alert level for dev %p", device);
+       /* reset alert level on erroneous devices */
+       link_loss_remove_condev(condev);
+       return ATT_ECODE_IO;
+}
+
+void link_loss_register(struct btd_adapter *adapter, DBusConnection *conn)
+{
+       gboolean svc_added;
+       bt_uuid_t uuid;
+       struct link_loss_adapter *lladapter;
+
+       bt_uuid16_create(&uuid, LINK_LOSS_SVC_UUID);
+
+       lladapter = g_new0(struct link_loss_adapter, 1);
+       lladapter->adapter = adapter;
+       lladapter->conn = dbus_connection_ref(conn);
+
+       link_loss_adapters = g_slist_append(link_loss_adapters, lladapter);
+
+       /* Link Loss Service */
+       svc_added = gatt_service_add(adapter,
+                       GATT_PRIM_SVC_UUID, &uuid,
+                       /* Alert level characteristic */
+                       GATT_OPT_CHR_UUID, ALERT_LEVEL_CHR_UUID,
+                       GATT_OPT_CHR_PROPS,
+                               ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_WRITE,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                               link_loss_alert_lvl_read, lladapter,
+                       GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+                               link_loss_alert_lvl_write, lladapter,
+                       GATT_OPT_CHR_VALUE_GET_HANDLE,
+                               &lladapter->alert_lvl_value_handle,
+                       GATT_OPT_INVALID);
+
+       if (!svc_added)
+               goto err;
+
+       DBG("Link Loss service added");
+       return;
+
+err:
+       error("Error adding Link Loss service");
+       link_loss_unregister(adapter);
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+       struct connected_device *condev = data;
+
+       link_loss_remove_condev(condev);
+}
+
+void link_loss_unregister(struct btd_adapter *adapter)
+{
+       struct link_loss_adapter *lladapter;
+       lladapter = find_link_loss_adapter(adapter);
+       if (!lladapter)
+               return;
+
+       g_slist_foreach(lladapter->connected_devices, remove_condev_list_item,
+                       NULL);
+       dbus_connection_unref(lladapter->conn);
+
+       link_loss_adapters = g_slist_remove(link_loss_adapters, lladapter);
+       g_free(lladapter);
+}
diff --git a/proximity/linkloss.h b/proximity/linkloss.h
new file mode 100644 (file)
index 0000000..a7d83d0
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012  Texas Instruments Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void link_loss_register(struct btd_adapter *adapter, DBusConnection *conn);
+void link_loss_unregister(struct btd_adapter *adapter);
+const char *link_loss_get_alert_level(struct btd_device *device);
diff --git a/proximity/main.c b/proximity/main.c
new file mode 100644 (file)
index 0000000..3d5d9b2
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdint.h>
+#include <glib.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "plugin.h"
+#include "manager.h"
+#include "hcid.h"
+
+static DBusConnection *connection = NULL;
+static GKeyFile *config = NULL;
+
+static GKeyFile *open_config_file(const char *file)
+{
+       GError *gerr = NULL;
+       GKeyFile *keyfile;
+
+       keyfile = g_key_file_new();
+
+       g_key_file_set_list_separator(keyfile, ',');
+
+       if (!g_key_file_load_from_file(keyfile, file, 0, &gerr)) {
+               error("Parsing %s failed: %s", file, gerr->message);
+               g_error_free(gerr);
+               g_key_file_free(keyfile);
+               return NULL;
+       }
+
+       return keyfile;
+}
+
+static int proximity_init(void)
+{
+       if (!main_opts.gatt_enabled) {
+               DBG("GATT is disabled");
+               return -ENOTSUP;
+       }
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -EIO;
+
+       config = open_config_file(CONFIGDIR "/proximity.conf");
+
+       if (proximity_manager_init(connection, config) < 0) {
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void proximity_exit(void)
+{
+       if (!main_opts.gatt_enabled)
+               return;
+
+       if (config)
+               g_key_file_free(config);
+
+       proximity_manager_exit();
+       dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(proximity, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                       proximity_init, proximity_exit)
diff --git a/proximity/manager.c b/proximity/manager.c
new file mode 100644 (file)
index 0000000..f2e49a6
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gdbus.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "monitor.h"
+#include "reporter.h"
+#include "manager.h"
+
+static DBusConnection *connection = NULL;
+
+static struct enabled enabled  = {
+       .linkloss = TRUE,
+       .pathloss = TRUE,
+       .findme = TRUE,
+};
+
+static gint primary_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct gatt_primary *prim = a;
+       const char *uuid = b;
+
+       return g_strcmp0(prim->uuid, uuid);
+}
+
+static int attio_device_probe(struct btd_device *device, GSList *uuids)
+{
+       struct gatt_primary *linkloss, *txpower, *immediate;
+       GSList *l, *primaries;
+
+       primaries = btd_device_get_primaries(device);
+
+       l = g_slist_find_custom(primaries, IMMEDIATE_ALERT_UUID,
+                       primary_uuid_cmp);
+       immediate = (l ? l->data : NULL);
+
+       l = g_slist_find_custom(primaries, TX_POWER_UUID, primary_uuid_cmp);
+       txpower = (l ? l->data : NULL);
+
+       l = g_slist_find_custom(primaries, LINK_LOSS_UUID, primary_uuid_cmp);
+       linkloss = (l ? l->data : NULL);
+
+       return monitor_register(connection, device, linkloss, txpower,
+                                                       immediate, &enabled);
+}
+
+static void attio_device_remove(struct btd_device *device)
+{
+       monitor_unregister(connection, device);
+}
+
+static struct btd_device_driver monitor_driver = {
+       .name = "Proximity GATT Monitor Driver",
+       .uuids = BTD_UUIDS(IMMEDIATE_ALERT_UUID, LINK_LOSS_UUID, TX_POWER_UUID),
+       .probe = attio_device_probe,
+       .remove = attio_device_remove,
+};
+
+static struct btd_adapter_driver reporter_server_driver = {
+       .name = "Proximity GATT Reporter Driver",
+       .probe = reporter_init,
+       .remove = reporter_exit,
+};
+
+static void load_config_file(GKeyFile *config)
+{
+       char **list;
+       int i;
+
+       if (config == NULL)
+               return;
+
+       list = g_key_file_get_string_list(config, "General", "Disable",
+                                                               NULL, NULL);
+       for (i = 0; list && list[i] != NULL; i++) {
+               if (g_str_equal(list[i], "FindMe"))
+                       enabled.findme = FALSE;
+               else if (g_str_equal(list[i], "LinkLoss"))
+                       enabled.linkloss = FALSE;
+               else if (g_str_equal(list[i], "PathLoss"))
+                       enabled.pathloss = FALSE;
+       }
+
+       g_strfreev(list);
+}
+
+int proximity_manager_init(DBusConnection *conn, GKeyFile *config)
+{
+       int ret;
+
+       load_config_file(config);
+
+       connection = dbus_connection_ref(conn);
+
+       ret = btd_register_device_driver(&monitor_driver);
+       if (ret < 0)
+               goto fail_monitor;
+
+       ret = btd_register_adapter_driver(&reporter_server_driver);
+       if (ret < 0)
+               goto fail_reporter;
+
+       return 0;
+
+fail_reporter:
+       btd_unregister_device_driver(&monitor_driver);
+
+fail_monitor:
+       dbus_connection_unref(connection);
+       return ret;
+}
+
+void proximity_manager_exit(void)
+{
+       btd_unregister_device_driver(&monitor_driver);
+       btd_unregister_adapter_driver(&reporter_server_driver);
+       dbus_connection_unref(connection);
+}
diff --git a/proximity/manager.h b/proximity/manager.h
new file mode 100644 (file)
index 0000000..b0fe7c8
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int proximity_manager_init(DBusConnection *conn, GKeyFile *conf);
+void proximity_manager_exit(void);
diff --git a/proximity/monitor.c b/proximity/monitor.c
new file mode 100644 (file)
index 0000000..98dbcd1
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <gdbus.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include "dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+#include "error.h"
+#include "log.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "attio.h"
+#include "monitor.h"
+#include "textfile.h"
+
+#define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor"
+
+#define ALERT_LEVEL_CHR_UUID 0x2A06
+#define POWER_LEVEL_CHR_UUID 0x2A07
+
+#define IMMEDIATE_TIMEOUT      5
+
+enum {
+       ALERT_NONE = 0,
+       ALERT_MILD,
+       ALERT_HIGH,
+};
+
+struct monitor {
+       struct btd_device *device;
+       GAttrib *attrib;
+       DBusConnection *conn;
+       struct att_range *linkloss;
+       struct att_range *txpower;
+       struct att_range *immediate;
+       struct enabled enabled;
+       char *linklosslevel;            /* Link Loss Alert Level */
+       char *fallbacklevel;            /* Immediate fallback alert level */
+       char *immediatelevel;           /* Immediate Alert Level */
+       char *signallevel;              /* Path Loss RSSI level */
+       uint16_t linklosshandle;        /* Link Loss Characteristic
+                                        * Value Handle */
+       uint16_t txpowerhandle;         /* Tx Characteristic Value Handle */
+       uint16_t immediatehandle;       /* Immediate Alert Value Handle */
+       guint immediateto;              /* Reset Immediate Alert to "none" */
+       guint attioid;
+};
+
+static inline int create_filename(char *buf, size_t size,
+                               const bdaddr_t *bdaddr, const char *name)
+{
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+
+       return create_name(buf, size, STORAGEDIR, addr, name);
+}
+
+static int write_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
+                                       const char *alert, const char *level)
+{
+       char filename[PATH_MAX + 1], addr[18], key[38];
+
+       create_filename(filename, PATH_MAX, sba, "proximity");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(dba, addr);
+
+       snprintf(key, sizeof(key), "%17s#%s", addr, alert);
+
+       return textfile_put(filename, key, level);
+}
+
+static char *read_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
+                                                       const char *alert)
+{
+       char filename[PATH_MAX + 1], addr[18], key[38];
+       char *str, *strnew;
+
+       create_filename(filename, PATH_MAX, sba, "proximity");
+
+       ba2str(dba, addr);
+       snprintf(key, sizeof(key), "%17s#%s", addr, alert);
+
+       str = textfile_caseget(filename, key);
+       if (str == NULL)
+               return NULL;
+
+       strnew = g_strdup(str);
+       free(str);
+
+       return strnew;
+}
+
+static uint8_t str2level(const char *level)
+{
+       if (g_strcmp0("high", level) == 0)
+               return ALERT_HIGH;
+       else if (g_strcmp0("mild", level) == 0)
+               return ALERT_MILD;
+
+       return ALERT_NONE;
+}
+
+static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       struct btd_device *device = monitor->device;
+       const char *path = device_get_path(device);
+
+       if (status != 0) {
+               error("Link Loss Write Request failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       if (!dec_write_resp(pdu, plen)) {
+               error("Link Loss Write Request: protocol error");
+               return;
+       }
+
+       DBG("Link Loss Alert Level written");
+
+       emit_property_changed(monitor->conn, path,
+                               PROXIMITY_INTERFACE, "LinkLossAlertLevel",
+                               DBUS_TYPE_STRING, &monitor->linklosslevel);
+}
+
+static void char_discovered_cb(GSList *characteristics, guint8 status,
+                                                       gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       struct gatt_char *chr;
+       uint8_t value = str2level(monitor->linklosslevel);
+
+       if (status) {
+               error("Discover Link Loss handle: %s", att_ecode2str(status));
+               return;
+       }
+
+       DBG("Setting alert level \"%s\" on Reporter", monitor->linklosslevel);
+
+       /* Assume there is a single Alert Level characteristic */
+       chr = characteristics->data;
+       monitor->linklosshandle = chr->value_handle;
+
+       gatt_write_char(monitor->attrib, monitor->linklosshandle, &value, 1,
+                                               linkloss_written, monitor);
+}
+
+static int write_alert_level(struct monitor *monitor)
+{
+       struct att_range *linkloss = monitor->linkloss;
+       bt_uuid_t uuid;
+
+       if (monitor->linklosshandle) {
+               uint8_t value = str2level(monitor->linklosslevel);
+
+               gatt_write_char(monitor->attrib, monitor->linklosshandle,
+                                       &value, 1, linkloss_written, monitor);
+               return 0;
+       }
+
+       bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+       /* FIXME: use cache (requires service changed support) ? */
+       gatt_discover_char(monitor->attrib, linkloss->start, linkloss->end,
+                                       &uuid, char_discovered_cb, monitor);
+
+       return 0;
+}
+
+static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       uint8_t value[ATT_MAX_MTU];
+       int vlen;
+
+       if (status != 0) {
+               DBG("Tx Power Level read failed: %s", att_ecode2str(status));
+               return;
+       }
+
+       if (!dec_read_resp(pdu, plen, value, &vlen)) {
+               DBG("Protocol error");
+               return;
+       }
+
+       if (vlen != 1) {
+               DBG("Invalid length for TX Power value: %d", vlen);
+               return;
+       }
+
+       DBG("Tx Power Level: %02x", (int8_t) value[0]);
+}
+
+static void tx_power_handle_cb(GSList *characteristics, guint8 status,
+                                                       gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       struct gatt_char *chr;
+
+       if (status) {
+               error("Discover Tx Power handle: %s", att_ecode2str(status));
+               return;
+       }
+
+       chr = characteristics->data;
+       monitor->txpowerhandle = chr->value_handle;
+
+       DBG("Tx Power handle: 0x%04x", monitor->txpowerhandle);
+
+       gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
+                                                       tx_power_read_cb, monitor);
+}
+
+static void read_tx_power(struct monitor *monitor)
+{
+       struct att_range *txpower = monitor->txpower;
+       bt_uuid_t uuid;
+
+       if (monitor->txpowerhandle != 0) {
+               gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
+                                               tx_power_read_cb, monitor);
+               return;
+       }
+
+       bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+
+       gatt_discover_char(monitor->attrib, txpower->start, txpower->end,
+                               &uuid, tx_power_handle_cb, monitor);
+}
+
+static gboolean immediate_timeout(gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       const char *path = device_get_path(monitor->device);
+
+       monitor->immediateto = 0;
+
+       if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+               return FALSE;
+
+       if (monitor->attrib) {
+               uint8_t value = ALERT_NONE;
+               gatt_write_cmd(monitor->attrib, monitor->immediatehandle,
+                               &value, 1, NULL, NULL);
+       }
+
+       g_free(monitor->immediatelevel);
+       monitor->immediatelevel = g_strdup("none");
+       emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
+                                       "ImmediateAlertLevel", DBUS_TYPE_STRING,
+                                       &monitor->immediatelevel);
+
+       return FALSE;
+}
+
+static void immediate_written(gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       const char *path = device_get_path(monitor->device);
+
+       g_free(monitor->fallbacklevel);
+       monitor->fallbacklevel = NULL;
+
+       emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
+                               "ImmediateAlertLevel",
+                               DBUS_TYPE_STRING, &monitor->immediatelevel);
+
+       monitor->immediateto = g_timeout_add_seconds(IMMEDIATE_TIMEOUT,
+                                               immediate_timeout, monitor);
+}
+
+static void write_immediate_alert(struct monitor *monitor)
+{
+       uint8_t value = str2level(monitor->immediatelevel);
+
+       gatt_write_cmd(monitor->attrib, monitor->immediatehandle, &value, 1,
+                                               immediate_written, monitor);
+}
+
+static void immediate_handle_cb(GSList *characteristics, guint8 status,
+                                                       gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       struct gatt_char *chr;
+
+       if (status) {
+               error("Discover Immediate Alert handle: %s",
+                                               att_ecode2str(status));
+               return;
+       }
+
+       chr = characteristics->data;
+       monitor->immediatehandle = chr->value_handle;
+
+       DBG("Immediate Alert handle: 0x%04x", monitor->immediatehandle);
+
+       if (monitor->fallbacklevel)
+               write_immediate_alert(monitor);
+}
+
+static void discover_immediate_handle(struct monitor *monitor)
+{
+       struct att_range *immediate = monitor->immediate;
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+       gatt_discover_char(monitor->attrib, immediate->start, immediate->end,
+                                       &uuid, immediate_handle_cb, monitor);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+
+       monitor->attrib = g_attrib_ref(attrib);
+
+       if (monitor->enabled.linkloss)
+               write_alert_level(monitor);
+
+       if (monitor->enabled.pathloss)
+               read_tx_power(monitor);
+
+       if (monitor->immediatehandle == 0) {
+               if(monitor->enabled.pathloss || monitor->enabled.findme)
+                       discover_immediate_handle(monitor);
+       } else if (monitor->fallbacklevel)
+               write_immediate_alert(monitor);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+       const char *path = device_get_path(monitor->device);
+
+       g_attrib_unref(monitor->attrib);
+       monitor->attrib = NULL;
+
+       if (monitor->immediateto == 0)
+               return;
+
+       g_source_remove(monitor->immediateto);
+       monitor->immediateto = 0;
+
+       if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+               return;
+
+       g_free(monitor->immediatelevel);
+       monitor->immediatelevel = g_strdup("none");
+       emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
+                                       "ImmediateAlertLevel", DBUS_TYPE_STRING,
+                                       &monitor->immediatelevel);
+}
+
+static gboolean level_is_valid(const char *level)
+{
+       return (g_str_equal("none", level) ||
+                       g_str_equal("mild", level) ||
+                       g_str_equal("high", level));
+}
+
+static DBusMessage *set_link_loss_alert(DBusConnection *conn, DBusMessage *msg,
+                                               const char *level, void *data)
+{
+       struct monitor *monitor = data;
+       struct btd_device *device = monitor->device;
+       bdaddr_t sba, dba;
+
+       if (!level_is_valid(level))
+               return btd_error_invalid_args(msg);
+
+       if (g_strcmp0(monitor->linklosslevel, level) == 0)
+               return dbus_message_new_method_return(msg);
+
+       g_free(monitor->linklosslevel);
+       monitor->linklosslevel = g_strdup(level);
+
+       adapter_get_address(device_get_adapter(device), &sba);
+       device_get_address(device, &dba, NULL);
+
+       write_proximity_config(&sba, &dba, "LinkLossAlertLevel", level);
+
+       if (monitor->attrib)
+               write_alert_level(monitor);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_immediate_alert(DBusConnection *conn, DBusMessage *msg,
+                                               const char *level, void *data)
+{
+       struct monitor *monitor = data;
+
+       if (!level_is_valid(level))
+               return btd_error_invalid_args(msg);
+
+       if (g_strcmp0(monitor->immediatelevel, level) == 0)
+               return dbus_message_new_method_return(msg);
+
+       if (monitor->immediateto) {
+               g_source_remove(monitor->immediateto);
+               monitor->immediateto = 0;
+       }
+
+       /* Previous Immediate Alert level if connection/write fails */
+       g_free(monitor->fallbacklevel);
+       monitor->fallbacklevel = monitor->immediatelevel;
+
+       monitor->immediatelevel = g_strdup(level);
+
+       /*
+        * Means that Link/Path Loss are disabled or there is a pending
+        * writting for Find Me(Immediate Alert characteristic value).
+        * If enabled, Path Loss always registers a connection callback
+        * when the Proximity Monitor starts.
+        */
+       if (monitor->attioid == 0)
+               monitor->attioid = btd_device_add_attio_callback(monitor->device,
+                                                       attio_connected_cb,
+                                                       attio_disconnected_cb,
+                                                       monitor);
+       else if (monitor->attrib)
+               write_immediate_alert(monitor);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct monitor *monitor = data;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       if (monitor->enabled.linkloss)
+               dict_append_entry(&dict, "LinkLossAlertLevel",
+                               DBUS_TYPE_STRING, &monitor->linklosslevel);
+
+       if (monitor->enabled.findme || monitor->enabled.pathloss)
+               dict_append_entry(&dict, "ImmediateAlertLevel",
+                               DBUS_TYPE_STRING, &monitor->immediatelevel);
+
+       if (monitor->enabled.pathloss)
+               dict_append_entry(&dict, "SignalLevel",
+                               DBUS_TYPE_STRING, &monitor->signallevel);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct monitor *monitor = data;
+       const char *property;
+       DBusMessageIter iter;
+       DBusMessageIter sub;
+       const char *level;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return btd_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &sub);
+
+       if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&sub, &level);
+
+       if (g_str_equal("ImmediateAlertLevel", property)) {
+               if (monitor->enabled.findme == FALSE &&
+                               monitor->enabled.pathloss == FALSE)
+                       return btd_error_not_available(msg);
+
+               return set_immediate_alert(conn, msg, level, data);
+       } else if (g_str_equal("LinkLossAlertLevel", property)) {
+               if (monitor->enabled.linkloss == FALSE)
+                       return btd_error_not_available(msg);
+
+               return set_link_loss_alert(conn, msg, level, data);
+       }
+
+       return btd_error_invalid_args(msg);
+}
+
+static const GDBusMethodTable monitor_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_ASYNC_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+                       set_property) },
+       { }
+};
+
+static const GDBusSignalTable monitor_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static void monitor_destroy(gpointer user_data)
+{
+       struct monitor *monitor = user_data;
+
+       if (monitor->immediateto)
+               g_source_remove(monitor->immediateto);
+
+       if (monitor->attioid)
+               btd_device_remove_attio_callback(monitor->device,
+                                               monitor->attioid);
+       if (monitor->attrib)
+               g_attrib_unref(monitor->attrib);
+
+       dbus_connection_unref(monitor->conn);
+       btd_device_unref(monitor->device);
+       g_free(monitor->linkloss);
+       g_free(monitor->immediate);
+       g_free(monitor->txpower);
+       g_free(monitor->linklosslevel);
+       g_free(monitor->immediatelevel);
+       g_free(monitor->signallevel);
+       g_free(monitor);
+}
+
+int monitor_register(DBusConnection *conn, struct btd_device *device,
+               struct gatt_primary *linkloss, struct gatt_primary *txpower,
+               struct gatt_primary *immediate, struct enabled *enabled)
+{
+       const char *path = device_get_path(device);
+       struct monitor *monitor;
+       bdaddr_t sba, dba;
+       char *level;
+
+       adapter_get_address(device_get_adapter(device), &sba);
+       device_get_address(device, &dba, NULL);
+
+       level = read_proximity_config(&sba, &dba, "LinkLossAlertLevel");
+
+       monitor = g_new0(struct monitor, 1);
+       monitor->device = btd_device_ref(device);
+       monitor->conn = dbus_connection_ref(conn);
+       monitor->linklosslevel = (level ? : g_strdup("high"));
+       monitor->signallevel = g_strdup("unknown");
+       monitor->immediatelevel = g_strdup("none");
+
+       if (g_dbus_register_interface(conn, path,
+                               PROXIMITY_INTERFACE,
+                               monitor_methods, monitor_signals,
+                               NULL, monitor, monitor_destroy) == FALSE) {
+               error("D-Bus failed to register %s interface",
+                                               PROXIMITY_INTERFACE);
+               monitor_destroy(monitor);
+               return -1;
+       }
+
+       DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
+
+       if (linkloss && enabled->linkloss) {
+               monitor->linkloss = g_new0(struct att_range, 1);
+               monitor->linkloss->start = linkloss->range.start;
+               monitor->linkloss->end = linkloss->range.end;
+
+               monitor->enabled.linkloss = TRUE;
+       }
+
+       if (immediate) {
+               if (txpower && enabled->pathloss) {
+                       monitor->txpower = g_new0(struct att_range, 1);
+                       monitor->txpower->start = txpower->range.start;
+                       monitor->txpower->end = txpower->range.end;
+
+                       monitor->enabled.pathloss = TRUE;
+               }
+
+               if (enabled->pathloss || enabled->findme) {
+                       monitor->immediate = g_new0(struct att_range, 1);
+                       monitor->immediate->start = immediate->range.start;
+                       monitor->immediate->end = immediate->range.end;
+               }
+
+               monitor->enabled.findme = enabled->findme;
+       }
+
+       DBG("Link Loss: %s, Path Loss: %s, FindMe: %s",
+                               monitor->enabled.linkloss ? "TRUE" : "FALSE",
+                               monitor->enabled.pathloss ? "TRUE" : "FALSE",
+                               monitor->enabled.findme ? "TRUE" : "FALSE");
+
+       if (monitor->enabled.linkloss || monitor->enabled.pathloss)
+               monitor->attioid = btd_device_add_attio_callback(device,
+                                                       attio_connected_cb,
+                                                       attio_disconnected_cb,
+                                                       monitor);
+
+       return 0;
+}
+
+void monitor_unregister(DBusConnection *conn, struct btd_device *device)
+{
+       const char *path = device_get_path(device);
+
+       g_dbus_unregister_interface(conn, path, PROXIMITY_INTERFACE);
+}
diff --git a/proximity/monitor.h b/proximity/monitor.h
new file mode 100644 (file)
index 0000000..b71363d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct enabled {
+       gboolean linkloss;
+       gboolean pathloss;
+       gboolean findme;
+};
+
+int monitor_register(DBusConnection *conn, struct btd_device *device,
+               struct gatt_primary *linkloss, struct gatt_primary *txpower,
+               struct gatt_primary *immediate, struct enabled *enabled);
+void monitor_unregister(DBusConnection *conn, struct btd_device *device);
diff --git a/proximity/proximity.conf b/proximity/proximity.conf
new file mode 100644 (file)
index 0000000..417610f
--- /dev/null
@@ -0,0 +1,9 @@
+# Configuration file for the proximity service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# Configuration to allow disabling Proximity services
+# Allowed values: LinkLoss,PathLoss,FindMe
+Disable=PathLoss
diff --git a/proximity/reporter.c b/proximity/reporter.c
new file mode 100644 (file)
index 0000000..607de2b
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+#include <errno.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "dbus-common.h"
+#include "error.h"
+#include "device.h"
+#include "hcid.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
+#include "attrib-server.h"
+#include "reporter.h"
+#include "linkloss.h"
+#include "immalert.h"
+
+#define BLUEZ_SERVICE "org.bluez"
+
+struct reporter_adapter {
+       DBusConnection *conn;
+       struct btd_adapter *adapter;
+       GSList *devices;
+};
+
+static GSList *reporter_adapters;
+
+static int radapter_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct reporter_adapter *radapter = a;
+       const struct btd_adapter *adapter = b;
+
+       if (radapter->adapter == adapter)
+               return 0;
+
+       return -1;
+}
+
+static struct reporter_adapter *
+find_reporter_adapter(struct btd_adapter *adapter)
+{
+       GSList *l = g_slist_find_custom(reporter_adapters, adapter,
+                                                               radapter_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+const char *get_alert_level_string(uint8_t level)
+{
+       switch (level) {
+       case NO_ALERT:
+               return "none";
+       case MILD_ALERT:
+               return "mild";
+       case HIGH_ALERT:
+               return "high";
+       }
+
+       return "unknown";
+}
+
+static void register_tx_power(struct btd_adapter *adapter)
+{
+       uint16_t start_handle, h;
+       const int svc_size = 4;
+       uint8_t atval[256];
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, TX_POWER_SVC_UUID);
+       start_handle = attrib_db_find_avail(adapter, &uuid, svc_size);
+       if (start_handle == 0) {
+               error("Not enough free handles to register service");
+               return;
+       }
+
+       DBG("start_handle=0x%04x", start_handle);
+
+       h = start_handle;
+
+       /* Primary service definition */
+       bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+       att_put_u16(TX_POWER_SVC_UUID, &atval[0]);
+       attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+       /* Power level characteristic */
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_NOTIFY;
+       att_put_u16(h + 1, &atval[1]);
+       att_put_u16(POWER_LEVEL_CHR_UUID, &atval[3]);
+       attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+       /* Power level value */
+       bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+       att_put_u8(0x00, &atval[0]);
+       attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
+
+       /* Client characteristic configuration */
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       atval[0] = 0x00;
+       atval[1] = 0x00;
+       attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2);
+
+       g_assert(h - start_handle == svc_size);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusMessage *reply = NULL;
+       const char *linkloss_level, *immalert_level;
+       struct btd_device *device = data;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       linkloss_level = link_loss_get_alert_level(device);
+       immalert_level = imm_alert_get_level(device);
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict))
+               goto err;
+
+       dict_append_entry(&dict, "LinkLossAlertLevel", DBUS_TYPE_STRING,
+                                                       &linkloss_level);
+       dict_append_entry(&dict, "ImmediateAlertLevel", DBUS_TYPE_STRING,
+                                                       &immalert_level);
+
+       if (!dbus_message_iter_close_container(&iter, &dict))
+               goto err;
+
+       return reply;
+
+err:
+       if (reply)
+               dbus_message_unref(reply);
+       return btd_error_failed(msg, "not enough memory");
+}
+
+static const GDBusMethodTable reporter_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { }
+};
+
+static const GDBusSignalTable reporter_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static void unregister_reporter_device(gpointer data, gpointer user_data)
+{
+       struct btd_device *device = data;
+       struct reporter_adapter *radapter = user_data;
+       const char *path = device_get_path(device);
+
+       DBG("unregister on device %s", path);
+
+       g_dbus_unregister_interface(radapter->conn, path,
+                                       PROXIMITY_REPORTER_INTERFACE);
+
+       radapter->devices = g_slist_remove(radapter->devices, device);
+       btd_device_unref(device);
+}
+
+static void register_reporter_device(struct btd_device *device,
+                                       struct reporter_adapter *radapter)
+{
+       const char *path = device_get_path(device);
+
+       DBG("register on device %s", path);
+
+       g_dbus_register_interface(radapter->conn, path,
+                                       PROXIMITY_REPORTER_INTERFACE,
+                                       reporter_methods, reporter_signals,
+                                       NULL, device, NULL);
+
+       btd_device_ref(device);
+       radapter->devices = g_slist_prepend(radapter->devices, device);
+}
+
+static int reporter_device_probe(struct btd_device *device, GSList *uuids)
+{
+       struct reporter_adapter *radapter;
+       struct btd_adapter *adapter = device_get_adapter(device);
+
+       radapter = find_reporter_adapter(adapter);
+       if (!radapter)
+               return -1;
+
+       register_reporter_device(device, radapter);
+       return 0;
+}
+
+static void reporter_device_remove(struct btd_device *device)
+{
+       struct reporter_adapter *radapter;
+       struct btd_adapter *adapter = device_get_adapter(device);
+
+       radapter = find_reporter_adapter(adapter);
+       if (!radapter)
+               return;
+
+       unregister_reporter_device(device, radapter);
+}
+
+/* device driver for tracking remote GATT client devices */
+static struct btd_device_driver reporter_device_driver = {
+       .name = "Proximity GATT Reporter Device Tracker Driver",
+       .uuids = BTD_UUIDS(GATT_UUID),
+       .probe = reporter_device_probe,
+       .remove = reporter_device_remove,
+};
+
+int reporter_init(struct btd_adapter *adapter)
+{
+       struct reporter_adapter *radapter;
+       DBusConnection *conn;
+
+       if (!main_opts.gatt_enabled) {
+               DBG("GATT is disabled");
+               return -ENOTSUP;
+       }
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (!conn)
+               return -1;
+
+       radapter = g_new0(struct reporter_adapter, 1);
+       radapter->adapter = adapter;
+       radapter->conn = conn;
+
+       link_loss_register(adapter, radapter->conn);
+       register_tx_power(adapter);
+       imm_alert_register(adapter, radapter->conn);
+
+       btd_register_device_driver(&reporter_device_driver);
+
+       reporter_adapters = g_slist_prepend(reporter_adapters, radapter);
+       DBG("Proximity Reporter for adapter %p", adapter);
+
+       return 0;
+}
+
+void reporter_exit(struct btd_adapter *adapter)
+{
+       struct reporter_adapter *radapter = find_reporter_adapter(adapter);
+       if (!radapter)
+               return;
+
+       btd_unregister_device_driver(&reporter_device_driver);
+
+       g_slist_foreach(radapter->devices, unregister_reporter_device,
+                                                               radapter);
+
+       link_loss_unregister(adapter);
+       imm_alert_unregister(adapter);
+       dbus_connection_unref(radapter->conn);
+
+       reporter_adapters = g_slist_remove(reporter_adapters, radapter);
+       g_free(radapter);
+}
diff --git a/proximity/reporter.h b/proximity/reporter.h
new file mode 100644 (file)
index 0000000..5ae0eb2
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define PROXIMITY_REPORTER_INTERFACE "org.bluez.ProximityReporter"
+
+#define IMMEDIATE_ALERT_SVC_UUID       0x1802
+#define LINK_LOSS_SVC_UUID             0x1803
+#define TX_POWER_SVC_UUID              0x1804
+#define ALERT_LEVEL_CHR_UUID           0x2A06
+#define POWER_LEVEL_CHR_UUID           0x2A07
+
+enum {
+       NO_ALERT = 0x00,
+       MILD_ALERT = 0x01,
+       HIGH_ALERT = 0x02,
+};
+
+int reporter_init(struct btd_adapter *adapter);
+void reporter_exit(struct btd_adapter *adapter);
+
+const char *get_alert_level_string(uint8_t level);
diff --git a/sap/main.c b/sap/main.c
new file mode 100644 (file)
index 0000000..c9c90bd
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <gdbus.h>
+#include "plugin.h"
+#include "manager.h"
+
+static DBusConnection *connection;
+
+static int sap_init(void)
+{
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+       if (!connection)
+               return -EIO;
+
+       if (sap_manager_init(connection) < 0) {
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void sap_exit(void)
+{
+       sap_manager_exit();
+
+       dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(sap, VERSION,
+               BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, sap_init, sap_exit)
diff --git a/sap/manager.c b/sap/manager.c
new file mode 100644 (file)
index 0000000..9fa9c56
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "log.h"
+#include "adapter.h"
+
+#include "manager.h"
+#include "server.h"
+
+static DBusConnection *connection = NULL;
+
+static int sap_server_probe(struct btd_adapter *adapter)
+{
+       const char *path = adapter_get_path(adapter);
+       bdaddr_t src;
+
+       DBG("path %s", path);
+
+       adapter_get_address(adapter, &src);
+
+       return sap_server_register(path, &src);
+}
+
+static void sap_server_remove(struct btd_adapter *adapter)
+{
+       const char *path = adapter_get_path(adapter);
+
+       DBG("path %s", path);
+
+       sap_server_unregister(path);
+}
+
+static struct btd_adapter_driver sap_server_driver = {
+       .name   = "sap-server",
+       .probe  = sap_server_probe,
+       .remove = sap_server_remove,
+};
+
+int sap_manager_init(DBusConnection *conn)
+{
+       connection = dbus_connection_ref(conn);
+
+       if (sap_server_init(connection) < 0) {
+               error("Can't init SAP server");
+               dbus_connection_unref(conn);
+               return -1;
+       }
+
+       btd_register_adapter_driver(&sap_server_driver);
+
+       return 0;
+}
+
+void sap_manager_exit(void)
+{
+       btd_unregister_adapter_driver(&sap_server_driver);
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+
+       sap_server_exit();
+}
diff --git a/sap/manager.h b/sap/manager.h
new file mode 100644 (file)
index 0000000..e08c882
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+int sap_manager_init(DBusConnection *conn);
+void sap_manager_exit(void);
diff --git a/sap/sap-dummy.c b/sap/sap-dummy.c
new file mode 100644 (file)
index 0000000..7ea4e92
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *  Copyright (C) 2011 Tieto Poland
+ *
+ *  Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
+ *          for ST-Ericsson
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "sap.h"
+
+#define SAP_DUMMY_IFACE "org.bluez.SimAccessTest"
+#define SAP_DUMMY_PATH "/org/bluez/test"
+
+enum {
+       SIM_DISCONNECTED = 0x00,
+       SIM_CONNECTED    = 0x01,
+       SIM_POWERED_OFF  = 0x02,
+       SIM_MISSING      = 0x03
+};
+
+static DBusConnection *connection = NULL;
+
+static int sim_card_conn_status = SIM_DISCONNECTED;
+static void *sap_data = NULL; /* SAP server private data. */
+static gboolean ongoing_call_status = FALSE;
+static int max_msg_size_supported = 512;
+
+void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
+{
+       DBG("status: %d", sim_card_conn_status);
+
+       if (sim_card_conn_status != SIM_DISCONNECTED) {
+               sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED,
+                                                               maxmsgsize);
+               return;
+       }
+
+       if (max_msg_size_supported > maxmsgsize) {
+               sap_connect_rsp(sap_device, SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL,
+                                               max_msg_size_supported);
+               return;
+       }
+
+       if (max_msg_size_supported < maxmsgsize) {
+               sap_connect_rsp(sap_device,
+                               SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED,
+                               max_msg_size_supported);
+               return;
+       }
+
+       if (ongoing_call_status) {
+               sap_connect_rsp(sap_device, SAP_STATUS_OK_ONGOING_CALL,
+                                               max_msg_size_supported);
+               return;
+       }
+
+       sim_card_conn_status = SIM_CONNECTED;
+       sap_data = sap_device;
+
+       sap_connect_rsp(sap_device, SAP_STATUS_OK, maxmsgsize);
+       sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+}
+
+void sap_disconnect_req(void *sap_device, uint8_t linkloss)
+{
+       sim_card_conn_status = SIM_DISCONNECTED;
+       sap_data = NULL;
+       ongoing_call_status = FALSE;
+
+       DBG("status: %d", sim_card_conn_status);
+
+       if (linkloss)
+               return;
+
+       sap_disconnect_rsp(sap_device);
+}
+
+void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
+{
+       char apdu[] = "APDU response!";
+
+       DBG("status: %d", sim_card_conn_status);
+
+       if (sim_card_conn_status == SIM_MISSING)
+               sap_transfer_apdu_rsp(sap_device,
+                               SAP_RESULT_ERROR_CARD_REMOVED, NULL, 0);
+       else if (sim_card_conn_status == SIM_POWERED_OFF)
+               sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
+                                                               NULL, 0);
+       else if (sim_card_conn_status != SIM_CONNECTED)
+               sap_transfer_apdu_rsp(sap_device,
+                       SAP_RESULT_ERROR_NOT_ACCESSIBLE, NULL, 0);
+       else
+               sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK,
+                                               (uint8_t *)&apdu, sizeof(apdu));
+}
+
+void sap_transfer_atr_req(void *sap_device)
+{
+       char atr[] = "ATR response!";
+
+       DBG("status: %d", sim_card_conn_status);
+
+       if (sim_card_conn_status == SIM_MISSING)
+               sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED,
+                                                               NULL, 0);
+       else if (sim_card_conn_status == SIM_POWERED_OFF)
+               sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
+                                                               NULL, 0);
+       else if (sim_card_conn_status != SIM_CONNECTED)
+               sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON,
+                                                               NULL, 0);
+       else
+               sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK,
+                                               (uint8_t *)&atr, sizeof(atr));
+}
+
+void sap_power_sim_off_req(void *sap_device)
+{
+       DBG("status: %d", sim_card_conn_status);
+
+       if (sim_card_conn_status == SIM_MISSING) {
+               sap_power_sim_off_rsp(sap_device,
+                                       SAP_RESULT_ERROR_CARD_REMOVED);
+       } else if (sim_card_conn_status == SIM_POWERED_OFF) {
+               sap_power_sim_off_rsp(sap_device,
+                                       SAP_RESULT_ERROR_POWERED_OFF);
+       } else if (sim_card_conn_status != SIM_CONNECTED) {
+               sap_power_sim_off_rsp(sap_device,
+                                       SAP_RESULT_ERROR_NO_REASON);
+       } else {
+               sap_power_sim_off_rsp(sap_device, SAP_RESULT_OK);
+               sim_card_conn_status = SIM_POWERED_OFF;
+       }
+}
+
+void sap_power_sim_on_req(void *sap_device)
+{
+       DBG("status: %d", sim_card_conn_status);
+
+       if (sim_card_conn_status == SIM_MISSING) {
+               sap_power_sim_on_rsp(sap_device,
+                                       SAP_RESULT_ERROR_CARD_REMOVED);
+       } else if (sim_card_conn_status == SIM_POWERED_OFF) {
+               sap_power_sim_on_rsp(sap_device, SAP_RESULT_OK);
+               sim_card_conn_status = SIM_CONNECTED;
+               return;
+       } else if (sim_card_conn_status != SIM_CONNECTED) {
+               sap_power_sim_on_rsp(sap_device,
+                                       SAP_RESULT_ERROR_NOT_ACCESSIBLE);
+       } else {
+               sap_power_sim_on_rsp(sap_device,
+                                       SAP_RESULT_ERROR_NO_REASON);
+       }
+}
+
+void sap_reset_sim_req(void *sap_device)
+{
+       DBG("status: %d", sim_card_conn_status);
+
+       if (sim_card_conn_status == SIM_MISSING) {
+               sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED);
+       } else if (sim_card_conn_status == SIM_POWERED_OFF) {
+               sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF);
+       } else if (sim_card_conn_status != SIM_CONNECTED) {
+               sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+       } else {
+               sap_reset_sim_rsp(sap_device, SAP_RESULT_OK);
+       }
+}
+
+void sap_transfer_card_reader_status_req(void *sap_device)
+{
+       DBG("status: %d", sim_card_conn_status);
+
+       if (sim_card_conn_status != SIM_CONNECTED) {
+               sap_transfer_card_reader_status_rsp(sap_device,
+                                       SAP_RESULT_ERROR_NO_REASON, 0xF1);
+               return;
+       }
+
+       sap_transfer_card_reader_status_rsp(sap_device, SAP_RESULT_OK, 0xF1);
+}
+
+void sap_set_transport_protocol_req(void *sap_device,
+                                       struct sap_parameter *param)
+{
+       sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
+}
+
+static inline DBusMessage *invalid_args(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
+                                       "Invalid arguments in method call");
+}
+
+static DBusMessage *ongoing_call(DBusConnection *conn, DBusMessage *msg,
+                                               void *data)
+{
+       dbus_bool_t ongoing;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &ongoing,
+                                               DBUS_TYPE_INVALID))
+               return invalid_args(msg);
+
+       if (ongoing_call_status && !ongoing) {
+               /* An ongoing call has finished. Continue connection.*/
+               sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_RESET);
+               ongoing_call_status = FALSE;
+       } else if (!ongoing_call_status && ongoing) {
+               /* An ongoing call has started.*/
+               ongoing_call_status = TRUE;
+       }
+
+       DBG("OngoingCall status set to %d", ongoing_call_status);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *max_msg_size(DBusConnection *conn, DBusMessage *msg,
+                                               void *data)
+{
+       dbus_uint32_t size;
+
+       if (sim_card_conn_status == SIM_CONNECTED)
+               return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+                               "Can't change msg size when connected.");
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &size,
+                                                       DBUS_TYPE_INVALID))
+               return invalid_args(msg);
+
+       max_msg_size_supported = size;
+
+       DBG("MaxMessageSize set to %d", max_msg_size_supported);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disconnect_immediate(DBusConnection *conn, DBusMessage *msg,
+                                               void *data)
+{
+       if (sim_card_conn_status == SIM_DISCONNECTED)
+               return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+                               "Already disconnected.");
+
+       sim_card_conn_status = SIM_DISCONNECTED;
+       sap_disconnect_ind(sap_data, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *card_status(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       dbus_uint32_t status;
+
+       DBG("status %d", sim_card_conn_status);
+
+       if (sim_card_conn_status != SIM_CONNECTED)
+               return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+                               "Can't change msg size when not connected.");
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &status,
+                                                       DBUS_TYPE_INVALID))
+               return invalid_args(msg);
+
+       switch (status) {
+       case 0: /* card removed */
+               sim_card_conn_status = SIM_MISSING;
+               sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_REMOVED);
+               break;
+
+       case 1: /* card inserted */
+               if (sim_card_conn_status == SIM_MISSING) {
+                       sim_card_conn_status = SIM_CONNECTED;
+                       sap_status_ind(sap_data,
+                                       SAP_STATUS_CHANGE_CARD_INSERTED);
+               }
+               break;
+
+       case 2: /* card not longer available*/
+               sim_card_conn_status = SIM_POWERED_OFF;
+               sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE);
+               break;
+
+       default:
+               return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+                               "Unknown card status. Use 0, 1 or 2.");
+       }
+
+       DBG("Card status changed to %d", status);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable dummy_methods[] = {
+       { GDBUS_METHOD("OngoingCall",
+                               GDBUS_ARGS({ "ongoing", "b" }), NULL,
+                               ongoing_call) },
+       { GDBUS_METHOD("MaxMessageSize",
+                               GDBUS_ARGS({ "size", "u" }), NULL,
+                               max_msg_size) },
+       { GDBUS_METHOD("DisconnectImmediate", NULL, NULL,
+                               disconnect_immediate) },
+       { GDBUS_METHOD("CardStatus",
+                               GDBUS_ARGS({ "status", "" }), NULL,
+                               card_status) },
+       { }
+};
+
+int sap_init(void)
+{
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+       if (g_dbus_register_interface(connection, SAP_DUMMY_PATH,
+                               SAP_DUMMY_IFACE, dummy_methods, NULL, NULL,
+                               NULL, NULL) == FALSE) {
+               error("sap-dummy interface %s init failed on path %s",
+                                       SAP_DUMMY_IFACE, SAP_DUMMY_PATH);
+               dbus_connection_unref(connection);
+               connection = NULL;
+               return -1;
+       }
+
+       return 0;
+}
+
+void sap_exit(void)
+{
+       g_dbus_unregister_interface(connection, SAP_DUMMY_PATH,
+                                                       SAP_DUMMY_IFACE);
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
diff --git a/sap/sap-u8500.c b/sap/sap-u8500.c
new file mode 100644 (file)
index 0000000..2920cc7
--- /dev/null
@@ -0,0 +1,761 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  SAP Driver for ST-Ericsson U8500 platform
+ *
+ *  Copyright (C) 2010-2011 ST-Ericsson SA
+ *
+ *  Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com> for
+ *  ST-Ericsson.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <glib.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "log.h"
+#include "sap.h"
+
+#define STE_SIMD_SOCK  "/dev/socket/catd_a"
+#define STE_CLIENT_TAG 0x0000
+
+#ifdef STE_SAP_DEBUG
+#define DBG_VERBOSE(fmt, arg...) DBG(fmt, arg)
+#else
+#define DBG_VERBOSE(fmt...)
+#endif
+
+#define sap_error(fmt, arg...) do { \
+       error("STE U8500 SAP: " fmt, ## arg); \
+       } while (0)
+
+#define sap_info(fmt, arg...) do { \
+       info("STE U8500 SAP: " fmt, ## arg); \
+       } while (0)
+
+struct ste_message {
+       uint16_t len;
+       uint16_t id;
+       uint32_t client_tag;
+       uint8_t payload[0];
+} __attribute__((packed));
+
+#define STE_MSG_PAYLOAD_SIZE(msg) (msg->len - sizeof(*msg) + sizeof(msg->len))
+
+enum ste_protocol {
+       STE_START_SAP_REQ       = 0x2D01,
+       STE_START_SAP_RSP       = 0x2E01,
+       STE_END_SAP_REQ         = 0x2D02,
+       STE_END_SAP_RSP         = 0x2E02,
+       STE_POWER_OFF_REQ       = 0x2D03,
+       STE_POWER_OFF_RSP       = 0x2E03,
+       STE_POWER_ON_REQ        = 0x2D04,
+       STE_POWER_ON_RSP        = 0x2E04,
+       STE_RESET_REQ           = 0x2D05,
+       STE_RESET_RSP           = 0x2E05,
+       STE_SEND_APDU_REQ       = 0x2D06,
+       STE_SEND_APDU_RSP       = 0x2E06,
+       STE_GET_ATR_REQ         = 0x2D07,
+       STE_GET_ATR_RSP         = 0x2E07,
+       STE_GET_STATUS_REQ      = 0x2D08,
+       STE_GET_STATUS_RSP      = 0x2E08,
+       STE_STATUS_IND          = 0x2F02,
+       STE_SIM_READY_IND       = 0x2F03,
+};
+
+enum ste_msg {
+       STE_SEND_APDU_MSG,
+       STE_GET_ATR_MSG,
+       STE_POWER_OFF_MSG,
+       STE_POWER_ON_MSG,
+       STE_RESET_MSG,
+       STE_GET_STATUS_MSG,
+       STE_MSG_MAX,
+};
+
+enum ste_status {
+       STE_STATUS_OK           = 0x00000000,
+       STE_STATUS_FAILURE      = 0x00000001,
+       STE_STATUS_BUSY_CALL    = 0x00000002,
+};
+
+enum ste_card_status {
+       STE_CARD_STATUS_UNKNOWN         = 0x00,
+       STE_CARD_STATUS_ACTIVE          = 0x01,
+       STE_CARD_STATUS_NOT_ACTIVE      = 0x02,
+       STE_CARD_STATUS_MISSING         = 0x03,
+       STE_CARD_STATUS_INVALID         = 0x04,
+       STE_CARD_STATUS_DISCONNECTED    = 0x05,
+};
+
+/* Card reader status bits as described in GSM 11.14, Section 12.33
+ * Bits 0-2 are for card reader identity and always zeros. */
+#define ICC_READER_REMOVABLE   (1 << 3)
+#define ICC_READER_PRESENT     (1 << 4)
+#define ICC_READER_ID1         (1 << 5)
+#define ICC_READER_CARD_PRESENT        (1 << 6)
+#define ICC_READER_CARD_POWERED        (1 << 7)
+
+enum ste_state {
+       STE_DISABLED,           /* Reader not present or removed */
+       STE_POWERED_OFF,        /* Card in the reader but powered off */
+       STE_NO_CARD,            /* No card in the reader */
+       STE_ENABLED,            /* Card in the reader and powered on */
+       STE_SIM_BUSY,           /* Modem is busy with ongoing call.*/
+       STE_STATE_MAX
+};
+
+struct ste_u8500 {
+       GIOChannel *io;
+       enum ste_state state;
+       void *sap_data;
+};
+
+typedef int(*recv_state_change_cb)(void *sap, uint8_t result);
+typedef int(*recv_pdu_cb)(void *sap, uint8_t result, uint8_t *data,
+                                                               uint16_t len);
+
+static struct ste_u8500 u8500;
+
+static const uint8_t sim2sap_result[STE_MSG_MAX][STE_STATE_MAX] = {
+       /* SAP results for SEND APDU message */
+       {
+               SAP_RESULT_ERROR_NOT_ACCESSIBLE,        /* STE_DISABLED */
+               SAP_RESULT_ERROR_POWERED_OFF,           /* STE_POWERED_OFF */
+               SAP_RESULT_ERROR_CARD_REMOVED,          /* STE_NO_CARD */
+               SAP_RESULT_ERROR_NO_REASON              /* STE_ENABLED */
+       },
+
+       /* SAP results for GET_ATR message */
+       {
+               SAP_RESULT_ERROR_NO_REASON,
+               SAP_RESULT_ERROR_POWERED_OFF,
+               SAP_RESULT_ERROR_CARD_REMOVED,
+               SAP_RESULT_ERROR_NO_REASON
+       },
+
+       /* SAP results POWER OFF message */
+       {
+               SAP_RESULT_ERROR_NO_REASON,
+               SAP_RESULT_ERROR_POWERED_OFF,
+               SAP_RESULT_ERROR_CARD_REMOVED,
+               SAP_RESULT_ERROR_NO_REASON
+       },
+
+       /* SAP results POWER ON message */
+       {
+               SAP_RESULT_ERROR_NO_REASON,
+               SAP_RESULT_ERROR_NOT_ACCESSIBLE,
+               SAP_RESULT_ERROR_CARD_REMOVED,
+               SAP_RESULT_ERROR_POWERED_ON
+       },
+
+       /* SAP results SIM RESET message */
+       {
+               SAP_RESULT_ERROR_NO_REASON,
+               SAP_RESULT_ERROR_POWERED_OFF,
+               SAP_RESULT_ERROR_CARD_REMOVED,
+               SAP_RESULT_ERROR_NOT_ACCESSIBLE
+       },
+
+       /* SAP results GET STATUS message */
+       {
+               SAP_RESULT_ERROR_NO_REASON,
+               SAP_RESULT_ERROR_NO_REASON,
+               SAP_RESULT_ERROR_NO_REASON,
+               SAP_RESULT_ERROR_NO_REASON
+       }
+};
+
+static uint8_t get_sap_result(enum ste_msg msg, uint32_t status)
+{
+       if (!u8500.io)
+               return SAP_RESULT_ERROR_NO_REASON;
+
+       switch (status) {
+       case STE_STATUS_OK:
+               return SAP_RESULT_OK;
+
+       case STE_STATUS_FAILURE:
+               return sim2sap_result[msg][u8500.state];
+
+       default:
+               DBG("Can't convert a result (status %u)", status);
+               return SAP_RESULT_ERROR_NO_REASON;
+       }
+}
+
+static int get_sap_reader_status(uint32_t card_status, uint8_t *icc_status)
+{
+       /* Card reader is present, not removable and not ID-1 size. */
+       *icc_status = ICC_READER_PRESENT;
+
+       switch (card_status) {
+       case STE_CARD_STATUS_ACTIVE:
+               *icc_status |= ICC_READER_CARD_POWERED;
+
+       case STE_CARD_STATUS_NOT_ACTIVE:
+       case STE_CARD_STATUS_INVALID:
+               *icc_status |= ICC_READER_CARD_PRESENT;
+
+       case STE_CARD_STATUS_MISSING:
+       case STE_CARD_STATUS_DISCONNECTED:
+               return 0;
+
+       default:
+               DBG("Can't convert reader status %u", card_status);
+
+       case STE_CARD_STATUS_UNKNOWN:
+               return -1;
+       }
+}
+
+static uint8_t get_sap_status_change(uint32_t card_status)
+{
+       if (!u8500.io)
+               return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
+
+       switch (card_status) {
+       case STE_CARD_STATUS_UNKNOWN:
+               return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
+
+       case STE_CARD_STATUS_ACTIVE:
+               u8500.state = STE_ENABLED;
+               return SAP_STATUS_CHANGE_CARD_RESET;
+
+       case STE_CARD_STATUS_NOT_ACTIVE:
+               u8500.state = STE_DISABLED;
+               return SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE;
+
+       case STE_CARD_STATUS_MISSING:
+               u8500.state = STE_DISABLED;
+               return SAP_STATUS_CHANGE_CARD_REMOVED;
+
+       case STE_CARD_STATUS_INVALID:
+               u8500.state = STE_DISABLED;
+               return SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE;
+
+       default:
+               DBG("Can't convert status change %u", card_status);
+               return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
+       }
+}
+
+static int send_message(GIOChannel *io, void *buf, size_t size)
+{
+       gsize written;
+
+       DBG_VERBOSE("io %p, size %zu", io, size);
+
+       if (g_io_channel_write_chars(io, buf, size, &written, NULL) !=
+                                                       G_IO_STATUS_NORMAL)
+               return -EIO;
+
+       return written;
+}
+
+static int send_request(GIOChannel *io, uint16_t id,
+                                               struct sap_parameter *param)
+{
+       int ret;
+       struct ste_message *msg;
+       size_t size = sizeof(*msg);
+
+       DBG_VERBOSE("io %p", io);
+
+       if (param)
+               size += param->len;
+
+       msg = g_try_malloc0(size);
+       if (!msg) {
+               sap_error("sending request failed: %s", strerror(ENOMEM));
+               return -ENOMEM;
+       }
+
+       msg->len = size - sizeof(msg->len);
+       msg->id = id;
+       msg->client_tag = STE_CLIENT_TAG;
+
+       if (param)
+               memcpy(msg->payload, param->val, param->len);
+
+       ret = send_message(io, msg, size);
+       if (ret < 0) {
+               sap_error("sending request failed: %s", strerror(-ret));
+       } else if (ret != (int) size) {
+               sap_error("sending request failed: %d out of %zu bytes sent",
+                                                               ret, size);
+               ret = -EIO;
+       }
+
+       g_free(msg);
+
+       return ret;
+}
+
+static void recv_status(uint32_t status)
+{
+       sap_status_ind(u8500.sap_data, get_sap_status_change(status));
+}
+
+static void recv_card_status(uint32_t status, uint8_t *param)
+{
+       uint32_t *card_status;
+       uint8_t result;
+       uint8_t iccrs;
+
+       if (status != STE_STATUS_OK)
+               return;
+
+       card_status = (uint32_t *)param;
+
+       if (get_sap_reader_status(*card_status, &iccrs) < 0)
+               result = SAP_RESULT_ERROR_NO_REASON;
+       else
+               result = get_sap_result(STE_GET_STATUS_MSG, status);
+
+       sap_transfer_card_reader_status_rsp(u8500.sap_data, result, iccrs);
+}
+
+static void recv_state_change(uint32_t ste_msg, uint32_t status,
+                       uint32_t new_state, recv_state_change_cb callback)
+{
+       if (status != STE_STATUS_OK)
+               return;
+
+       u8500.state = new_state;
+
+       if (callback)
+               callback(u8500.sap_data, get_sap_result(ste_msg, status));
+}
+
+static void recv_pdu(uint32_t ste_msg, struct ste_message *msg, uint32_t status,
+                                       uint8_t *param, recv_pdu_cb callback)
+{
+       uint8_t *data = NULL;
+       uint8_t result;
+       int size = 0;
+
+       if (status == STE_STATUS_OK) {
+               data = param;
+               size = STE_MSG_PAYLOAD_SIZE(msg) - sizeof(status);
+       }
+
+       result = get_sap_result(ste_msg, status);
+
+       if (callback)
+               callback(u8500.sap_data, result, data, size);
+}
+
+static void simd_close(void)
+{
+       DBG("io %p", u8500.io);
+
+       if (u8500.io) {
+               g_io_channel_shutdown(u8500.io, TRUE, NULL);
+               g_io_channel_unref(u8500.io);
+       }
+
+       u8500.state = STE_DISABLED;
+       u8500.io = NULL;
+       u8500.sap_data = NULL;
+}
+
+static void recv_sim_ready(void)
+{
+       sap_info("sim is ready. Try to connect again");
+
+       if (send_request(u8500.io, STE_START_SAP_REQ, NULL) < 0) {
+               sap_connect_rsp(u8500.sap_data, SAP_STATUS_CONNECTION_FAILED,
+                                                               SAP_BUF_SIZE);
+               simd_close();
+       }
+}
+
+static void recv_connect_rsp(uint32_t status)
+{
+       switch (status) {
+       case STE_STATUS_OK:
+               if (u8500.state != STE_SIM_BUSY)
+                       sap_connect_rsp(u8500.sap_data,
+                                       SAP_STATUS_OK, 0);
+               break;
+       case STE_STATUS_BUSY_CALL:
+               if (u8500.state != STE_SIM_BUSY) {
+                       sap_connect_rsp(u8500.sap_data,
+                               SAP_STATUS_OK_ONGOING_CALL,
+                               SAP_BUF_SIZE);
+
+                       u8500.state = STE_SIM_BUSY;
+               }
+               break;
+       default:
+               sap_connect_rsp(u8500.sap_data,
+                               SAP_STATUS_CONNECTION_FAILED, 0);
+               simd_close();
+               break;
+       }
+}
+
+static void recv_response(struct ste_message *msg)
+{
+       uint32_t status;
+       uint8_t *param;
+
+       DBG_VERBOSE("msg_id 0x%x", msg->id);
+
+       if (msg->id == STE_END_SAP_RSP) {
+               sap_disconnect_rsp(u8500.sap_data);
+               simd_close();
+               return;
+       }
+
+       param = msg->payload;
+       status = *(uint32_t *)param;
+       param += sizeof(status);
+
+       DBG_VERBOSE("status 0x%x", status);
+
+       switch (msg->id) {
+       case STE_START_SAP_RSP:
+               recv_connect_rsp(status);
+               break;
+       case STE_SEND_APDU_RSP:
+               recv_pdu(STE_SEND_APDU_MSG, msg, status, param,
+                                                       sap_transfer_apdu_rsp);
+               break;
+
+       case STE_GET_ATR_RSP:
+               recv_pdu(STE_GET_ATR_MSG, msg, status, param,
+                                                       sap_transfer_atr_rsp);
+               break;
+
+       case STE_POWER_OFF_RSP:
+               recv_state_change(STE_POWER_OFF_MSG, status, STE_POWERED_OFF,
+                                                       sap_power_sim_off_rsp);
+               break;
+
+       case STE_POWER_ON_RSP:
+               recv_state_change(STE_POWER_ON_MSG, status, STE_ENABLED,
+                                                       sap_power_sim_on_rsp);
+               break;
+
+       case STE_RESET_RSP:
+               recv_state_change(STE_RESET_MSG, status, STE_ENABLED,
+                                                       sap_reset_sim_rsp);
+               break;
+
+       case STE_GET_STATUS_RSP:
+               recv_card_status(status, param);
+               break;
+
+       case STE_STATUS_IND:
+               recv_status(status);
+               break;
+
+       case STE_SIM_READY_IND:
+               recv_sim_ready();
+               break;
+
+       default:
+               sap_error("unsupported message received (id 0x%x)", msg->id);
+       }
+}
+
+static int recv_message(void *buf, size_t size)
+{
+       uint8_t *iter = buf;
+       struct ste_message *msg = buf;
+
+       do {
+               DBG_VERBOSE("size %zu msg->len %u.", size, msg->len);
+
+               if (size < sizeof(*msg)) {
+                       sap_error("invalid message received (%zu bytes)", size);
+                       return -EBADMSG;
+               }
+
+               /* Message must be complete. */
+               if (size < (msg->len + sizeof(msg->len))) {
+                       sap_error("incomplete message received (%zu bytes)",
+                                                                       size);
+                       return -EBADMSG;
+               }
+
+               recv_response(msg);
+
+               /* Reduce total buffer size by just handled frame size. */
+               size -= msg->len + sizeof(msg->len);
+
+               /* Move msg pointer to the next message if any. */
+               iter += msg->len + sizeof(msg->len);
+               msg = (struct ste_message *)iter;
+       } while (size > 0);
+
+       return 0;
+}
+
+static gboolean simd_data_cb(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+       char buf[SAP_BUF_SIZE];
+       gsize bytes_read;
+       GIOStatus gstatus;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+               DBG("Error condition on sim socket (0x%x)", cond);
+               return FALSE;
+       }
+
+       gstatus = g_io_channel_read_chars(io, buf, sizeof(buf), &bytes_read,
+                                                                       NULL);
+       if (gstatus != G_IO_STATUS_NORMAL) {
+               sap_error("error while reading from channel (%d)", gstatus);
+               return TRUE;
+       }
+
+       if (recv_message(buf, bytes_read) < 0)
+               sap_error("error while parsing STE Sim message");
+
+       return TRUE;
+}
+
+static void simd_watch(int sock, void *sap_data)
+{
+       GIOChannel *io;
+
+       DBG("sock %d, sap_data %p ", sock, sap_data);
+
+       io = g_io_channel_unix_new(sock);
+
+       g_io_channel_set_close_on_unref(io, TRUE);
+       g_io_channel_set_encoding(io, NULL, NULL);
+       g_io_channel_set_buffered(io, FALSE);
+
+       u8500.io = io;
+       u8500.sap_data = sap_data;
+       u8500.state = STE_DISABLED;
+
+       g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                       simd_data_cb, NULL, NULL);
+}
+
+static int simd_connect(void *sap_data)
+{
+       struct sockaddr_un addr;
+       int sock;
+       int err;
+
+       /* Already connected to simd */
+       if (u8500.io)
+               return -EALREADY;
+
+       sock = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (sock < 0) {
+               err = -errno;
+               sap_error("creating socket failed: %s", strerror(-err));
+               return err;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       memcpy(addr.sun_path, STE_SIMD_SOCK, sizeof(STE_SIMD_SOCK) - 1);
+
+       if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               err = -errno;
+               sap_error("connect to the socket failed: %s", strerror(-err));
+               goto failed;
+       }
+
+       if (fcntl(sock, F_SETFL, O_NONBLOCK) > 0) {
+               err = -errno;
+               sap_error("setting up socket failed: %s", strerror(-err));
+               goto failed;
+       }
+
+       simd_watch(sock, sap_data);
+
+       return 0;
+
+failed:
+       close(sock);
+       return err;
+}
+
+void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
+{
+       DBG("sap_device %p maxmsgsize %u", sap_device, maxmsgsize);
+
+       sap_info("connect request");
+
+       if (simd_connect(sap_device) < 0) {
+               sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED, 0);
+               return;
+       }
+
+       if (send_request(u8500.io, STE_START_SAP_REQ, NULL) < 0) {
+               sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED,
+                                                               SAP_BUF_SIZE);
+               simd_close();
+       }
+}
+
+void sap_disconnect_req(void *sap_device, uint8_t linkloss)
+{
+       DBG("sap_device %p linkloss %u", sap_device, linkloss);
+
+       sap_info("disconnect request %s", linkloss ? "by link loss" : "");
+
+       if (u8500.state == STE_DISABLED) {
+               sap_disconnect_rsp(sap_device);
+               simd_close();
+               return;
+       }
+
+       if (linkloss) {
+               simd_close();
+               return;
+       }
+
+       if (send_request(u8500.io, STE_END_SAP_REQ, NULL) < 0) {
+               sap_disconnect_rsp(sap_device);
+               return;
+       }
+}
+
+void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
+{
+       uint8_t result;
+
+       DBG_VERBOSE("sap_device %p param %p", sap_device, param);
+
+       if (u8500.state != STE_ENABLED) {
+               result = get_sap_result(STE_SEND_APDU_MSG, STE_STATUS_FAILURE);
+               sap_transfer_apdu_rsp(sap_device, result, NULL, 0);
+               return;
+       }
+
+       if (send_request(u8500.io, STE_SEND_APDU_REQ, param) < 0)
+               sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_NO_DATA,
+                                                               NULL, 0);
+}
+
+void sap_transfer_atr_req(void *sap_device)
+{
+       uint8_t result;
+
+       DBG("sap_device %p", sap_device);
+
+       if (u8500.state != STE_ENABLED) {
+               result = get_sap_result(STE_GET_ATR_MSG, STE_STATUS_FAILURE);
+               sap_transfer_atr_rsp(sap_device, result, NULL, 0);
+               return;
+       }
+
+       if (send_request(u8500.io, STE_GET_ATR_REQ, NULL) < 0)
+               sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_DATA, NULL,
+                                                                       0);
+}
+
+void sap_power_sim_off_req(void *sap_device)
+{
+       uint8_t result;
+
+       DBG("sap_device %p", sap_device);
+
+       if (u8500.state != STE_ENABLED) {
+               result = get_sap_result(STE_POWER_OFF_MSG, STE_STATUS_FAILURE);
+               sap_power_sim_off_rsp(sap_device, result);
+               return;
+       }
+
+       if (send_request(u8500.io, STE_POWER_OFF_REQ, NULL) < 0)
+               sap_power_sim_off_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+}
+
+void sap_power_sim_on_req(void *sap_device)
+{
+       uint8_t result;
+
+       DBG("sap_device %p", sap_device);
+
+       if (u8500.state != STE_POWERED_OFF) {
+               result = get_sap_result(STE_POWER_ON_MSG, STE_STATUS_FAILURE);
+               sap_power_sim_on_rsp(sap_device, result);
+               return;
+       }
+
+       if (send_request(u8500.io, STE_POWER_ON_REQ, NULL) < 0)
+               sap_power_sim_on_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+}
+
+void sap_reset_sim_req(void *sap_device)
+{
+       uint8_t result;
+
+       DBG("sap_device %p", sap_device);
+
+       if (u8500.state != STE_ENABLED) {
+               result = get_sap_result(STE_RESET_MSG, STE_STATUS_FAILURE);
+               sap_reset_sim_rsp(sap_device, result);
+               return;
+       }
+
+       if (send_request(u8500.io, STE_RESET_REQ, NULL) < 0)
+               sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+}
+
+void sap_transfer_card_reader_status_req(void *sap_device)
+{
+       uint8_t result;
+
+       DBG("sap_device %p", sap_device);
+
+       if (u8500.state == STE_DISABLED) {
+               result = get_sap_result(STE_GET_STATUS_MSG, STE_STATUS_FAILURE);
+               sap_transfer_card_reader_status_rsp(sap_device, result, 0);
+               return;
+       }
+
+       if (send_request(u8500.io, STE_GET_STATUS_REQ, NULL) < 0)
+               sap_transfer_card_reader_status_rsp(sap_device,
+                                               SAP_RESULT_ERROR_NO_DATA, 0);
+}
+
+void sap_set_transport_protocol_req(void *sap_device,
+                                               struct sap_parameter *param)
+{
+       DBG("sap_device %p", sap_device);
+
+       sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
+}
+
+int sap_init(void)
+{
+       u8500.state = STE_DISABLED;
+       info("STE U8500 SAP driver initialized");
+       return 0;
+}
+
+void sap_exit(void)
+{
+}
diff --git a/sap/sap.h b/sap/sap.h
new file mode 100644 (file)
index 0000000..1dbb235
--- /dev/null
+++ b/sap/sap.h
@@ -0,0 +1,176 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *
+ *  Author: Marek Skowron <marek.skowron@tieto.com> for ST-Ericsson.
+ *  Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
+ *          for ST-Ericsson.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdint.h>
+#include <glib.h>
+
+#define SAP_VERSION 0x0101
+
+/* Connection Status - SAP v1.1 section 5.2.2 */
+enum sap_status {
+       SAP_STATUS_OK                           = 0x00,
+       SAP_STATUS_CONNECTION_FAILED            = 0x01,
+       SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED   = 0x02,
+       SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL       = 0x03,
+       SAP_STATUS_OK_ONGOING_CALL              = 0x04
+};
+
+/* Disconnection Type - SAP v1.1 section 5.2.3 */
+enum sap_disconnection_type {
+       SAP_DISCONNECTION_TYPE_GRACEFUL         = 0x00,
+       SAP_DISCONNECTION_TYPE_IMMEDIATE        = 0x01,
+       SAP_DISCONNECTION_TYPE_CLIENT           = 0xFF
+};
+
+/* Result codes - SAP v1.1 section 5.2.4 */
+enum sap_result {
+       SAP_RESULT_OK                   = 0x00,
+       SAP_RESULT_ERROR_NO_REASON      = 0x01,
+       SAP_RESULT_ERROR_NOT_ACCESSIBLE = 0x02,
+       SAP_RESULT_ERROR_POWERED_OFF    = 0x03,
+       SAP_RESULT_ERROR_CARD_REMOVED   = 0x04,
+       SAP_RESULT_ERROR_POWERED_ON     = 0x05,
+       SAP_RESULT_ERROR_NO_DATA        = 0x06,
+       SAP_RESULT_NOT_SUPPORTED        = 0x07
+};
+
+/* Status Change - SAP v1.1 section 5.2.8 */
+enum sap_status_change {
+       SAP_STATUS_CHANGE_UNKNOWN_ERROR         = 0x00,
+       SAP_STATUS_CHANGE_CARD_RESET            = 0x01,
+       SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE   = 0x02,
+       SAP_STATUS_CHANGE_CARD_REMOVED          = 0x03,
+       SAP_STATUS_CHANGE_CARD_INSERTED         = 0x04,
+       SAP_STATUS_CHANGE_CARD_RECOVERED        = 0x05
+};
+
+/* Message format - SAP v1.1 section 5.1 */
+struct sap_parameter {
+       uint8_t id;
+       uint8_t reserved;
+       uint16_t len;
+       uint8_t val[0];
+       /*
+        * Padding bytes 0-3 bytes
+        */
+} __attribute__((packed));
+
+struct sap_message {
+       uint8_t id;
+       uint8_t nparam;
+       uint16_t reserved;
+       struct sap_parameter param[0];
+} __attribute__((packed));
+
+#define SAP_BUF_SIZE           512
+#define SAP_MSG_HEADER_SIZE    4
+
+enum sap_protocol {
+       SAP_CONNECT_REQ         = 0x00,
+       SAP_CONNECT_RESP        = 0x01,
+       SAP_DISCONNECT_REQ      = 0x02,
+       SAP_DISCONNECT_RESP     = 0x03,
+       SAP_DISCONNECT_IND      = 0x04,
+       SAP_TRANSFER_APDU_REQ   = 0x05,
+       SAP_TRANSFER_APDU_RESP  = 0x06,
+       SAP_TRANSFER_ATR_REQ    = 0x07,
+       SAP_TRANSFER_ATR_RESP   = 0x08,
+       SAP_POWER_SIM_OFF_REQ   = 0x09,
+       SAP_POWER_SIM_OFF_RESP  = 0x0A,
+       SAP_POWER_SIM_ON_REQ    = 0x0B,
+       SAP_POWER_SIM_ON_RESP   = 0x0C,
+       SAP_RESET_SIM_REQ       = 0x0D,
+       SAP_RESET_SIM_RESP      = 0x0E,
+       SAP_TRANSFER_CARD_READER_STATUS_REQ     = 0x0F,
+       SAP_TRANSFER_CARD_READER_STATUS_RESP    = 0x10,
+       SAP_STATUS_IND  = 0x11,
+       SAP_ERROR_RESP  = 0x12,
+       SAP_SET_TRANSPORT_PROTOCOL_REQ  = 0x13,
+       SAP_SET_TRANSPORT_PROTOCOL_RESP = 0x14
+};
+
+/* Parameters Ids - SAP 1.1 section 5.2 */
+enum sap_param_id {
+       SAP_PARAM_ID_MAX_MSG_SIZE       = 0x00,
+       SAP_PARAM_ID_CONN_STATUS        = 0x01,
+       SAP_PARAM_ID_RESULT_CODE        = 0x02,
+       SAP_PARAM_ID_DISCONNECT_IND     = 0x03,
+       SAP_PARAM_ID_COMMAND_APDU       = 0x04,
+       SAP_PARAM_ID_COMMAND_APDU7816   = 0x10,
+       SAP_PARAM_ID_RESPONSE_APDU      = 0x05,
+       SAP_PARAM_ID_ATR                = 0x06,
+       SAP_PARAM_ID_CARD_READER_STATUS = 0x07,
+       SAP_PARAM_ID_STATUS_CHANGE      = 0x08,
+       SAP_PARAM_ID_TRANSPORT_PROTOCOL = 0x09
+};
+
+#define SAP_PARAM_ID_MAX_MSG_SIZE_LEN          0x02
+#define SAP_PARAM_ID_CONN_STATUS_LEN           0x01
+#define SAP_PARAM_ID_RESULT_CODE_LEN           0x01
+#define SAP_PARAM_ID_DISCONNECT_IND_LEN                0x01
+#define SAP_PARAM_ID_CARD_READER_STATUS_LEN    0x01
+#define SAP_PARAM_ID_STATUS_CHANGE_LEN         0x01
+#define SAP_PARAM_ID_TRANSPORT_PROTO_LEN       0x01
+
+/* Transport Protocol - SAP v1.1 section 5.2.9 */
+enum sap_transport_protocol {
+       SAP_TRANSPORT_PROTOCOL_T0 = 0x00,
+       SAP_TRANSPORT_PROTOCOL_T1 = 0x01
+};
+
+/*SAP driver init and exit routines. Implemented by sap-*.c */
+int sap_init(void);
+void sap_exit(void);
+
+/* SAP requests implemented by sap-*.c */
+void sap_connect_req(void *sap_device, uint16_t maxmsgsize);
+void sap_disconnect_req(void *sap_device, uint8_t linkloss);
+void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param);
+void sap_transfer_atr_req(void *sap_device);
+void sap_power_sim_off_req(void *sap_device);
+void sap_power_sim_on_req(void *sap_device);
+void sap_reset_sim_req(void *sap_device);
+void sap_transfer_card_reader_status_req(void *sap_device);
+void sap_set_transport_protocol_req(void *sap_device,
+                                       struct sap_parameter *param);
+
+/*SAP responses to SAP requests. Implemented by server.c */
+int sap_connect_rsp(void *sap_device, uint8_t status, uint16_t maxmsgsize);
+int sap_disconnect_rsp(void *sap_device);
+int sap_transfer_apdu_rsp(void *sap_device, uint8_t result,
+                               uint8_t *sap_apdu_resp, uint16_t length);
+int sap_transfer_atr_rsp(void *sap_device, uint8_t result,
+                               uint8_t *sap_atr, uint16_t length);
+int sap_power_sim_off_rsp(void *sap_device, uint8_t result);
+int sap_power_sim_on_rsp(void *sap_device, uint8_t result);
+int sap_reset_sim_rsp(void *sap_device, uint8_t result);
+int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
+                                               uint8_t status);
+int sap_error_rsp(void *sap_device);
+int sap_transport_protocol_rsp(void *sap_device, uint8_t result);
+
+/* Event indication. Implemented by server.c*/
+int sap_status_ind(void *sap_device, uint8_t status_change);
+int sap_disconnect_ind(void *sap_device, uint8_t disc_type);
diff --git a/sap/server.c b/sap/server.c
new file mode 100644 (file)
index 0000000..f5d19c4
--- /dev/null
@@ -0,0 +1,1426 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 Instituto Nokia de Tecnologia - INdT
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *  Copyright (C) 2011 Tieto Poland
+ *
+ *  Author: Marek Skowron <marek.skowron@tieto.com> for ST-Ericsson.
+ *  Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
+ *          for ST-Ericsson.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <glib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "btio.h"
+#include "sdpd.h"
+#include "log.h"
+#include "error.h"
+#include "dbus-common.h"
+#include "sap.h"
+#include "server.h"
+
+#define SAP_SERVER_INTERFACE   "org.bluez.SimAccess"
+#define SAP_SERVER_CHANNEL     8
+
+#define PADDING4(x) ((4 - ((x) & 0x03)) & 0x03)
+#define PARAMETER_SIZE(x) (sizeof(struct sap_parameter) + x + PADDING4(x))
+
+#define SAP_NO_REQ 0xFF
+
+#define SAP_TIMER_GRACEFUL_DISCONNECT 30
+#define SAP_TIMER_NO_ACTIVITY 30
+
+enum {
+       SAP_STATE_DISCONNECTED,
+       SAP_STATE_CONNECT_IN_PROGRESS,
+       SAP_STATE_CONNECT_MODEM_BUSY,
+       SAP_STATE_CONNECTED,
+       SAP_STATE_GRACEFUL_DISCONNECT,
+       SAP_STATE_IMMEDIATE_DISCONNECT,
+       SAP_STATE_CLIENT_DISCONNECT
+};
+
+struct sap_connection {
+       GIOChannel *io;
+       uint32_t state;
+       uint8_t processing_req;
+       guint timer_id;
+};
+
+struct sap_server {
+       char *path;
+       uint32_t record_id;
+       GIOChannel *listen_io;
+       struct sap_connection *conn;
+};
+
+static DBusConnection *connection;
+static struct sap_server *server;
+
+static void start_guard_timer(struct sap_connection *conn, guint interval);
+static void stop_guard_timer(struct sap_connection *conn);
+static gboolean guard_timeout(gpointer data);
+
+static size_t add_result_parameter(uint8_t result,
+                                       struct sap_parameter *param)
+{
+       param->id = SAP_PARAM_ID_RESULT_CODE;
+       param->len = htons(SAP_PARAM_ID_RESULT_CODE_LEN);
+       *param->val = result;
+
+       return PARAMETER_SIZE(SAP_PARAM_ID_RESULT_CODE_LEN);
+}
+
+static int is_power_sim_off_req_allowed(uint8_t processing_req)
+{
+       switch (processing_req) {
+       case SAP_NO_REQ:
+       case SAP_TRANSFER_APDU_REQ:
+       case SAP_TRANSFER_ATR_REQ:
+       case SAP_POWER_SIM_ON_REQ:
+       case SAP_RESET_SIM_REQ:
+       case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static int is_reset_sim_req_allowed(uint8_t processing_req)
+{
+       switch (processing_req) {
+       case SAP_NO_REQ:
+       case SAP_TRANSFER_APDU_REQ:
+       case SAP_TRANSFER_ATR_REQ:
+       case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static int check_msg(struct sap_message *msg)
+{
+       switch (msg->id) {
+       case SAP_CONNECT_REQ:
+               if (msg->nparam != 0x01)
+                       return -EBADMSG;
+
+               if (msg->param->id != SAP_PARAM_ID_MAX_MSG_SIZE)
+                       return -EBADMSG;
+
+               if (ntohs(msg->param->len) != SAP_PARAM_ID_MAX_MSG_SIZE_LEN)
+                       return -EBADMSG;
+
+               break;
+
+       case SAP_TRANSFER_APDU_REQ:
+               if (msg->nparam != 0x01)
+                       return -EBADMSG;
+
+               if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU)
+                       if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU7816)
+                               return -EBADMSG;
+
+               if (msg->param->len == 0x00)
+                       return -EBADMSG;
+
+               break;
+
+       case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+               if (msg->nparam != 0x01)
+                       return -EBADMSG;
+
+               if (msg->param->id != SAP_PARAM_ID_TRANSPORT_PROTOCOL)
+                       return -EBADMSG;
+
+               if (ntohs(msg->param->len) != SAP_PARAM_ID_TRANSPORT_PROTO_LEN)
+                       return -EBADMSG;
+
+               if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T0)
+                       if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T1)
+                               return -EBADMSG;
+
+               break;
+
+       case SAP_DISCONNECT_REQ:
+       case SAP_TRANSFER_ATR_REQ:
+       case SAP_POWER_SIM_OFF_REQ:
+       case SAP_POWER_SIM_ON_REQ:
+       case SAP_RESET_SIM_REQ:
+       case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+               if (msg->nparam != 0x00)
+                       return -EBADMSG;
+
+               break;
+       }
+
+       return 0;
+}
+
+static sdp_record_t *create_sap_record(uint8_t channel)
+{
+       sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
+       uuid_t sap_uuid, gt_uuid, root_uuid, l2cap, rfcomm;
+       sdp_profile_desc_t profile;
+       sdp_record_t *record;
+       sdp_data_t *ch;
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(record, root);
+       sdp_list_free(root, NULL);
+
+       sdp_uuid16_create(&sap_uuid, SAP_SVCLASS_ID);
+       svclass_id = sdp_list_append(NULL, &sap_uuid);
+       sdp_uuid16_create(&gt_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &gt_uuid);
+
+       sdp_set_service_classes(record, svclass_id);
+       sdp_list_free(svclass_id, NULL);
+
+       sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
+       profile.version = SAP_VERSION;
+       profiles = sdp_list_append(NULL, &profile);
+       sdp_set_profile_descs(record, profiles);
+       sdp_list_free(profiles, NULL);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(NULL, &l2cap);
+       apseq = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+       proto[1] = sdp_list_append(NULL, &rfcomm);
+       ch = sdp_data_alloc(SDP_UINT8, &channel);
+       proto[1] = sdp_list_append(proto[1], ch);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_set_info_attr(record, "SIM Access Server", NULL, NULL);
+
+       sdp_data_free(ch);
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(aproto, NULL);
+
+       return record;
+}
+
+static int send_message(struct sap_connection *conn, void *buf, size_t size)
+{
+       size_t written = 0;
+       GError *gerr = NULL;
+       GIOStatus gstatus;
+
+       if (!conn || !buf)
+               return -EINVAL;
+
+       DBG("conn %p, size %zu", conn, size);
+
+       gstatus = g_io_channel_write_chars(conn->io, buf, size, &written,
+                                                                       &gerr);
+       if (gstatus != G_IO_STATUS_NORMAL) {
+               if (gerr)
+                       g_error_free(gerr);
+
+               error("write error (0x%02x).", gstatus);
+               return -EIO;
+       }
+
+       if (written != size) {
+               error("written %zu bytes out of %zu", written, size);
+               return -EIO;
+       }
+
+       return written;
+}
+
+static int disconnect_ind(void *sap_device, uint8_t disc_type)
+{
+       struct sap_connection *conn = sap_device;
+       char buf[SAP_BUF_SIZE];
+       struct sap_message *msg = (struct sap_message *) buf;
+       struct sap_parameter *param = (struct sap_parameter *) msg->param;
+       size_t size = sizeof(struct sap_message);
+
+       DBG("data %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
+
+       memset(buf, 0, sizeof(buf));
+       msg->id = SAP_DISCONNECT_IND;
+       msg->nparam = 0x01;
+
+       /* Add disconnection type param. */
+       param->id  = SAP_PARAM_ID_DISCONNECT_IND;
+       param->len = htons(SAP_PARAM_ID_DISCONNECT_IND_LEN);
+       *param->val = disc_type;
+       size += PARAMETER_SIZE(SAP_PARAM_ID_DISCONNECT_IND_LEN);
+
+       return send_message(sap_device, buf, size);
+}
+
+static void connect_req(struct sap_connection *conn,
+                               struct sap_parameter *param)
+{
+       uint16_t maxmsgsize, *val;
+
+       DBG("conn %p state %d", conn, conn->state);
+
+       if (!param)
+               goto error_rsp;
+
+       if (conn->state != SAP_STATE_DISCONNECTED)
+               goto error_rsp;
+
+       stop_guard_timer(conn);
+
+       val = (uint16_t *) &param->val;
+       maxmsgsize = ntohs(*val);
+
+       DBG("Connect MaxMsgSize: 0x%04x", maxmsgsize);
+
+       conn->state = SAP_STATE_CONNECT_IN_PROGRESS;
+
+       if (maxmsgsize <= SAP_BUF_SIZE) {
+               conn->processing_req = SAP_CONNECT_REQ;
+               sap_connect_req(conn, maxmsgsize);
+       } else {
+               sap_connect_rsp(conn, SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED,
+                                                               SAP_BUF_SIZE);
+       }
+
+       return;
+
+error_rsp:
+       sap_error_rsp(conn);
+}
+
+static int disconnect_req(struct sap_connection *conn, uint8_t disc_type)
+{
+       DBG("conn %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
+
+       switch (disc_type) {
+       case SAP_DISCONNECTION_TYPE_GRACEFUL:
+               if (conn->state == SAP_STATE_DISCONNECTED ||
+                               conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+                               conn->state == SAP_STATE_CONNECT_MODEM_BUSY)
+                       return -EPERM;
+
+               if (conn->state == SAP_STATE_CONNECTED) {
+                       conn->state = SAP_STATE_GRACEFUL_DISCONNECT;
+                       conn->processing_req = SAP_NO_REQ;
+
+                       disconnect_ind(conn, disc_type);
+                       /* Timer will disconnect if client won't do.*/
+                       start_guard_timer(conn, SAP_TIMER_GRACEFUL_DISCONNECT);
+               }
+
+               return 0;
+
+       case SAP_DISCONNECTION_TYPE_IMMEDIATE:
+               if (conn->state == SAP_STATE_DISCONNECTED ||
+                               conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+                               conn->state == SAP_STATE_CONNECT_MODEM_BUSY)
+                       return -EPERM;
+
+               if (conn->state == SAP_STATE_CONNECTED ||
+                               conn->state == SAP_STATE_GRACEFUL_DISCONNECT) {
+                       conn->state = SAP_STATE_IMMEDIATE_DISCONNECT;
+                       conn->processing_req = SAP_NO_REQ;
+
+                       stop_guard_timer(conn);
+                       disconnect_ind(conn, disc_type);
+                       sap_disconnect_req(conn, 0);
+               }
+
+               return 0;
+
+       case SAP_DISCONNECTION_TYPE_CLIENT:
+               if (conn->state != SAP_STATE_CONNECTED &&
+                               conn->state != SAP_STATE_GRACEFUL_DISCONNECT) {
+                       sap_error_rsp(conn);
+                       return -EPERM;
+               }
+
+               conn->state = SAP_STATE_CLIENT_DISCONNECT;
+               conn->processing_req = SAP_NO_REQ;
+
+               stop_guard_timer(conn);
+               sap_disconnect_req(conn, 0);
+
+               return 0;
+
+       default:
+               error("Unknown disconnection type (0x%02x).", disc_type);
+               return -EINVAL;
+       }
+}
+
+static void transfer_apdu_req(struct sap_connection *conn,
+                                       struct sap_parameter *param)
+{
+       DBG("conn %p state %d", conn, conn->state);
+
+       if (!param)
+               goto error_rsp;
+
+       param->len = ntohs(param->len);
+
+       if (conn->state != SAP_STATE_CONNECTED &&
+                       conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+               goto error_rsp;
+
+       if (conn->processing_req != SAP_NO_REQ)
+               goto error_rsp;
+
+       conn->processing_req = SAP_TRANSFER_APDU_REQ;
+       sap_transfer_apdu_req(conn, param);
+
+       return;
+
+error_rsp:
+       sap_error_rsp(conn);
+}
+
+static void transfer_atr_req(struct sap_connection *conn)
+{
+       DBG("conn %p state %d", conn, conn->state);
+
+       if (conn->state != SAP_STATE_CONNECTED)
+               goto error_rsp;
+
+       if (conn->processing_req != SAP_NO_REQ)
+               goto error_rsp;
+
+       conn->processing_req = SAP_TRANSFER_ATR_REQ;
+       sap_transfer_atr_req(conn);
+
+       return;
+
+error_rsp:
+       sap_error_rsp(conn);
+}
+
+static void power_sim_off_req(struct sap_connection *conn)
+{
+       DBG("conn %p state %d", conn, conn->state);
+
+       if (conn->state != SAP_STATE_CONNECTED)
+               goto error_rsp;
+
+       if (!is_power_sim_off_req_allowed(conn->processing_req))
+               goto error_rsp;
+
+       conn->processing_req = SAP_POWER_SIM_OFF_REQ;
+       sap_power_sim_off_req(conn);
+
+       return;
+
+error_rsp:
+       sap_error_rsp(conn);
+}
+
+static void power_sim_on_req(struct sap_connection *conn)
+{
+       DBG("conn %p state %d", conn, conn->state);
+
+       if (conn->state != SAP_STATE_CONNECTED)
+               goto error_rsp;
+
+       if (conn->processing_req != SAP_NO_REQ)
+               goto error_rsp;
+
+       conn->processing_req = SAP_POWER_SIM_ON_REQ;
+       sap_power_sim_on_req(conn);
+
+       return;
+
+error_rsp:
+       sap_error_rsp(conn);
+}
+
+static void reset_sim_req(struct sap_connection *conn)
+{
+       DBG("conn %p state %d", conn, conn->state);
+
+       if (conn->state != SAP_STATE_CONNECTED)
+               goto error_rsp;
+
+       if (!is_reset_sim_req_allowed(conn->processing_req))
+               goto error_rsp;
+
+       conn->processing_req = SAP_RESET_SIM_REQ;
+       sap_reset_sim_req(conn);
+
+       return;
+
+error_rsp:
+       sap_error_rsp(conn);
+}
+
+static void transfer_card_reader_status_req(struct sap_connection *conn)
+{
+       DBG("conn %p state %d", conn, conn->state);
+
+       if (conn->state != SAP_STATE_CONNECTED)
+               goto error_rsp;
+
+       if (conn->processing_req != SAP_NO_REQ)
+               goto error_rsp;
+
+       conn->processing_req = SAP_TRANSFER_CARD_READER_STATUS_REQ;
+       sap_transfer_card_reader_status_req(conn);
+
+       return;
+
+error_rsp:
+       sap_error_rsp(conn);
+}
+
+static void set_transport_protocol_req(struct sap_connection *conn,
+                                       struct sap_parameter *param)
+{
+       if (!param)
+               goto error_rsp;
+
+       DBG("conn %p state %d param %p", conn, conn->state, param);
+
+       if (conn->state != SAP_STATE_CONNECTED)
+               goto error_rsp;
+
+       if (conn->processing_req != SAP_NO_REQ)
+               goto error_rsp;
+
+       conn->processing_req = SAP_SET_TRANSPORT_PROTOCOL_REQ;
+       sap_set_transport_protocol_req(conn, param);
+
+       return;
+
+error_rsp:
+       sap_error_rsp(conn);
+}
+
+static void start_guard_timer(struct sap_connection *conn, guint interval)
+{
+       if (!conn)
+               return;
+
+       if (!conn->timer_id)
+               conn->timer_id = g_timeout_add_seconds(interval, guard_timeout,
+                                                                       conn);
+       else
+               error("Timer is already active.");
+}
+
+static void stop_guard_timer(struct sap_connection *conn)
+{
+       if (conn  && conn->timer_id) {
+               g_source_remove(conn->timer_id);
+               conn->timer_id = 0;
+       }
+}
+
+static gboolean guard_timeout(gpointer data)
+{
+       struct sap_connection *conn = data;
+
+       if (!conn)
+               return FALSE;
+
+       DBG("conn %p state %d pr 0x%02x", conn, conn->state,
+                                               conn->processing_req);
+
+       conn->timer_id = 0;
+
+       switch (conn->state) {
+       case SAP_STATE_DISCONNECTED:
+               /* Client opened RFCOMM channel but didn't send CONNECT_REQ,
+                * in fixed time or client disconnected SAP connection but
+                * didn't closed RFCOMM channel in fixed time.*/
+               if (conn->io) {
+                       g_io_channel_shutdown(conn->io, TRUE, NULL);
+                       g_io_channel_unref(conn->io);
+               }
+               break;
+
+       case SAP_STATE_GRACEFUL_DISCONNECT:
+               /* Client didn't disconnect SAP connection in fixed time,
+                * so close SAP connection immediately. */
+               disconnect_req(conn, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+               break;
+
+       default:
+               error("Unexpected state (%d).", conn->state);
+               break;
+       }
+
+       return FALSE;
+}
+
+static void sap_set_connected(struct sap_connection *conn)
+{
+       gboolean connected = TRUE;
+
+       emit_property_changed(connection, server->path,
+                                       SAP_SERVER_INTERFACE,
+               "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+       conn->state = SAP_STATE_CONNECTED;
+}
+
+int sap_connect_rsp(void *sap_device, uint8_t status, uint16_t maxmsgsize)
+{
+       struct sap_connection *conn = sap_device;
+       char buf[SAP_BUF_SIZE];
+       struct sap_message *msg = (struct sap_message *) buf;
+       struct sap_parameter *param = (struct sap_parameter *) msg->param;
+       size_t size = sizeof(struct sap_message);
+
+       if (!conn)
+               return -EINVAL;
+
+       DBG("state %d pr 0x%02x status 0x%02x", conn->state,
+                                               conn->processing_req, status);
+
+       if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS)
+               return -EPERM;
+
+       memset(buf, 0, sizeof(buf));
+       msg->id = SAP_CONNECT_RESP;
+       msg->nparam = 0x01;
+
+       /* Add connection status */
+       param->id = SAP_PARAM_ID_CONN_STATUS;
+       param->len = htons(SAP_PARAM_ID_CONN_STATUS_LEN);
+       *param->val = status;
+       size += PARAMETER_SIZE(SAP_PARAM_ID_CONN_STATUS_LEN);
+
+       /* Add MaxMsgSize */
+       if (maxmsgsize) {
+               uint16_t *len;
+
+               msg->nparam++;
+               param = (struct sap_parameter *) &buf[size];
+               param->id = SAP_PARAM_ID_MAX_MSG_SIZE;
+               param->len = htons(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
+               len = (uint16_t *) &param->val;
+               *len = htons(maxmsgsize);
+               size += PARAMETER_SIZE(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
+       }
+
+       if (status == SAP_STATUS_OK) {
+               sap_set_connected(conn);
+       } else if (status == SAP_STATUS_OK_ONGOING_CALL) {
+               DBG("ongoing call. Wait for reset indication!");
+               conn->state = SAP_STATE_CONNECT_MODEM_BUSY;
+       } else {
+               conn->state = SAP_STATE_DISCONNECTED;
+
+               /* Timer will shutdown channel if client doesn't send
+                * CONNECT_REQ or doesn't shutdown channel itself.*/
+               start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+       }
+
+       conn->processing_req = SAP_NO_REQ;
+
+       return send_message(sap_device, buf, size);
+}
+
+int sap_disconnect_rsp(void *sap_device)
+{
+       struct sap_connection *conn = sap_device;
+       struct sap_message msg;
+
+       if (!conn)
+               return -EINVAL;
+
+       DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+       switch (conn->state) {
+       case SAP_STATE_CLIENT_DISCONNECT:
+               memset(&msg, 0, sizeof(msg));
+               msg.id = SAP_DISCONNECT_RESP;
+
+               conn->state = SAP_STATE_DISCONNECTED;
+               conn->processing_req = SAP_NO_REQ;
+
+               /* Timer will close channel if client doesn't do it.*/
+               start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+
+               return send_message(sap_device, &msg, sizeof(msg));
+
+       case SAP_STATE_IMMEDIATE_DISCONNECT:
+               conn->state = SAP_STATE_DISCONNECTED;
+               conn->processing_req = SAP_NO_REQ;
+
+               if (conn->io) {
+                       g_io_channel_shutdown(conn->io, TRUE, NULL);
+                       g_io_channel_unref(conn->io);
+               }
+
+               return 0;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+int sap_transfer_apdu_rsp(void *sap_device, uint8_t result, uint8_t *apdu,
+                                       uint16_t length)
+{
+       struct sap_connection *conn = sap_device;
+       char buf[SAP_BUF_SIZE];
+       struct sap_message *msg = (struct sap_message *) buf;
+       struct sap_parameter *param = (struct sap_parameter *) msg->param;
+       size_t size = sizeof(struct sap_message);
+
+       if (!conn)
+               return -EINVAL;
+
+       DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+       if (conn->processing_req != SAP_TRANSFER_APDU_REQ)
+               return 0;
+
+       if (result == SAP_RESULT_OK && (!apdu || (apdu && length == 0x00)))
+               return -EINVAL;
+
+       memset(buf, 0, sizeof(buf));
+       msg->id = SAP_TRANSFER_APDU_RESP;
+       msg->nparam = 0x01;
+       size += add_result_parameter(result, param);
+
+       /* Add APDU response. */
+       if (result == SAP_RESULT_OK) {
+               msg->nparam++;
+               param = (struct sap_parameter *) &buf[size];
+               param->id = SAP_PARAM_ID_RESPONSE_APDU;
+               param->len = htons(length);
+
+               size += PARAMETER_SIZE(length);
+
+               if (size > SAP_BUF_SIZE)
+                       return -EOVERFLOW;
+
+               memcpy(param->val, apdu, length);
+       }
+
+       conn->processing_req = SAP_NO_REQ;
+
+       return send_message(sap_device, buf, size);
+}
+
+int sap_transfer_atr_rsp(void *sap_device, uint8_t result, uint8_t *atr,
+                                       uint16_t length)
+{
+       struct sap_connection *conn = sap_device;
+       char buf[SAP_BUF_SIZE];
+       struct sap_message *msg = (struct sap_message *) buf;
+       struct sap_parameter *param = (struct sap_parameter *) msg->param;
+       size_t size = sizeof(struct sap_message);
+
+       if (!conn)
+               return -EINVAL;
+
+       DBG("result 0x%02x state %d pr 0x%02x len %d", result, conn->state,
+                       conn->processing_req, length);
+
+       if (conn->processing_req != SAP_TRANSFER_ATR_REQ)
+               return 0;
+
+       if (result == SAP_RESULT_OK && (!atr || (atr && length == 0x00)))
+               return -EINVAL;
+
+       memset(buf, 0, sizeof(buf));
+       msg->id = SAP_TRANSFER_ATR_RESP;
+       msg->nparam = 0x01;
+       size += add_result_parameter(result, param);
+
+       /* Add ATR response */
+       if (result == SAP_RESULT_OK) {
+               msg->nparam++;
+               param = (struct sap_parameter *) &buf[size];
+               param->id = SAP_PARAM_ID_ATR;
+               param->len = htons(length);
+               size += PARAMETER_SIZE(length);
+
+               if (size > SAP_BUF_SIZE)
+                       return -EOVERFLOW;
+
+               memcpy(param->val, atr, length);
+       }
+
+       conn->processing_req = SAP_NO_REQ;
+
+       return send_message(sap_device, buf, size);
+}
+
+int sap_power_sim_off_rsp(void *sap_device, uint8_t result)
+{
+       struct sap_connection *conn = sap_device;
+       char buf[SAP_BUF_SIZE];
+       struct sap_message *msg = (struct sap_message *) buf;
+       size_t size = sizeof(struct sap_message);
+
+       if (!conn)
+               return -EINVAL;
+
+       DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+       if (conn->processing_req != SAP_POWER_SIM_OFF_REQ)
+               return 0;
+
+       memset(buf, 0, sizeof(buf));
+       msg->id = SAP_POWER_SIM_OFF_RESP;
+       msg->nparam = 0x01;
+       size += add_result_parameter(result, msg->param);
+
+       conn->processing_req = SAP_NO_REQ;
+
+       return send_message(sap_device, buf, size);
+}
+
+int sap_power_sim_on_rsp(void *sap_device, uint8_t result)
+{
+       struct sap_connection *conn = sap_device;
+       char buf[SAP_BUF_SIZE];
+       struct sap_message *msg = (struct sap_message *) buf;
+       size_t size = sizeof(struct sap_message);
+
+       if (!conn)
+               return -EINVAL;
+
+       DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+       if (conn->processing_req != SAP_POWER_SIM_ON_REQ)
+               return 0;
+
+       memset(buf, 0, sizeof(buf));
+       msg->id = SAP_POWER_SIM_ON_RESP;
+       msg->nparam = 0x01;
+       size += add_result_parameter(result, msg->param);
+
+       conn->processing_req = SAP_NO_REQ;
+
+       return send_message(sap_device, buf, size);
+}
+
+int sap_reset_sim_rsp(void *sap_device, uint8_t result)
+{
+       struct sap_connection *conn = sap_device;
+       char buf[SAP_BUF_SIZE];
+       struct sap_message *msg = (struct sap_message *) buf;
+       size_t size = sizeof(struct sap_message);
+
+       if (!conn)
+               return -EINVAL;
+
+       DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+                                               conn->processing_req, result);
+
+       if (conn->processing_req != SAP_RESET_SIM_REQ)
+               return 0;
+
+       memset(buf, 0, sizeof(buf));
+       msg->id = SAP_RESET_SIM_RESP;
+       msg->nparam = 0x01;
+       size += add_result_parameter(result, msg->param);
+
+       conn->processing_req = SAP_NO_REQ;
+
+       return send_message(sap_device, buf, size);
+}
+
+int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
+                                               uint8_t status)
+{
+       struct sap_connection *conn = sap_device;
+       char buf[SAP_BUF_SIZE];
+       struct sap_message *msg = (struct sap_message *) buf;
+       struct sap_parameter *param = (struct sap_parameter *) msg->param;
+       size_t size = sizeof(struct sap_message);
+
+       if (!conn)
+               return -EINVAL;
+
+       DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+                                               conn->processing_req, result);
+
+       if (conn->processing_req != SAP_TRANSFER_CARD_READER_STATUS_REQ)
+               return 0;
+
+       memset(buf, 0, sizeof(buf));
+       msg->id = SAP_TRANSFER_CARD_READER_STATUS_RESP;
+       msg->nparam = 0x01;
+       size += add_result_parameter(result, param);
+
+       /* Add card reader status. */
+       if (result == SAP_RESULT_OK) {
+               msg->nparam++;
+               param = (struct sap_parameter *) &buf[size];
+               param->id = SAP_PARAM_ID_CARD_READER_STATUS;
+               param->len = htons(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
+               *param->val = status;
+               size += PARAMETER_SIZE(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
+       }
+
+       conn->processing_req = SAP_NO_REQ;
+
+       return send_message(sap_device, buf, size);
+}
+
+int sap_transport_protocol_rsp(void *sap_device, uint8_t result)
+{
+       struct sap_connection *conn = sap_device;
+       char buf[SAP_BUF_SIZE];
+       struct sap_message *msg = (struct sap_message *) buf;
+       size_t size = sizeof(struct sap_message);
+
+       if (!conn)
+               return -EINVAL;
+
+       DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+                                               conn->processing_req, result);
+
+       if (conn->processing_req != SAP_SET_TRANSPORT_PROTOCOL_REQ)
+               return 0;
+
+       memset(buf, 0, sizeof(buf));
+       msg->id = SAP_SET_TRANSPORT_PROTOCOL_RESP;
+       msg->nparam = 0x01;
+       size += add_result_parameter(result, msg->param);
+
+       conn->processing_req = SAP_NO_REQ;
+
+       return send_message(sap_device, buf, size);
+}
+
+int sap_error_rsp(void *sap_device)
+{
+       struct sap_message msg;
+       struct sap_connection *conn = sap_device;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.id = SAP_ERROR_RESP;
+
+       error("SAP error (state %d pr 0x%02x).", conn->state,
+                                                       conn->processing_req);
+
+       return send_message(conn, &msg, sizeof(msg));
+}
+
+int sap_status_ind(void *sap_device, uint8_t status_change)
+{
+       struct sap_connection *conn = sap_device;
+       char buf[SAP_BUF_SIZE];
+       struct sap_message *msg = (struct sap_message *) buf;
+       struct sap_parameter *param = (struct sap_parameter *) msg->param;
+       size_t size = sizeof(struct sap_message);
+
+       if (!conn)
+               return -EINVAL;
+
+       DBG("state %d pr 0x%02x sc 0x%02x", conn->state, conn->processing_req,
+                                                               status_change);
+
+       /* Might be need to change state to connected after ongoing call.*/
+       if (conn->state == SAP_STATE_CONNECT_MODEM_BUSY &&
+                       status_change == SAP_STATUS_CHANGE_CARD_RESET)
+               sap_set_connected(conn);
+
+       if (conn->state != SAP_STATE_CONNECTED &&
+                       conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+               return 0;
+
+       memset(buf, 0, sizeof(buf));
+       msg->id = SAP_STATUS_IND;
+       msg->nparam = 0x01;
+
+       /* Add status change. */
+       param->id  = SAP_PARAM_ID_STATUS_CHANGE;
+       param->len = htons(SAP_PARAM_ID_STATUS_CHANGE_LEN);
+       *param->val = status_change;
+       size += PARAMETER_SIZE(SAP_PARAM_ID_STATUS_CHANGE_LEN);
+
+       return send_message(sap_device, buf, size);
+}
+
+int sap_disconnect_ind(void *sap_device, uint8_t disc_type)
+{
+       struct sap_connection *conn = sap_device;
+
+       return disconnect_req(conn, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+}
+
+static int handle_cmd(struct sap_connection *conn, void *buf, size_t size)
+{
+       struct sap_message *msg = buf;
+
+       if (!conn)
+               return -EINVAL;
+
+       if (size < sizeof(struct sap_message))
+               goto error_rsp;
+
+       if (msg->nparam != 0 && size < (sizeof(struct sap_message) +
+                                       sizeof(struct sap_parameter) + 4))
+               goto error_rsp;
+
+       if (check_msg(msg) < 0)
+               goto error_rsp;
+
+       switch (msg->id) {
+       case SAP_CONNECT_REQ:
+               connect_req(conn, msg->param);
+               return 0;
+       case SAP_DISCONNECT_REQ:
+               disconnect_req(conn, SAP_DISCONNECTION_TYPE_CLIENT);
+               return 0;
+       case SAP_TRANSFER_APDU_REQ:
+               transfer_apdu_req(conn, msg->param);
+               return 0;
+       case SAP_TRANSFER_ATR_REQ:
+               transfer_atr_req(conn);
+               return 0;
+       case SAP_POWER_SIM_OFF_REQ:
+               power_sim_off_req(conn);
+               return 0;
+       case SAP_POWER_SIM_ON_REQ:
+               power_sim_on_req(conn);
+               return 0;
+       case SAP_RESET_SIM_REQ:
+               reset_sim_req(conn);
+               return 0;
+       case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+               transfer_card_reader_status_req(conn);
+               return 0;
+       case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+               set_transport_protocol_req(conn, msg->param);
+               return 0;
+       default:
+               DBG("Unknown SAP message id 0x%02x.", msg->id);
+               break;
+       }
+
+error_rsp:
+       DBG("Invalid SAP message format.");
+       sap_error_rsp(conn);
+       return -EBADMSG;
+}
+
+static void sap_conn_remove(struct sap_connection *conn)
+{
+       DBG("conn %p", conn);
+
+       if (!conn)
+               return;
+
+       if (conn->io) {
+               g_io_channel_shutdown(conn->io, TRUE, NULL);
+               g_io_channel_unref(conn->io);
+       }
+
+       conn->io = NULL;
+       g_free(conn);
+       server->conn = NULL;
+}
+
+static gboolean sap_io_cb(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+       struct sap_connection *conn = data;
+
+       char buf[SAP_BUF_SIZE];
+       size_t bytes_read = 0;
+       GError *gerr = NULL;
+       GIOStatus gstatus;
+
+       DBG("conn %p io %p", conn, io);
+
+       if (cond & G_IO_NVAL) {
+               DBG("ERR (G_IO_NVAL) on rfcomm socket.");
+               return FALSE;
+       }
+
+       if (cond & G_IO_ERR) {
+               DBG("ERR (G_IO_ERR) on rfcomm socket.");
+               return FALSE;
+       }
+
+       if (cond & G_IO_HUP) {
+               DBG("HUP on rfcomm socket.");
+               return FALSE;
+       }
+
+       gstatus = g_io_channel_read_chars(io, buf, sizeof(buf) - 1,
+                                                       &bytes_read, &gerr);
+       if (gstatus != G_IO_STATUS_NORMAL) {
+               if (gerr)
+                       g_error_free(gerr);
+
+               return TRUE;
+       }
+
+       if (handle_cmd(conn, buf, bytes_read) < 0)
+               error("SAP protocol processing failure.");
+
+       return TRUE;
+}
+
+static void sap_io_destroy(void *data)
+{
+       struct sap_connection *conn = data;
+       gboolean connected = FALSE;
+
+       DBG("conn %p", conn);
+
+       if (!conn || !conn->io)
+               return;
+
+       stop_guard_timer(conn);
+
+       if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS &&
+                       conn->state != SAP_STATE_CONNECT_MODEM_BUSY)
+               emit_property_changed(connection, server->path,
+                                       SAP_SERVER_INTERFACE, "Connected",
+                                       DBUS_TYPE_BOOLEAN, &connected);
+
+       if (conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+                       conn->state == SAP_STATE_CONNECT_MODEM_BUSY ||
+                       conn->state == SAP_STATE_CONNECTED ||
+                       conn->state == SAP_STATE_GRACEFUL_DISCONNECT)
+               sap_disconnect_req(NULL, 1);
+
+       sap_conn_remove(conn);
+}
+
+static void sap_connect_cb(GIOChannel *io, GError *gerr, gpointer data)
+{
+       struct sap_connection *conn = data;
+
+       DBG("conn %p, io %p", conn, io);
+
+       if (!conn)
+               return;
+
+       /* Timer will shutdown the channel in case of lack of client
+          activity */
+       start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+
+       g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                       sap_io_cb, conn, sap_io_destroy);
+}
+
+static void connect_auth_cb(DBusError *derr, void *data)
+{
+       struct sap_connection *conn = data;
+       GError *gerr = NULL;
+
+       DBG("conn %p", conn);
+
+       if (!conn)
+               return;
+
+       if (derr && dbus_error_is_set(derr)) {
+               error("Access has been denied (%s)", derr->message);
+               sap_conn_remove(conn);
+               return;
+       }
+
+       if (!bt_io_accept(conn->io, sap_connect_cb, conn, NULL, &gerr)) {
+               error("bt_io_accept: %s", gerr->message);
+               g_error_free(gerr);
+               sap_conn_remove(conn);
+               return;
+       }
+
+       DBG("Access has been granted.");
+}
+
+static void connect_confirm_cb(GIOChannel *io, gpointer data)
+{
+       struct sap_connection *conn = server->conn;
+       GError *gerr = NULL;
+       bdaddr_t src, dst;
+       char dstaddr[18];
+       int err;
+
+       DBG("conn %p io %p", conn, io);
+
+       if (!io)
+               return;
+
+       if (conn) {
+               DBG("Another SAP connection already exists.");
+               g_io_channel_shutdown(io, TRUE, NULL);
+               return;
+       }
+
+       conn = g_try_new0(struct sap_connection, 1);
+       if (!conn) {
+               error("Can't allocate memory for incoming SAP connection.");
+               g_io_channel_shutdown(io, TRUE, NULL);
+               return;
+       }
+
+       g_io_channel_set_encoding(io, NULL, NULL);
+       g_io_channel_set_buffered(io, FALSE);
+
+       server->conn = conn;
+       conn->io = g_io_channel_ref(io);
+       conn->state = SAP_STATE_DISCONNECTED;
+
+       bt_io_get(io, BT_IO_RFCOMM, &gerr,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               sap_conn_remove(conn);
+               return;
+       }
+
+       ba2str(&dst, dstaddr);
+
+       err = btd_request_authorization(&src, &dst, SAP_UUID, connect_auth_cb,
+                                                                       conn);
+       if (err < 0) {
+               error("Authorization failure (err %d)", err);
+               sap_conn_remove(conn);
+               return;
+       }
+
+       DBG("Authorizing incoming SAP connection from %s", dstaddr);
+}
+
+static inline DBusMessage *message_failed(DBusMessage *msg,
+                                       const char *description)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s",
+                                                               description);
+}
+
+static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct sap_server *server = data;
+
+       if (!server)
+               return message_failed(msg, "Server internal error.");
+
+       DBG("conn %p", server->conn);
+
+       if (!server->conn)
+               return message_failed(msg, "Client already disconnected");
+
+       if (disconnect_req(server->conn, SAP_DISCONNECTION_TYPE_GRACEFUL) < 0)
+               return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+                                       "There is no active connection");
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *c,
+                               DBusMessage *msg, void *data)
+{
+       struct sap_connection *conn = data;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       dbus_bool_t connected;
+
+       if (!conn)
+               return message_failed(msg, "Server internal error.");
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       connected = (conn->state == SAP_STATE_CONNECTED ||
+                               conn->state == SAP_STATE_GRACEFUL_DISCONNECT);
+       dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static const GDBusMethodTable server_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect) },
+       { }
+};
+
+static const GDBusSignalTable server_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static void server_free(struct sap_server *server)
+{
+       if (!server)
+               return;
+
+       sap_conn_remove(server->conn);
+       g_free(server->path);
+       g_free(server);
+       server = NULL;
+}
+
+static void destroy_sap_interface(void *data)
+{
+       struct sap_server *server = data;
+
+       DBG("Unregistered interface %s on path %s", SAP_SERVER_INTERFACE,
+                                                               server->path);
+
+       server_free(server);
+}
+
+int sap_server_register(const char *path, bdaddr_t *src)
+{
+       sdp_record_t *record = NULL;
+       GError *gerr = NULL;
+       GIOChannel *io;
+
+       if (sap_init() < 0) {
+               error("Sap driver initialization failed.");
+               return -1;
+       }
+
+       server = g_try_new0(struct sap_server, 1);
+       if (!server) {
+               sap_exit();
+               return -ENOMEM;
+       }
+
+       server->path = g_strdup(path);
+
+       record = create_sap_record(SAP_SERVER_CHANNEL);
+       if (!record) {
+               error("Creating SAP SDP record failed.");
+               goto sdp_err;
+       }
+
+       if (add_record_to_server(src, record) < 0) {
+               error("Adding SAP SDP record to the SDP server failed.");
+               sdp_record_free(record);
+               goto sdp_err;
+       }
+
+       server->record_id = record->handle;
+
+       io = bt_io_listen(BT_IO_RFCOMM, NULL, connect_confirm_cb, server,
+                       NULL, &gerr,
+                       BT_IO_OPT_SOURCE_BDADDR, src,
+                       BT_IO_OPT_CHANNEL, SAP_SERVER_CHANNEL,
+                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
+                       BT_IO_OPT_MASTER, TRUE,
+                       BT_IO_OPT_INVALID);
+       if (!io) {
+               error("Can't listen at channel %d.", SAP_SERVER_CHANNEL);
+               g_error_free(gerr);
+               goto server_err;
+       }
+
+       DBG("Listen socket 0x%02x", g_io_channel_unix_get_fd(io));
+
+       server->listen_io = io;
+       server->conn = NULL;
+
+       if (!g_dbus_register_interface(connection, path, SAP_SERVER_INTERFACE,
+                                       server_methods, server_signals, NULL,
+                                       server, destroy_sap_interface)) {
+               error("D-Bus failed to register %s interface",
+                                                       SAP_SERVER_INTERFACE);
+               goto server_err;
+       }
+
+       return 0;
+
+server_err:
+       remove_record_from_server(server->record_id);
+sdp_err:
+       server_free(server);
+       sap_exit();
+
+       return -1;
+}
+
+int sap_server_unregister(const char *path)
+{
+       if (!server)
+               return -EINVAL;
+
+       remove_record_from_server(server->record_id);
+
+       if (server->conn)
+               sap_conn_remove(server->conn);
+
+       if (server->listen_io) {
+               g_io_channel_shutdown(server->listen_io, TRUE, NULL);
+               g_io_channel_unref(server->listen_io);
+               server->listen_io = NULL;
+       }
+
+       g_dbus_unregister_interface(connection, path, SAP_SERVER_INTERFACE);
+
+       sap_exit();
+
+       return 0;
+}
+
+int sap_server_init(DBusConnection *conn)
+{
+       connection = dbus_connection_ref(conn);
+       return 0;
+}
+
+void sap_server_exit(void)
+{
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
diff --git a/sap/server.h b/sap/server.h
new file mode 100644 (file)
index 0000000..ef2b7b8
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <gdbus.h>
+
+int sap_server_init(DBusConnection *conn);
+void sap_server_exit(void);
+int sap_server_register(const char *path, bdaddr_t *src);
+int sap_server_unregister(const char *path);
diff --git a/sbc/formats.h b/sbc/formats.h
new file mode 100644 (file)
index 0000000..3050b25
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <byteswap.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define COMPOSE_ID(a,b,c,d)    ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
+#define LE_SHORT(v)            (v)
+#define LE_INT(v)              (v)
+#define BE_SHORT(v)            bswap_16(v)
+#define BE_INT(v)              bswap_32(v)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define COMPOSE_ID(a,b,c,d)    ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
+#define LE_SHORT(v)            bswap_16(v)
+#define LE_INT(v)              bswap_32(v)
+#define BE_SHORT(v)            (v)
+#define BE_INT(v)              (v)
+#else
+#error "Wrong endian"
+#endif
+
+#define AU_MAGIC               COMPOSE_ID('.','s','n','d')
+
+#define AU_FMT_ULAW            1
+#define AU_FMT_LIN8            2
+#define AU_FMT_LIN16           3
+
+struct au_header {
+       uint32_t magic;         /* '.snd' */
+       uint32_t hdr_size;      /* size of header (min 24) */
+       uint32_t data_size;     /* size of data */
+       uint32_t encoding;      /* see to AU_FMT_XXXX */
+       uint32_t sample_rate;   /* sample rate */
+       uint32_t channels;      /* number of channels (voices) */
+};
diff --git a/sbc/sbc.c b/sbc/sbc.c
new file mode 100644 (file)
index 0000000..c5015ab
--- /dev/null
+++ b/sbc/sbc.c
@@ -0,0 +1,1241 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2008  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  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
+ *
+ */
+
+/* todo items:
+
+  use a log2 table for byte integer scale factors calculation (sum log2 results
+  for high and low bytes) fill bitpool by 16 bits instead of one at a time in
+  bits allocation/bitpool generation port to the dsp
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <limits.h>
+
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc.h"
+#include "sbc_primitives.h"
+
+#define SBC_SYNCWORD   0x9C
+
+/* This structure contains an unpacked SBC frame.
+   Yes, there is probably quite some unused space herein */
+struct sbc_frame {
+       uint8_t frequency;
+       uint8_t block_mode;
+       uint8_t blocks;
+       enum {
+               MONO            = SBC_MODE_MONO,
+               DUAL_CHANNEL    = SBC_MODE_DUAL_CHANNEL,
+               STEREO          = SBC_MODE_STEREO,
+               JOINT_STEREO    = SBC_MODE_JOINT_STEREO
+       } mode;
+       uint8_t channels;
+       enum {
+               LOUDNESS        = SBC_AM_LOUDNESS,
+               SNR             = SBC_AM_SNR
+       } allocation;
+       uint8_t subband_mode;
+       uint8_t subbands;
+       uint8_t bitpool;
+       uint16_t codesize;
+       uint8_t length;
+
+       /* bit number x set means joint stereo has been used in subband x */
+       uint8_t joint;
+
+       /* only the lower 4 bits of every element are to be used */
+       uint32_t SBC_ALIGNED scale_factor[2][8];
+
+       /* raw integer subband samples in the frame */
+       int32_t SBC_ALIGNED sb_sample_f[16][2][8];
+
+       /* modified subband samples */
+       int32_t SBC_ALIGNED sb_sample[16][2][8];
+
+       /* original pcm audio samples */
+       int16_t SBC_ALIGNED pcm_sample[2][16*8];
+};
+
+struct sbc_decoder_state {
+       int subbands;
+       int32_t V[2][170];
+       int offset[2][16];
+};
+
+/*
+ * Calculates the CRC-8 of the first len bits in data
+ */
+static const uint8_t crc_table[256] = {
+       0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53,
+       0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB,
+       0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E,
+       0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76,
+       0x87, 0x9A, 0xBD, 0xA0, 0xF3, 0xEE, 0xC9, 0xD4,
+       0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C,
+       0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19,
+       0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1,
+       0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
+       0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8,
+       0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D,
+       0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65,
+       0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7,
+       0x7C, 0x61, 0x46, 0x5B, 0x08, 0x15, 0x32, 0x2F,
+       0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A,
+       0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2,
+       0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75,
+       0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
+       0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8,
+       0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50,
+       0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2,
+       0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A,
+       0x6C, 0x71, 0x56, 0x4B, 0x18, 0x05, 0x22, 0x3F,
+       0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7,
+       0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66,
+       0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E,
+       0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
+       0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43,
+       0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1,
+       0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09,
+       0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C,
+       0x97, 0x8A, 0xAD, 0xB0, 0xE3, 0xFE, 0xD9, 0xC4
+};
+
+static uint8_t sbc_crc8(const uint8_t *data, size_t len)
+{
+       uint8_t crc = 0x0f;
+       size_t i;
+       uint8_t octet;
+
+       for (i = 0; i < len / 8; i++)
+               crc = crc_table[crc ^ data[i]];
+
+       octet = data[i];
+       for (i = 0; i < len % 8; i++) {
+               char bit = ((octet ^ crc) & 0x80) >> 7;
+
+               crc = ((crc & 0x7f) << 1) ^ (bit ? 0x1d : 0);
+
+               octet = octet << 1;
+       }
+
+       return crc;
+}
+
+/*
+ * Code straight from the spec to calculate the bits array
+ * Takes a pointer to the frame in question, a pointer to the bits array and
+ * the sampling frequency (as 2 bit integer)
+ */
+static SBC_ALWAYS_INLINE void sbc_calculate_bits_internal(
+               const struct sbc_frame *frame, int (*bits)[8], int subbands)
+{
+       uint8_t sf = frame->frequency;
+
+       if (frame->mode == MONO || frame->mode == DUAL_CHANNEL) {
+               int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice;
+               int ch, sb;
+
+               for (ch = 0; ch < frame->channels; ch++) {
+                       max_bitneed = 0;
+                       if (frame->allocation == SNR) {
+                               for (sb = 0; sb < subbands; sb++) {
+                                       bitneed[ch][sb] = frame->scale_factor[ch][sb];
+                                       if (bitneed[ch][sb] > max_bitneed)
+                                               max_bitneed = bitneed[ch][sb];
+                               }
+                       } else {
+                               for (sb = 0; sb < subbands; sb++) {
+                                       if (frame->scale_factor[ch][sb] == 0)
+                                               bitneed[ch][sb] = -5;
+                                       else {
+                                               if (subbands == 4)
+                                                       loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb];
+                                               else
+                                                       loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb];
+                                               if (loudness > 0)
+                                                       bitneed[ch][sb] = loudness / 2;
+                                               else
+                                                       bitneed[ch][sb] = loudness;
+                                       }
+                                       if (bitneed[ch][sb] > max_bitneed)
+                                               max_bitneed = bitneed[ch][sb];
+                               }
+                       }
+
+                       bitcount = 0;
+                       slicecount = 0;
+                       bitslice = max_bitneed + 1;
+                       do {
+                               bitslice--;
+                               bitcount += slicecount;
+                               slicecount = 0;
+                               for (sb = 0; sb < subbands; sb++) {
+                                       if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16))
+                                               slicecount++;
+                                       else if (bitneed[ch][sb] == bitslice + 1)
+                                               slicecount += 2;
+                               }
+                       } while (bitcount + slicecount < frame->bitpool);
+
+                       if (bitcount + slicecount == frame->bitpool) {
+                               bitcount += slicecount;
+                               bitslice--;
+                       }
+
+                       for (sb = 0; sb < subbands; sb++) {
+                               if (bitneed[ch][sb] < bitslice + 2)
+                                       bits[ch][sb] = 0;
+                               else {
+                                       bits[ch][sb] = bitneed[ch][sb] - bitslice;
+                                       if (bits[ch][sb] > 16)
+                                               bits[ch][sb] = 16;
+                               }
+                       }
+
+                       for (sb = 0; bitcount < frame->bitpool &&
+                                                       sb < subbands; sb++) {
+                               if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) {
+                                       bits[ch][sb]++;
+                                       bitcount++;
+                               } else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) {
+                                       bits[ch][sb] = 2;
+                                       bitcount += 2;
+                               }
+                       }
+
+                       for (sb = 0; bitcount < frame->bitpool &&
+                                                       sb < subbands; sb++) {
+                               if (bits[ch][sb] < 16) {
+                                       bits[ch][sb]++;
+                                       bitcount++;
+                               }
+                       }
+
+               }
+
+       } else if (frame->mode == STEREO || frame->mode == JOINT_STEREO) {
+               int bitneed[2][8], loudness, max_bitneed, bitcount, slicecount, bitslice;
+               int ch, sb;
+
+               max_bitneed = 0;
+               if (frame->allocation == SNR) {
+                       for (ch = 0; ch < 2; ch++) {
+                               for (sb = 0; sb < subbands; sb++) {
+                                       bitneed[ch][sb] = frame->scale_factor[ch][sb];
+                                       if (bitneed[ch][sb] > max_bitneed)
+                                               max_bitneed = bitneed[ch][sb];
+                               }
+                       }
+               } else {
+                       for (ch = 0; ch < 2; ch++) {
+                               for (sb = 0; sb < subbands; sb++) {
+                                       if (frame->scale_factor[ch][sb] == 0)
+                                               bitneed[ch][sb] = -5;
+                                       else {
+                                               if (subbands == 4)
+                                                       loudness = frame->scale_factor[ch][sb] - sbc_offset4[sf][sb];
+                                               else
+                                                       loudness = frame->scale_factor[ch][sb] - sbc_offset8[sf][sb];
+                                               if (loudness > 0)
+                                                       bitneed[ch][sb] = loudness / 2;
+                                               else
+                                                       bitneed[ch][sb] = loudness;
+                                       }
+                                       if (bitneed[ch][sb] > max_bitneed)
+                                               max_bitneed = bitneed[ch][sb];
+                               }
+                       }
+               }
+
+               bitcount = 0;
+               slicecount = 0;
+               bitslice = max_bitneed + 1;
+               do {
+                       bitslice--;
+                       bitcount += slicecount;
+                       slicecount = 0;
+                       for (ch = 0; ch < 2; ch++) {
+                               for (sb = 0; sb < subbands; sb++) {
+                                       if ((bitneed[ch][sb] > bitslice + 1) && (bitneed[ch][sb] < bitslice + 16))
+                                               slicecount++;
+                                       else if (bitneed[ch][sb] == bitslice + 1)
+                                               slicecount += 2;
+                               }
+                       }
+               } while (bitcount + slicecount < frame->bitpool);
+
+               if (bitcount + slicecount == frame->bitpool) {
+                       bitcount += slicecount;
+                       bitslice--;
+               }
+
+               for (ch = 0; ch < 2; ch++) {
+                       for (sb = 0; sb < subbands; sb++) {
+                               if (bitneed[ch][sb] < bitslice + 2) {
+                                       bits[ch][sb] = 0;
+                               } else {
+                                       bits[ch][sb] = bitneed[ch][sb] - bitslice;
+                                       if (bits[ch][sb] > 16)
+                                               bits[ch][sb] = 16;
+                               }
+                       }
+               }
+
+               ch = 0;
+               sb = 0;
+               while (bitcount < frame->bitpool) {
+                       if ((bits[ch][sb] >= 2) && (bits[ch][sb] < 16)) {
+                               bits[ch][sb]++;
+                               bitcount++;
+                       } else if ((bitneed[ch][sb] == bitslice + 1) && (frame->bitpool > bitcount + 1)) {
+                               bits[ch][sb] = 2;
+                               bitcount += 2;
+                       }
+                       if (ch == 1) {
+                               ch = 0;
+                               sb++;
+                               if (sb >= subbands)
+                                       break;
+                       } else
+                               ch = 1;
+               }
+
+               ch = 0;
+               sb = 0;
+               while (bitcount < frame->bitpool) {
+                       if (bits[ch][sb] < 16) {
+                               bits[ch][sb]++;
+                               bitcount++;
+                       }
+                       if (ch == 1) {
+                               ch = 0;
+                               sb++;
+                               if (sb >= subbands)
+                                       break;
+                       } else
+                               ch = 1;
+               }
+
+       }
+
+}
+
+static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8])
+{
+       if (frame->subbands == 4)
+               sbc_calculate_bits_internal(frame, bits, 4);
+       else
+               sbc_calculate_bits_internal(frame, bits, 8);
+}
+
+/*
+ * Unpacks a SBC frame at the beginning of the stream in data,
+ * which has at most len bytes into frame.
+ * Returns the length in bytes of the packed frame, or a negative
+ * value on error. The error codes are:
+ *
+ *  -1   Data stream too short
+ *  -2   Sync byte incorrect
+ *  -3   CRC8 incorrect
+ *  -4   Bitpool value out of bounds
+ */
+static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
+                                                               size_t len)
+{
+       unsigned int consumed;
+       /* Will copy the parts of the header that are relevant to crc
+        * calculation here */
+       uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+       int crc_pos = 0;
+       int32_t temp;
+
+       uint32_t audio_sample;
+       int ch, sb, blk, bit;   /* channel, subband, block and bit standard
+                                  counters */
+       int bits[2][8];         /* bits distribution */
+       uint32_t levels[2][8];  /* levels derived from that */
+
+       if (len < 4)
+               return -1;
+
+       if (data[0] != SBC_SYNCWORD)
+               return -2;
+
+       frame->frequency = (data[1] >> 6) & 0x03;
+
+       frame->block_mode = (data[1] >> 4) & 0x03;
+       switch (frame->block_mode) {
+       case SBC_BLK_4:
+               frame->blocks = 4;
+               break;
+       case SBC_BLK_8:
+               frame->blocks = 8;
+               break;
+       case SBC_BLK_12:
+               frame->blocks = 12;
+               break;
+       case SBC_BLK_16:
+               frame->blocks = 16;
+               break;
+       }
+
+       frame->mode = (data[1] >> 2) & 0x03;
+       switch (frame->mode) {
+       case MONO:
+               frame->channels = 1;
+               break;
+       case DUAL_CHANNEL:      /* fall-through */
+       case STEREO:
+       case JOINT_STEREO:
+               frame->channels = 2;
+               break;
+       }
+
+       frame->allocation = (data[1] >> 1) & 0x01;
+
+       frame->subband_mode = (data[1] & 0x01);
+       frame->subbands = frame->subband_mode ? 8 : 4;
+
+       frame->bitpool = data[2];
+
+       if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
+                       frame->bitpool > 16 * frame->subbands)
+               return -4;
+
+       if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
+                       frame->bitpool > 32 * frame->subbands)
+               return -4;
+
+       /* data[3] is crc, we're checking it later */
+
+       consumed = 32;
+
+       crc_header[0] = data[1];
+       crc_header[1] = data[2];
+       crc_pos = 16;
+
+       if (frame->mode == JOINT_STEREO) {
+               if (len * 8 < consumed + frame->subbands)
+                       return -1;
+
+               frame->joint = 0x00;
+               for (sb = 0; sb < frame->subbands - 1; sb++)
+                       frame->joint |= ((data[4] >> (7 - sb)) & 0x01) << sb;
+               if (frame->subbands == 4)
+                       crc_header[crc_pos / 8] = data[4] & 0xf0;
+               else
+                       crc_header[crc_pos / 8] = data[4];
+
+               consumed += frame->subbands;
+               crc_pos += frame->subbands;
+       }
+
+       if (len * 8 < consumed + (4 * frame->subbands * frame->channels))
+               return -1;
+
+       for (ch = 0; ch < frame->channels; ch++) {
+               for (sb = 0; sb < frame->subbands; sb++) {
+                       /* FIXME assert(consumed % 4 == 0); */
+                       frame->scale_factor[ch][sb] =
+                               (data[consumed >> 3] >> (4 - (consumed & 0x7))) & 0x0F;
+                       crc_header[crc_pos >> 3] |=
+                               frame->scale_factor[ch][sb] << (4 - (crc_pos & 0x7));
+
+                       consumed += 4;
+                       crc_pos += 4;
+               }
+       }
+
+       if (data[3] != sbc_crc8(crc_header, crc_pos))
+               return -3;
+
+       sbc_calculate_bits(frame, bits);
+
+       for (ch = 0; ch < frame->channels; ch++) {
+               for (sb = 0; sb < frame->subbands; sb++)
+                       levels[ch][sb] = (1 << bits[ch][sb]) - 1;
+       }
+
+       for (blk = 0; blk < frame->blocks; blk++) {
+               for (ch = 0; ch < frame->channels; ch++) {
+                       for (sb = 0; sb < frame->subbands; sb++) {
+                               uint32_t shift;
+
+                               if (levels[ch][sb] == 0) {
+                                       frame->sb_sample[blk][ch][sb] = 0;
+                                       continue;
+                               }
+
+                               shift = frame->scale_factor[ch][sb] +
+                                               1 + SBCDEC_FIXED_EXTRA_BITS;
+
+                               audio_sample = 0;
+                               for (bit = 0; bit < bits[ch][sb]; bit++) {
+                                       if (consumed > len * 8)
+                                               return -1;
+
+                                       if ((data[consumed >> 3] >> (7 - (consumed & 0x7))) & 0x01)
+                                               audio_sample |= 1 << (bits[ch][sb] - bit - 1);
+
+                                       consumed++;
+                               }
+
+                               frame->sb_sample[blk][ch][sb] = (int32_t)
+                                       (((((uint64_t) audio_sample << 1) | 1) << shift) /
+                                       levels[ch][sb]) - (1 << shift);
+                       }
+               }
+       }
+
+       if (frame->mode == JOINT_STEREO) {
+               for (blk = 0; blk < frame->blocks; blk++) {
+                       for (sb = 0; sb < frame->subbands; sb++) {
+                               if (frame->joint & (0x01 << sb)) {
+                                       temp = frame->sb_sample[blk][0][sb] +
+                                               frame->sb_sample[blk][1][sb];
+                                       frame->sb_sample[blk][1][sb] =
+                                               frame->sb_sample[blk][0][sb] -
+                                               frame->sb_sample[blk][1][sb];
+                                       frame->sb_sample[blk][0][sb] = temp;
+                               }
+                       }
+               }
+       }
+
+       if ((consumed & 0x7) != 0)
+               consumed += 8 - (consumed & 0x7);
+
+       return consumed >> 3;
+}
+
+static void sbc_decoder_init(struct sbc_decoder_state *state,
+                                       const struct sbc_frame *frame)
+{
+       int i, ch;
+
+       memset(state->V, 0, sizeof(state->V));
+       state->subbands = frame->subbands;
+
+       for (ch = 0; ch < 2; ch++)
+               for (i = 0; i < frame->subbands * 2; i++)
+                       state->offset[ch][i] = (10 * i + 10);
+}
+
+static SBC_ALWAYS_INLINE int16_t sbc_clip16(int32_t s)
+{
+       if (s > 0x7FFF)
+               return 0x7FFF;
+       else if (s < -0x8000)
+               return -0x8000;
+       else
+               return s;
+}
+
+static inline void sbc_synthesize_four(struct sbc_decoder_state *state,
+                               struct sbc_frame *frame, int ch, int blk)
+{
+       int i, k, idx;
+       int32_t *v = state->V[ch];
+       int *offset = state->offset[ch];
+
+       for (i = 0; i < 8; i++) {
+               /* Shifting */
+               offset[i]--;
+               if (offset[i] < 0) {
+                       offset[i] = 79;
+                       memcpy(v + 80, v, 9 * sizeof(*v));
+               }
+
+               /* Distribute the new matrix value to the shifted position */
+               v[offset[i]] = SCALE4_STAGED1(
+                       MULA(synmatrix4[i][0], frame->sb_sample[blk][ch][0],
+                       MULA(synmatrix4[i][1], frame->sb_sample[blk][ch][1],
+                       MULA(synmatrix4[i][2], frame->sb_sample[blk][ch][2],
+                       MUL (synmatrix4[i][3], frame->sb_sample[blk][ch][3])))));
+       }
+
+       /* Compute the samples */
+       for (idx = 0, i = 0; i < 4; i++, idx += 5) {
+               k = (i + 4) & 0xf;
+
+               /* Store in output, Q0 */
+               frame->pcm_sample[ch][blk * 4 + i] = sbc_clip16(SCALE4_STAGED1(
+                       MULA(v[offset[i] + 0], sbc_proto_4_40m0[idx + 0],
+                       MULA(v[offset[k] + 1], sbc_proto_4_40m1[idx + 0],
+                       MULA(v[offset[i] + 2], sbc_proto_4_40m0[idx + 1],
+                       MULA(v[offset[k] + 3], sbc_proto_4_40m1[idx + 1],
+                       MULA(v[offset[i] + 4], sbc_proto_4_40m0[idx + 2],
+                       MULA(v[offset[k] + 5], sbc_proto_4_40m1[idx + 2],
+                       MULA(v[offset[i] + 6], sbc_proto_4_40m0[idx + 3],
+                       MULA(v[offset[k] + 7], sbc_proto_4_40m1[idx + 3],
+                       MULA(v[offset[i] + 8], sbc_proto_4_40m0[idx + 4],
+                       MUL( v[offset[k] + 9], sbc_proto_4_40m1[idx + 4]))))))))))));
+       }
+}
+
+static inline void sbc_synthesize_eight(struct sbc_decoder_state *state,
+                               struct sbc_frame *frame, int ch, int blk)
+{
+       int i, j, k, idx;
+       int *offset = state->offset[ch];
+
+       for (i = 0; i < 16; i++) {
+               /* Shifting */
+               offset[i]--;
+               if (offset[i] < 0) {
+                       offset[i] = 159;
+                       for (j = 0; j < 9; j++)
+                               state->V[ch][j + 160] = state->V[ch][j];
+               }
+
+               /* Distribute the new matrix value to the shifted position */
+               state->V[ch][offset[i]] = SCALE8_STAGED1(
+                       MULA(synmatrix8[i][0], frame->sb_sample[blk][ch][0],
+                       MULA(synmatrix8[i][1], frame->sb_sample[blk][ch][1],
+                       MULA(synmatrix8[i][2], frame->sb_sample[blk][ch][2],
+                       MULA(synmatrix8[i][3], frame->sb_sample[blk][ch][3],
+                       MULA(synmatrix8[i][4], frame->sb_sample[blk][ch][4],
+                       MULA(synmatrix8[i][5], frame->sb_sample[blk][ch][5],
+                       MULA(synmatrix8[i][6], frame->sb_sample[blk][ch][6],
+                       MUL( synmatrix8[i][7], frame->sb_sample[blk][ch][7])))))))));
+       }
+
+       /* Compute the samples */
+       for (idx = 0, i = 0; i < 8; i++, idx += 5) {
+               k = (i + 8) & 0xf;
+
+               /* Store in output, Q0 */
+               frame->pcm_sample[ch][blk * 8 + i] = sbc_clip16(SCALE8_STAGED1(
+                       MULA(state->V[ch][offset[i] + 0], sbc_proto_8_80m0[idx + 0],
+                       MULA(state->V[ch][offset[k] + 1], sbc_proto_8_80m1[idx + 0],
+                       MULA(state->V[ch][offset[i] + 2], sbc_proto_8_80m0[idx + 1],
+                       MULA(state->V[ch][offset[k] + 3], sbc_proto_8_80m1[idx + 1],
+                       MULA(state->V[ch][offset[i] + 4], sbc_proto_8_80m0[idx + 2],
+                       MULA(state->V[ch][offset[k] + 5], sbc_proto_8_80m1[idx + 2],
+                       MULA(state->V[ch][offset[i] + 6], sbc_proto_8_80m0[idx + 3],
+                       MULA(state->V[ch][offset[k] + 7], sbc_proto_8_80m1[idx + 3],
+                       MULA(state->V[ch][offset[i] + 8], sbc_proto_8_80m0[idx + 4],
+                       MUL( state->V[ch][offset[k] + 9], sbc_proto_8_80m1[idx + 4]))))))))))));
+       }
+}
+
+static int sbc_synthesize_audio(struct sbc_decoder_state *state,
+                                               struct sbc_frame *frame)
+{
+       int ch, blk;
+
+       switch (frame->subbands) {
+       case 4:
+               for (ch = 0; ch < frame->channels; ch++) {
+                       for (blk = 0; blk < frame->blocks; blk++)
+                               sbc_synthesize_four(state, frame, ch, blk);
+               }
+               return frame->blocks * 4;
+
+       case 8:
+               for (ch = 0; ch < frame->channels; ch++) {
+                       for (blk = 0; blk < frame->blocks; blk++)
+                               sbc_synthesize_eight(state, frame, ch, blk);
+               }
+               return frame->blocks * 8;
+
+       default:
+               return -EIO;
+       }
+}
+
+static int sbc_analyze_audio(struct sbc_encoder_state *state,
+                                               struct sbc_frame *frame)
+{
+       int ch, blk;
+       int16_t *x;
+
+       switch (frame->subbands) {
+       case 4:
+               for (ch = 0; ch < frame->channels; ch++) {
+                       x = &state->X[ch][state->position - 16 +
+                                                       frame->blocks * 4];
+                       for (blk = 0; blk < frame->blocks; blk += 4) {
+                               state->sbc_analyze_4b_4s(
+                                       x,
+                                       frame->sb_sample_f[blk][ch],
+                                       frame->sb_sample_f[blk + 1][ch] -
+                                       frame->sb_sample_f[blk][ch]);
+                               x -= 16;
+                       }
+               }
+               return frame->blocks * 4;
+
+       case 8:
+               for (ch = 0; ch < frame->channels; ch++) {
+                       x = &state->X[ch][state->position - 32 +
+                                                       frame->blocks * 8];
+                       for (blk = 0; blk < frame->blocks; blk += 4) {
+                               state->sbc_analyze_4b_8s(
+                                       x,
+                                       frame->sb_sample_f[blk][ch],
+                                       frame->sb_sample_f[blk + 1][ch] -
+                                       frame->sb_sample_f[blk][ch]);
+                               x -= 32;
+                       }
+               }
+               return frame->blocks * 8;
+
+       default:
+               return -EIO;
+       }
+}
+
+/* Supplementary bitstream writing macros for 'sbc_pack_frame' */
+
+#define PUT_BITS(data_ptr, bits_cache, bits_count, v, n)               \
+       do {                                                            \
+               bits_cache = (v) | (bits_cache << (n));                 \
+               bits_count += (n);                                      \
+               if (bits_count >= 16) {                                 \
+                       bits_count -= 8;                                \
+                       *data_ptr++ = (uint8_t)                         \
+                               (bits_cache >> bits_count);             \
+                       bits_count -= 8;                                \
+                       *data_ptr++ = (uint8_t)                         \
+                               (bits_cache >> bits_count);             \
+               }                                                       \
+       } while (0)
+
+#define FLUSH_BITS(data_ptr, bits_cache, bits_count)                   \
+       do {                                                            \
+               while (bits_count >= 8) {                               \
+                       bits_count -= 8;                                \
+                       *data_ptr++ = (uint8_t)                         \
+                               (bits_cache >> bits_count);             \
+               }                                                       \
+               if (bits_count > 0)                                     \
+                       *data_ptr++ = (uint8_t)                         \
+                               (bits_cache << (8 - bits_count));       \
+       } while (0)
+
+/*
+ * Packs the SBC frame from frame into the memory at data. At most len
+ * bytes will be used, should more memory be needed an appropriate
+ * error code will be returned. Returns the length of the packed frame
+ * on success or a negative value on error.
+ *
+ * The error codes are:
+ * -1 Not enough memory reserved
+ * -2 Unsupported sampling rate
+ * -3 Unsupported number of blocks
+ * -4 Unsupported number of subbands
+ * -5 Bitpool value out of bounds
+ * -99 not implemented
+ */
+
+static SBC_ALWAYS_INLINE ssize_t sbc_pack_frame_internal(uint8_t *data,
+                                       struct sbc_frame *frame, size_t len,
+                                       int frame_subbands, int frame_channels,
+                                       int joint)
+{
+       /* Bitstream writer starts from the fourth byte */
+       uint8_t *data_ptr = data + 4;
+       uint32_t bits_cache = 0;
+       uint32_t bits_count = 0;
+
+       /* Will copy the header parts for CRC-8 calculation here */
+       uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+       int crc_pos = 0;
+
+       uint32_t audio_sample;
+
+       int ch, sb, blk;        /* channel, subband, block and bit counters */
+       int bits[2][8];         /* bits distribution */
+       uint32_t levels[2][8];  /* levels are derived from that */
+       uint32_t sb_sample_delta[2][8];
+
+       data[0] = SBC_SYNCWORD;
+
+       data[1] = (frame->frequency & 0x03) << 6;
+
+       data[1] |= (frame->block_mode & 0x03) << 4;
+
+       data[1] |= (frame->mode & 0x03) << 2;
+
+       data[1] |= (frame->allocation & 0x01) << 1;
+
+       switch (frame_subbands) {
+       case 4:
+               /* Nothing to do */
+               break;
+       case 8:
+               data[1] |= 0x01;
+               break;
+       default:
+               return -4;
+               break;
+       }
+
+       data[2] = frame->bitpool;
+
+       if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
+                       frame->bitpool > frame_subbands << 4)
+               return -5;
+
+       if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
+                       frame->bitpool > frame_subbands << 5)
+               return -5;
+
+       /* Can't fill in crc yet */
+
+       crc_header[0] = data[1];
+       crc_header[1] = data[2];
+       crc_pos = 16;
+
+       if (frame->mode == JOINT_STEREO) {
+               PUT_BITS(data_ptr, bits_cache, bits_count,
+                       joint, frame_subbands);
+               crc_header[crc_pos >> 3] = joint;
+               crc_pos += frame_subbands;
+       }
+
+       for (ch = 0; ch < frame_channels; ch++) {
+               for (sb = 0; sb < frame_subbands; sb++) {
+                       PUT_BITS(data_ptr, bits_cache, bits_count,
+                               frame->scale_factor[ch][sb] & 0x0F, 4);
+                       crc_header[crc_pos >> 3] <<= 4;
+                       crc_header[crc_pos >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
+                       crc_pos += 4;
+               }
+       }
+
+       /* align the last crc byte */
+       if (crc_pos % 8)
+               crc_header[crc_pos >> 3] <<= 8 - (crc_pos % 8);
+
+       data[3] = sbc_crc8(crc_header, crc_pos);
+
+       sbc_calculate_bits(frame, bits);
+
+       for (ch = 0; ch < frame_channels; ch++) {
+               for (sb = 0; sb < frame_subbands; sb++) {
+                       levels[ch][sb] = ((1 << bits[ch][sb]) - 1) <<
+                               (32 - (frame->scale_factor[ch][sb] +
+                                       SCALE_OUT_BITS + 2));
+                       sb_sample_delta[ch][sb] = (uint32_t) 1 <<
+                               (frame->scale_factor[ch][sb] +
+                                       SCALE_OUT_BITS + 1);
+               }
+       }
+
+       for (blk = 0; blk < frame->blocks; blk++) {
+               for (ch = 0; ch < frame_channels; ch++) {
+                       for (sb = 0; sb < frame_subbands; sb++) {
+
+                               if (bits[ch][sb] == 0)
+                                       continue;
+
+                               audio_sample = ((uint64_t) levels[ch][sb] *
+                                       (sb_sample_delta[ch][sb] +
+                                       frame->sb_sample_f[blk][ch][sb])) >> 32;
+
+                               PUT_BITS(data_ptr, bits_cache, bits_count,
+                                       audio_sample, bits[ch][sb]);
+                       }
+               }
+       }
+
+       FLUSH_BITS(data_ptr, bits_cache, bits_count);
+
+       return data_ptr - data;
+}
+
+static ssize_t sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len,
+                                                               int joint)
+{
+       if (frame->subbands == 4) {
+               if (frame->channels == 1)
+                       return sbc_pack_frame_internal(
+                               data, frame, len, 4, 1, joint);
+               else
+                       return sbc_pack_frame_internal(
+                               data, frame, len, 4, 2, joint);
+       } else {
+               if (frame->channels == 1)
+                       return sbc_pack_frame_internal(
+                               data, frame, len, 8, 1, joint);
+               else
+                       return sbc_pack_frame_internal(
+                               data, frame, len, 8, 2, joint);
+       }
+}
+
+static void sbc_encoder_init(struct sbc_encoder_state *state,
+                                       const struct sbc_frame *frame)
+{
+       memset(&state->X, 0, sizeof(state->X));
+       state->position = (SBC_X_BUFFER_SIZE - frame->subbands * 9) & ~7;
+
+       sbc_init_primitives(state);
+}
+
+struct sbc_priv {
+       int init;
+       struct SBC_ALIGNED sbc_frame frame;
+       struct SBC_ALIGNED sbc_decoder_state dec_state;
+       struct SBC_ALIGNED sbc_encoder_state enc_state;
+};
+
+static void sbc_set_defaults(sbc_t *sbc, unsigned long flags)
+{
+       sbc->frequency = SBC_FREQ_44100;
+       sbc->mode = SBC_MODE_STEREO;
+       sbc->subbands = SBC_SB_8;
+       sbc->blocks = SBC_BLK_16;
+       sbc->bitpool = 32;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       sbc->endian = SBC_LE;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+       sbc->endian = SBC_BE;
+#else
+#error "Unknown byte order"
+#endif
+}
+
+int sbc_init(sbc_t *sbc, unsigned long flags)
+{
+       if (!sbc)
+               return -EIO;
+
+       memset(sbc, 0, sizeof(sbc_t));
+
+       sbc->priv_alloc_base = malloc(sizeof(struct sbc_priv) + SBC_ALIGN_MASK);
+       if (!sbc->priv_alloc_base)
+               return -ENOMEM;
+
+       sbc->priv = (void *) (((uintptr_t) sbc->priv_alloc_base +
+                       SBC_ALIGN_MASK) & ~((uintptr_t) SBC_ALIGN_MASK));
+
+       memset(sbc->priv, 0, sizeof(struct sbc_priv));
+
+       sbc_set_defaults(sbc, flags);
+
+       return 0;
+}
+
+ssize_t sbc_parse(sbc_t *sbc, const void *input, size_t input_len)
+{
+       return sbc_decode(sbc, input, input_len, NULL, 0, NULL);
+}
+
+ssize_t sbc_decode(sbc_t *sbc, const void *input, size_t input_len,
+                       void *output, size_t output_len, size_t *written)
+{
+       struct sbc_priv *priv;
+       char *ptr;
+       int i, ch, framelen, samples;
+
+       if (!sbc || !input)
+               return -EIO;
+
+       priv = sbc->priv;
+
+       framelen = sbc_unpack_frame(input, &priv->frame, input_len);
+
+       if (!priv->init) {
+               sbc_decoder_init(&priv->dec_state, &priv->frame);
+               priv->init = 1;
+
+               sbc->frequency = priv->frame.frequency;
+               sbc->mode = priv->frame.mode;
+               sbc->subbands = priv->frame.subband_mode;
+               sbc->blocks = priv->frame.block_mode;
+               sbc->allocation = priv->frame.allocation;
+               sbc->bitpool = priv->frame.bitpool;
+
+               priv->frame.codesize = sbc_get_codesize(sbc);
+               priv->frame.length = framelen;
+       } else if (priv->frame.bitpool != sbc->bitpool) {
+               priv->frame.length = framelen;
+               sbc->bitpool = priv->frame.bitpool;
+       }
+
+       if (!output)
+               return framelen;
+
+       if (written)
+               *written = 0;
+
+       if (framelen <= 0)
+               return framelen;
+
+       samples = sbc_synthesize_audio(&priv->dec_state, &priv->frame);
+
+       ptr = output;
+
+       if (output_len < (size_t) (samples * priv->frame.channels * 2))
+               samples = output_len / (priv->frame.channels * 2);
+
+       for (i = 0; i < samples; i++) {
+               for (ch = 0; ch < priv->frame.channels; ch++) {
+                       int16_t s;
+                       s = priv->frame.pcm_sample[ch][i];
+
+                       if (sbc->endian == SBC_BE) {
+                               *ptr++ = (s & 0xff00) >> 8;
+                               *ptr++ = (s & 0x00ff);
+                       } else {
+                               *ptr++ = (s & 0x00ff);
+                               *ptr++ = (s & 0xff00) >> 8;
+                       }
+               }
+       }
+
+       if (written)
+               *written = samples * priv->frame.channels * 2;
+
+       return framelen;
+}
+
+ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len,
+                       void *output, size_t output_len, ssize_t *written)
+{
+       struct sbc_priv *priv;
+       int samples;
+       ssize_t framelen;
+       int (*sbc_enc_process_input)(int position,
+                       const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+                       int nsamples, int nchannels);
+
+       if (!sbc || !input)
+               return -EIO;
+
+       priv = sbc->priv;
+
+       if (written)
+               *written = 0;
+
+       if (!priv->init) {
+               priv->frame.frequency = sbc->frequency;
+               priv->frame.mode = sbc->mode;
+               priv->frame.channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+               priv->frame.allocation = sbc->allocation;
+               priv->frame.subband_mode = sbc->subbands;
+               priv->frame.subbands = sbc->subbands ? 8 : 4;
+               priv->frame.block_mode = sbc->blocks;
+               priv->frame.blocks = 4 + (sbc->blocks * 4);
+               priv->frame.bitpool = sbc->bitpool;
+               priv->frame.codesize = sbc_get_codesize(sbc);
+               priv->frame.length = sbc_get_frame_length(sbc);
+
+               sbc_encoder_init(&priv->enc_state, &priv->frame);
+               priv->init = 1;
+       } else if (priv->frame.bitpool != sbc->bitpool) {
+               priv->frame.length = sbc_get_frame_length(sbc);
+               priv->frame.bitpool = sbc->bitpool;
+       }
+
+       /* input must be large enough to encode a complete frame */
+       if (input_len < priv->frame.codesize)
+               return 0;
+
+       /* output must be large enough to receive the encoded frame */
+       if (!output || output_len < priv->frame.length)
+               return -ENOSPC;
+
+       /* Select the needed input data processing function and call it */
+       if (priv->frame.subbands == 8) {
+               if (sbc->endian == SBC_BE)
+                       sbc_enc_process_input =
+                               priv->enc_state.sbc_enc_process_input_8s_be;
+               else
+                       sbc_enc_process_input =
+                               priv->enc_state.sbc_enc_process_input_8s_le;
+       } else {
+               if (sbc->endian == SBC_BE)
+                       sbc_enc_process_input =
+                               priv->enc_state.sbc_enc_process_input_4s_be;
+               else
+                       sbc_enc_process_input =
+                               priv->enc_state.sbc_enc_process_input_4s_le;
+       }
+
+       priv->enc_state.position = sbc_enc_process_input(
+               priv->enc_state.position, (const uint8_t *) input,
+               priv->enc_state.X, priv->frame.subbands * priv->frame.blocks,
+               priv->frame.channels);
+
+       samples = sbc_analyze_audio(&priv->enc_state, &priv->frame);
+
+       if (priv->frame.mode == JOINT_STEREO) {
+               int j = priv->enc_state.sbc_calc_scalefactors_j(
+                       priv->frame.sb_sample_f, priv->frame.scale_factor,
+                       priv->frame.blocks, priv->frame.subbands);
+               framelen = sbc_pack_frame(output, &priv->frame, output_len, j);
+       } else {
+               priv->enc_state.sbc_calc_scalefactors(
+                       priv->frame.sb_sample_f, priv->frame.scale_factor,
+                       priv->frame.blocks, priv->frame.channels,
+                       priv->frame.subbands);
+               framelen = sbc_pack_frame(output, &priv->frame, output_len, 0);
+       }
+
+       if (written)
+               *written = framelen;
+
+       return samples * priv->frame.channels * 2;
+}
+
+void sbc_finish(sbc_t *sbc)
+{
+       if (!sbc)
+               return;
+
+       free(sbc->priv_alloc_base);
+
+       memset(sbc, 0, sizeof(sbc_t));
+}
+
+size_t sbc_get_frame_length(sbc_t *sbc)
+{
+       int ret;
+       uint8_t subbands, channels, blocks, joint, bitpool;
+       struct sbc_priv *priv;
+
+       priv = sbc->priv;
+       if (priv->init && priv->frame.bitpool == sbc->bitpool)
+               return priv->frame.length;
+
+       subbands = sbc->subbands ? 8 : 4;
+       blocks = 4 + (sbc->blocks * 4);
+       channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+       joint = sbc->mode == SBC_MODE_JOINT_STEREO ? 1 : 0;
+       bitpool = sbc->bitpool;
+
+       ret = 4 + (4 * subbands * channels) / 8;
+       /* This term is not always evenly divide so we round it up */
+       if (channels == 1)
+               ret += ((blocks * channels * bitpool) + 7) / 8;
+       else
+               ret += (((joint ? subbands : 0) + blocks * bitpool) + 7) / 8;
+
+       return ret;
+}
+
+unsigned sbc_get_frame_duration(sbc_t *sbc)
+{
+       uint8_t subbands, blocks;
+       uint16_t frequency;
+       struct sbc_priv *priv;
+
+       priv = sbc->priv;
+       if (!priv->init) {
+               subbands = sbc->subbands ? 8 : 4;
+               blocks = 4 + (sbc->blocks * 4);
+       } else {
+               subbands = priv->frame.subbands;
+               blocks = priv->frame.blocks;
+       }
+
+       switch (sbc->frequency) {
+       case SBC_FREQ_16000:
+               frequency = 16000;
+               break;
+
+       case SBC_FREQ_32000:
+               frequency = 32000;
+               break;
+
+       case SBC_FREQ_44100:
+               frequency = 44100;
+               break;
+
+       case SBC_FREQ_48000:
+               frequency = 48000;
+               break;
+       default:
+               return 0;
+       }
+
+       return (1000000 * blocks * subbands) / frequency;
+}
+
+size_t sbc_get_codesize(sbc_t *sbc)
+{
+       uint16_t subbands, channels, blocks;
+       struct sbc_priv *priv;
+
+       priv = sbc->priv;
+       if (!priv->init) {
+               subbands = sbc->subbands ? 8 : 4;
+               blocks = 4 + (sbc->blocks * 4);
+               channels = sbc->mode == SBC_MODE_MONO ? 1 : 2;
+       } else {
+               subbands = priv->frame.subbands;
+               blocks = priv->frame.blocks;
+               channels = priv->frame.channels;
+       }
+
+       return subbands * blocks * channels * 2;
+}
+
+const char *sbc_get_implementation_info(sbc_t *sbc)
+{
+       struct sbc_priv *priv;
+
+       if (!sbc)
+               return NULL;
+
+       priv = sbc->priv;
+       if (!priv)
+               return NULL;
+
+       return priv->enc_state.implementation_info;
+}
+
+int sbc_reinit(sbc_t *sbc, unsigned long flags)
+{
+       struct sbc_priv *priv;
+
+       if (!sbc || !sbc->priv)
+               return -EIO;
+
+       priv = sbc->priv;
+
+       if (priv->init == 1)
+               memset(sbc->priv, 0, sizeof(struct sbc_priv));
+
+       sbc_set_defaults(sbc, flags);
+
+       return 0;
+}
diff --git a/sbc/sbc.h b/sbc/sbc.h
new file mode 100644 (file)
index 0000000..f7ec99d
--- /dev/null
+++ b/sbc/sbc.h
@@ -0,0 +1,113 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __SBC_H
+#define __SBC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+
+/* sampling frequency */
+#define SBC_FREQ_16000         0x00
+#define SBC_FREQ_32000         0x01
+#define SBC_FREQ_44100         0x02
+#define SBC_FREQ_48000         0x03
+
+/* blocks */
+#define SBC_BLK_4              0x00
+#define SBC_BLK_8              0x01
+#define SBC_BLK_12             0x02
+#define SBC_BLK_16             0x03
+
+/* channel mode */
+#define SBC_MODE_MONO          0x00
+#define SBC_MODE_DUAL_CHANNEL  0x01
+#define SBC_MODE_STEREO                0x02
+#define SBC_MODE_JOINT_STEREO  0x03
+
+/* allocation method */
+#define SBC_AM_LOUDNESS                0x00
+#define SBC_AM_SNR             0x01
+
+/* subbands */
+#define SBC_SB_4               0x00
+#define SBC_SB_8               0x01
+
+/* Data endianness */
+#define SBC_LE                 0x00
+#define SBC_BE                 0x01
+
+struct sbc_struct {
+       unsigned long flags;
+
+       uint8_t frequency;
+       uint8_t blocks;
+       uint8_t subbands;
+       uint8_t mode;
+       uint8_t allocation;
+       uint8_t bitpool;
+       uint8_t endian;
+
+       void *priv;
+       void *priv_alloc_base;
+};
+
+typedef struct sbc_struct sbc_t;
+
+int sbc_init(sbc_t *sbc, unsigned long flags);
+int sbc_reinit(sbc_t *sbc, unsigned long flags);
+
+ssize_t sbc_parse(sbc_t *sbc, const void *input, size_t input_len);
+
+/* Decodes ONE input block into ONE output block */
+ssize_t sbc_decode(sbc_t *sbc, const void *input, size_t input_len,
+                       void *output, size_t output_len, size_t *written);
+
+/* Encodes ONE input block into ONE output block */
+ssize_t sbc_encode(sbc_t *sbc, const void *input, size_t input_len,
+                       void *output, size_t output_len, ssize_t *written);
+
+/* Returns the output block size in bytes */
+size_t sbc_get_frame_length(sbc_t *sbc);
+
+/* Returns the time one input/output block takes to play in msec*/
+unsigned sbc_get_frame_duration(sbc_t *sbc);
+
+/* Returns the input block size in bytes */
+size_t sbc_get_codesize(sbc_t *sbc);
+
+const char *sbc_get_implementation_info(sbc_t *sbc);
+void sbc_finish(sbc_t *sbc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SBC_H */
diff --git a/sbc/sbc_math.h b/sbc/sbc_math.h
new file mode 100644 (file)
index 0000000..5476860
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2008  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define fabs(x) ((x) < 0 ? -(x) : (x))
+/* C does not provide an explicit arithmetic shift right but this will
+   always be correct and every compiler *should* generate optimal code */
+#define ASR(val, bits) ((-2 >> 1 == -1) ? \
+                ((int32_t)(val)) >> (bits) : ((int32_t) (val)) / (1 << (bits)))
+
+#define SCALE_SPROTO4_TBL      12
+#define SCALE_SPROTO8_TBL      14
+#define SCALE_NPROTO4_TBL      11
+#define SCALE_NPROTO8_TBL      11
+#define SCALE4_STAGED1_BITS    15
+#define SCALE4_STAGED2_BITS    16
+#define SCALE8_STAGED1_BITS    15
+#define SCALE8_STAGED2_BITS    16
+
+typedef int32_t sbc_fixed_t;
+
+#define SCALE4_STAGED1(src) ASR(src, SCALE4_STAGED1_BITS)
+#define SCALE4_STAGED2(src) ASR(src, SCALE4_STAGED2_BITS)
+#define SCALE8_STAGED1(src) ASR(src, SCALE8_STAGED1_BITS)
+#define SCALE8_STAGED2(src) ASR(src, SCALE8_STAGED2_BITS)
+
+#define SBC_FIXED_0(val) { val = 0; }
+#define MUL(a, b)        ((a) * (b))
+#if defined(__arm__) && (!defined(__thumb__) || defined(__thumb2__))
+#define MULA(a, b, res) ({                             \
+               int tmp = res;                  \
+               __asm__(                                \
+                       "mla %0, %2, %3, %0"            \
+                       : "=&r" (tmp)                   \
+                       : "0" (tmp), "r" (a), "r" (b)); \
+               tmp; })
+#else
+#define MULA(a, b, res)  ((a) * (b) + (res))
+#endif
diff --git a/sbc/sbc_primitives.c b/sbc/sbc_primitives.c
new file mode 100644 (file)
index 0000000..ad780d0
--- /dev/null
@@ -0,0 +1,554 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives.h"
+#include "sbc_primitives_mmx.h"
+#include "sbc_primitives_iwmmxt.h"
+#include "sbc_primitives_neon.h"
+#include "sbc_primitives_armv6.h"
+
+/*
+ * A reference C code of analysis filter with SIMD-friendly tables
+ * reordering and code layout. This code can be used to develop platform
+ * specific SIMD optimizations. Also it may be used as some kind of test
+ * for compiler autovectorization capabilities (who knows, if the compiler
+ * is very good at this stuff, hand optimized assembly may be not strictly
+ * needed for some platform).
+ *
+ * Note: It is also possible to make a simple variant of analysis filter,
+ * which needs only a single constants table without taking care about
+ * even/odd cases. This simple variant of filter can be implemented without
+ * input data permutation. The only thing that would be lost is the
+ * possibility to use pairwise SIMD multiplications. But for some simple
+ * CPU cores without SIMD extensions it can be useful. If anybody is
+ * interested in implementing such variant of a filter, sourcecode from
+ * bluez versions 4.26/4.27 can be used as a reference and the history of
+ * the changes in git repository done around that time may be worth checking.
+ */
+
+static inline void sbc_analyze_four_simd(const int16_t *in, int32_t *out,
+                                                       const FIXED_T *consts)
+{
+       FIXED_A t1[4];
+       FIXED_T t2[4];
+       int hop = 0;
+
+       /* rounding coefficient */
+       t1[0] = t1[1] = t1[2] = t1[3] =
+               (FIXED_A) 1 << (SBC_PROTO_FIXED4_SCALE - 1);
+
+       /* low pass polyphase filter */
+       for (hop = 0; hop < 40; hop += 8) {
+               t1[0] += (FIXED_A) in[hop] * consts[hop];
+               t1[0] += (FIXED_A) in[hop + 1] * consts[hop + 1];
+               t1[1] += (FIXED_A) in[hop + 2] * consts[hop + 2];
+               t1[1] += (FIXED_A) in[hop + 3] * consts[hop + 3];
+               t1[2] += (FIXED_A) in[hop + 4] * consts[hop + 4];
+               t1[2] += (FIXED_A) in[hop + 5] * consts[hop + 5];
+               t1[3] += (FIXED_A) in[hop + 6] * consts[hop + 6];
+               t1[3] += (FIXED_A) in[hop + 7] * consts[hop + 7];
+       }
+
+       /* scaling */
+       t2[0] = t1[0] >> SBC_PROTO_FIXED4_SCALE;
+       t2[1] = t1[1] >> SBC_PROTO_FIXED4_SCALE;
+       t2[2] = t1[2] >> SBC_PROTO_FIXED4_SCALE;
+       t2[3] = t1[3] >> SBC_PROTO_FIXED4_SCALE;
+
+       /* do the cos transform */
+       t1[0]  = (FIXED_A) t2[0] * consts[40 + 0];
+       t1[0] += (FIXED_A) t2[1] * consts[40 + 1];
+       t1[1]  = (FIXED_A) t2[0] * consts[40 + 2];
+       t1[1] += (FIXED_A) t2[1] * consts[40 + 3];
+       t1[2]  = (FIXED_A) t2[0] * consts[40 + 4];
+       t1[2] += (FIXED_A) t2[1] * consts[40 + 5];
+       t1[3]  = (FIXED_A) t2[0] * consts[40 + 6];
+       t1[3] += (FIXED_A) t2[1] * consts[40 + 7];
+
+       t1[0] += (FIXED_A) t2[2] * consts[40 + 8];
+       t1[0] += (FIXED_A) t2[3] * consts[40 + 9];
+       t1[1] += (FIXED_A) t2[2] * consts[40 + 10];
+       t1[1] += (FIXED_A) t2[3] * consts[40 + 11];
+       t1[2] += (FIXED_A) t2[2] * consts[40 + 12];
+       t1[2] += (FIXED_A) t2[3] * consts[40 + 13];
+       t1[3] += (FIXED_A) t2[2] * consts[40 + 14];
+       t1[3] += (FIXED_A) t2[3] * consts[40 + 15];
+
+       out[0] = t1[0] >>
+               (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+       out[1] = t1[1] >>
+               (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+       out[2] = t1[2] >>
+               (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+       out[3] = t1[3] >>
+               (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+}
+
+static inline void sbc_analyze_eight_simd(const int16_t *in, int32_t *out,
+                                                       const FIXED_T *consts)
+{
+       FIXED_A t1[8];
+       FIXED_T t2[8];
+       int i, hop;
+
+       /* rounding coefficient */
+       t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] =
+               (FIXED_A) 1 << (SBC_PROTO_FIXED8_SCALE-1);
+
+       /* low pass polyphase filter */
+       for (hop = 0; hop < 80; hop += 16) {
+               t1[0] += (FIXED_A) in[hop] * consts[hop];
+               t1[0] += (FIXED_A) in[hop + 1] * consts[hop + 1];
+               t1[1] += (FIXED_A) in[hop + 2] * consts[hop + 2];
+               t1[1] += (FIXED_A) in[hop + 3] * consts[hop + 3];
+               t1[2] += (FIXED_A) in[hop + 4] * consts[hop + 4];
+               t1[2] += (FIXED_A) in[hop + 5] * consts[hop + 5];
+               t1[3] += (FIXED_A) in[hop + 6] * consts[hop + 6];
+               t1[3] += (FIXED_A) in[hop + 7] * consts[hop + 7];
+               t1[4] += (FIXED_A) in[hop + 8] * consts[hop + 8];
+               t1[4] += (FIXED_A) in[hop + 9] * consts[hop + 9];
+               t1[5] += (FIXED_A) in[hop + 10] * consts[hop + 10];
+               t1[5] += (FIXED_A) in[hop + 11] * consts[hop + 11];
+               t1[6] += (FIXED_A) in[hop + 12] * consts[hop + 12];
+               t1[6] += (FIXED_A) in[hop + 13] * consts[hop + 13];
+               t1[7] += (FIXED_A) in[hop + 14] * consts[hop + 14];
+               t1[7] += (FIXED_A) in[hop + 15] * consts[hop + 15];
+       }
+
+       /* scaling */
+       t2[0] = t1[0] >> SBC_PROTO_FIXED8_SCALE;
+       t2[1] = t1[1] >> SBC_PROTO_FIXED8_SCALE;
+       t2[2] = t1[2] >> SBC_PROTO_FIXED8_SCALE;
+       t2[3] = t1[3] >> SBC_PROTO_FIXED8_SCALE;
+       t2[4] = t1[4] >> SBC_PROTO_FIXED8_SCALE;
+       t2[5] = t1[5] >> SBC_PROTO_FIXED8_SCALE;
+       t2[6] = t1[6] >> SBC_PROTO_FIXED8_SCALE;
+       t2[7] = t1[7] >> SBC_PROTO_FIXED8_SCALE;
+
+
+       /* do the cos transform */
+       t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] = 0;
+
+       for (i = 0; i < 4; i++) {
+               t1[0] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 0];
+               t1[0] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 1];
+               t1[1] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 2];
+               t1[1] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 3];
+               t1[2] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 4];
+               t1[2] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 5];
+               t1[3] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 6];
+               t1[3] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 7];
+               t1[4] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 8];
+               t1[4] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 9];
+               t1[5] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 10];
+               t1[5] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 11];
+               t1[6] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 12];
+               t1[6] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 13];
+               t1[7] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 14];
+               t1[7] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 15];
+       }
+
+       for (i = 0; i < 8; i++)
+               out[i] = t1[i] >>
+                       (SBC_COS_TABLE_FIXED8_SCALE - SCALE_OUT_BITS);
+}
+
+static inline void sbc_analyze_4b_4s_simd(int16_t *x,
+                                               int32_t *out, int out_stride)
+{
+       /* Analyze blocks */
+       sbc_analyze_four_simd(x + 12, out, analysis_consts_fixed4_simd_odd);
+       out += out_stride;
+       sbc_analyze_four_simd(x + 8, out, analysis_consts_fixed4_simd_even);
+       out += out_stride;
+       sbc_analyze_four_simd(x + 4, out, analysis_consts_fixed4_simd_odd);
+       out += out_stride;
+       sbc_analyze_four_simd(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_simd(int16_t *x,
+                                         int32_t *out, int out_stride)
+{
+       /* Analyze blocks */
+       sbc_analyze_eight_simd(x + 24, out, analysis_consts_fixed8_simd_odd);
+       out += out_stride;
+       sbc_analyze_eight_simd(x + 16, out, analysis_consts_fixed8_simd_even);
+       out += out_stride;
+       sbc_analyze_eight_simd(x + 8, out, analysis_consts_fixed8_simd_odd);
+       out += out_stride;
+       sbc_analyze_eight_simd(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+static inline int16_t unaligned16_be(const uint8_t *ptr)
+{
+       return (int16_t) ((ptr[0] << 8) | ptr[1]);
+}
+
+static inline int16_t unaligned16_le(const uint8_t *ptr)
+{
+       return (int16_t) (ptr[0] | (ptr[1] << 8));
+}
+
+/*
+ * Internal helper functions for input data processing. In order to get
+ * optimal performance, it is important to have "nsamples", "nchannels"
+ * and "big_endian" arguments used with this inline function as compile
+ * time constants.
+ */
+
+static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s4_internal(
+       int position,
+       const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+       int nsamples, int nchannels, int big_endian)
+{
+       /* handle X buffer wraparound */
+       if (position < nsamples) {
+               if (nchannels > 0)
+                       memcpy(&X[0][SBC_X_BUFFER_SIZE - 40], &X[0][position],
+                                                       36 * sizeof(int16_t));
+               if (nchannels > 1)
+                       memcpy(&X[1][SBC_X_BUFFER_SIZE - 40], &X[1][position],
+                                                       36 * sizeof(int16_t));
+               position = SBC_X_BUFFER_SIZE - 40;
+       }
+
+       #define PCM(i) (big_endian ? \
+               unaligned16_be(pcm + (i) * 2) : unaligned16_le(pcm + (i) * 2))
+
+       /* copy/permutate audio samples */
+       while ((nsamples -= 8) >= 0) {
+               position -= 8;
+               if (nchannels > 0) {
+                       int16_t *x = &X[0][position];
+                       x[0]  = PCM(0 + 7 * nchannels);
+                       x[1]  = PCM(0 + 3 * nchannels);
+                       x[2]  = PCM(0 + 6 * nchannels);
+                       x[3]  = PCM(0 + 4 * nchannels);
+                       x[4]  = PCM(0 + 0 * nchannels);
+                       x[5]  = PCM(0 + 2 * nchannels);
+                       x[6]  = PCM(0 + 1 * nchannels);
+                       x[7]  = PCM(0 + 5 * nchannels);
+               }
+               if (nchannels > 1) {
+                       int16_t *x = &X[1][position];
+                       x[0]  = PCM(1 + 7 * nchannels);
+                       x[1]  = PCM(1 + 3 * nchannels);
+                       x[2]  = PCM(1 + 6 * nchannels);
+                       x[3]  = PCM(1 + 4 * nchannels);
+                       x[4]  = PCM(1 + 0 * nchannels);
+                       x[5]  = PCM(1 + 2 * nchannels);
+                       x[6]  = PCM(1 + 1 * nchannels);
+                       x[7]  = PCM(1 + 5 * nchannels);
+               }
+               pcm += 16 * nchannels;
+       }
+       #undef PCM
+
+       return position;
+}
+
+static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s8_internal(
+       int position,
+       const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+       int nsamples, int nchannels, int big_endian)
+{
+       /* handle X buffer wraparound */
+       if (position < nsamples) {
+               if (nchannels > 0)
+                       memcpy(&X[0][SBC_X_BUFFER_SIZE - 72], &X[0][position],
+                                                       72 * sizeof(int16_t));
+               if (nchannels > 1)
+                       memcpy(&X[1][SBC_X_BUFFER_SIZE - 72], &X[1][position],
+                                                       72 * sizeof(int16_t));
+               position = SBC_X_BUFFER_SIZE - 72;
+       }
+
+       #define PCM(i) (big_endian ? \
+               unaligned16_be(pcm + (i) * 2) : unaligned16_le(pcm + (i) * 2))
+
+       /* copy/permutate audio samples */
+       while ((nsamples -= 16) >= 0) {
+               position -= 16;
+               if (nchannels > 0) {
+                       int16_t *x = &X[0][position];
+                       x[0]  = PCM(0 + 15 * nchannels);
+                       x[1]  = PCM(0 + 7 * nchannels);
+                       x[2]  = PCM(0 + 14 * nchannels);
+                       x[3]  = PCM(0 + 8 * nchannels);
+                       x[4]  = PCM(0 + 13 * nchannels);
+                       x[5]  = PCM(0 + 9 * nchannels);
+                       x[6]  = PCM(0 + 12 * nchannels);
+                       x[7]  = PCM(0 + 10 * nchannels);
+                       x[8]  = PCM(0 + 11 * nchannels);
+                       x[9]  = PCM(0 + 3 * nchannels);
+                       x[10] = PCM(0 + 6 * nchannels);
+                       x[11] = PCM(0 + 0 * nchannels);
+                       x[12] = PCM(0 + 5 * nchannels);
+                       x[13] = PCM(0 + 1 * nchannels);
+                       x[14] = PCM(0 + 4 * nchannels);
+                       x[15] = PCM(0 + 2 * nchannels);
+               }
+               if (nchannels > 1) {
+                       int16_t *x = &X[1][position];
+                       x[0]  = PCM(1 + 15 * nchannels);
+                       x[1]  = PCM(1 + 7 * nchannels);
+                       x[2]  = PCM(1 + 14 * nchannels);
+                       x[3]  = PCM(1 + 8 * nchannels);
+                       x[4]  = PCM(1 + 13 * nchannels);
+                       x[5]  = PCM(1 + 9 * nchannels);
+                       x[6]  = PCM(1 + 12 * nchannels);
+                       x[7]  = PCM(1 + 10 * nchannels);
+                       x[8]  = PCM(1 + 11 * nchannels);
+                       x[9]  = PCM(1 + 3 * nchannels);
+                       x[10] = PCM(1 + 6 * nchannels);
+                       x[11] = PCM(1 + 0 * nchannels);
+                       x[12] = PCM(1 + 5 * nchannels);
+                       x[13] = PCM(1 + 1 * nchannels);
+                       x[14] = PCM(1 + 4 * nchannels);
+                       x[15] = PCM(1 + 2 * nchannels);
+               }
+               pcm += 32 * nchannels;
+       }
+       #undef PCM
+
+       return position;
+}
+
+/*
+ * Input data processing functions. The data is endian converted if needed,
+ * channels are deintrleaved and audio samples are reordered for use in
+ * SIMD-friendly analysis filter function. The results are put into "X"
+ * array, getting appended to the previous data (or it is better to say
+ * prepended, as the buffer is filled from top to bottom). Old data is
+ * discarded when neededed, but availability of (10 * nrof_subbands)
+ * contiguous samples is always guaranteed for the input to the analysis
+ * filter. This is achieved by copying a sufficient part of old data
+ * to the top of the buffer on buffer wraparound.
+ */
+
+static int sbc_enc_process_input_4s_le(int position,
+               const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+               int nsamples, int nchannels)
+{
+       if (nchannels > 1)
+               return sbc_encoder_process_input_s4_internal(
+                       position, pcm, X, nsamples, 2, 0);
+       else
+               return sbc_encoder_process_input_s4_internal(
+                       position, pcm, X, nsamples, 1, 0);
+}
+
+static int sbc_enc_process_input_4s_be(int position,
+               const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+               int nsamples, int nchannels)
+{
+       if (nchannels > 1)
+               return sbc_encoder_process_input_s4_internal(
+                       position, pcm, X, nsamples, 2, 1);
+       else
+               return sbc_encoder_process_input_s4_internal(
+                       position, pcm, X, nsamples, 1, 1);
+}
+
+static int sbc_enc_process_input_8s_le(int position,
+               const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+               int nsamples, int nchannels)
+{
+       if (nchannels > 1)
+               return sbc_encoder_process_input_s8_internal(
+                       position, pcm, X, nsamples, 2, 0);
+       else
+               return sbc_encoder_process_input_s8_internal(
+                       position, pcm, X, nsamples, 1, 0);
+}
+
+static int sbc_enc_process_input_8s_be(int position,
+               const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+               int nsamples, int nchannels)
+{
+       if (nchannels > 1)
+               return sbc_encoder_process_input_s8_internal(
+                       position, pcm, X, nsamples, 2, 1);
+       else
+               return sbc_encoder_process_input_s8_internal(
+                       position, pcm, X, nsamples, 1, 1);
+}
+
+/* Supplementary function to count the number of leading zeros */
+
+static inline int sbc_clz(uint32_t x)
+{
+#ifdef __GNUC__
+       return __builtin_clz(x);
+#else
+       /* TODO: this should be replaced with something better if good
+        * performance is wanted when using compilers other than gcc */
+       int cnt = 0;
+       while (x) {
+               cnt++;
+               x >>= 1;
+       }
+       return 32 - cnt;
+#endif
+}
+
+static void sbc_calc_scalefactors(
+       int32_t sb_sample_f[16][2][8],
+       uint32_t scale_factor[2][8],
+       int blocks, int channels, int subbands)
+{
+       int ch, sb, blk;
+       for (ch = 0; ch < channels; ch++) {
+               for (sb = 0; sb < subbands; sb++) {
+                       uint32_t x = 1 << SCALE_OUT_BITS;
+                       for (blk = 0; blk < blocks; blk++) {
+                               int32_t tmp = fabs(sb_sample_f[blk][ch][sb]);
+                               if (tmp != 0)
+                                       x |= tmp - 1;
+                       }
+                       scale_factor[ch][sb] = (31 - SCALE_OUT_BITS) -
+                               sbc_clz(x);
+               }
+       }
+}
+
+static int sbc_calc_scalefactors_j(
+       int32_t sb_sample_f[16][2][8],
+       uint32_t scale_factor[2][8],
+       int blocks, int subbands)
+{
+       int blk, joint = 0;
+       int32_t tmp0, tmp1;
+       uint32_t x, y;
+
+       /* last subband does not use joint stereo */
+       int sb = subbands - 1;
+       x = 1 << SCALE_OUT_BITS;
+       y = 1 << SCALE_OUT_BITS;
+       for (blk = 0; blk < blocks; blk++) {
+               tmp0 = fabs(sb_sample_f[blk][0][sb]);
+               tmp1 = fabs(sb_sample_f[blk][1][sb]);
+               if (tmp0 != 0)
+                       x |= tmp0 - 1;
+               if (tmp1 != 0)
+                       y |= tmp1 - 1;
+       }
+       scale_factor[0][sb] = (31 - SCALE_OUT_BITS) - sbc_clz(x);
+       scale_factor[1][sb] = (31 - SCALE_OUT_BITS) - sbc_clz(y);
+
+       /* the rest of subbands can use joint stereo */
+       while (--sb >= 0) {
+               int32_t sb_sample_j[16][2];
+               x = 1 << SCALE_OUT_BITS;
+               y = 1 << SCALE_OUT_BITS;
+               for (blk = 0; blk < blocks; blk++) {
+                       tmp0 = sb_sample_f[blk][0][sb];
+                       tmp1 = sb_sample_f[blk][1][sb];
+                       sb_sample_j[blk][0] = ASR(tmp0, 1) + ASR(tmp1, 1);
+                       sb_sample_j[blk][1] = ASR(tmp0, 1) - ASR(tmp1, 1);
+                       tmp0 = fabs(tmp0);
+                       tmp1 = fabs(tmp1);
+                       if (tmp0 != 0)
+                               x |= tmp0 - 1;
+                       if (tmp1 != 0)
+                               y |= tmp1 - 1;
+               }
+               scale_factor[0][sb] = (31 - SCALE_OUT_BITS) -
+                       sbc_clz(x);
+               scale_factor[1][sb] = (31 - SCALE_OUT_BITS) -
+                       sbc_clz(y);
+               x = 1 << SCALE_OUT_BITS;
+               y = 1 << SCALE_OUT_BITS;
+               for (blk = 0; blk < blocks; blk++) {
+                       tmp0 = fabs(sb_sample_j[blk][0]);
+                       tmp1 = fabs(sb_sample_j[blk][1]);
+                       if (tmp0 != 0)
+                               x |= tmp0 - 1;
+                       if (tmp1 != 0)
+                               y |= tmp1 - 1;
+               }
+               x = (31 - SCALE_OUT_BITS) - sbc_clz(x);
+               y = (31 - SCALE_OUT_BITS) - sbc_clz(y);
+
+               /* decide whether to use joint stereo for this subband */
+               if ((scale_factor[0][sb] + scale_factor[1][sb]) > x + y) {
+                       joint |= 1 << (subbands - 1 - sb);
+                       scale_factor[0][sb] = x;
+                       scale_factor[1][sb] = y;
+                       for (blk = 0; blk < blocks; blk++) {
+                               sb_sample_f[blk][0][sb] = sb_sample_j[blk][0];
+                               sb_sample_f[blk][1][sb] = sb_sample_j[blk][1];
+                       }
+               }
+       }
+
+       /* bitmask with the information about subbands using joint stereo */
+       return joint;
+}
+
+/*
+ * Detect CPU features and setup function pointers
+ */
+void sbc_init_primitives(struct sbc_encoder_state *state)
+{
+       /* Default implementation for analyze functions */
+       state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_simd;
+       state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_simd;
+
+       /* Default implementation for input reordering / deinterleaving */
+       state->sbc_enc_process_input_4s_le = sbc_enc_process_input_4s_le;
+       state->sbc_enc_process_input_4s_be = sbc_enc_process_input_4s_be;
+       state->sbc_enc_process_input_8s_le = sbc_enc_process_input_8s_le;
+       state->sbc_enc_process_input_8s_be = sbc_enc_process_input_8s_be;
+
+       /* Default implementation for scale factors calculation */
+       state->sbc_calc_scalefactors = sbc_calc_scalefactors;
+       state->sbc_calc_scalefactors_j = sbc_calc_scalefactors_j;
+       state->implementation_info = "Generic C";
+
+       /* X86/AMD64 optimizations */
+#ifdef SBC_BUILD_WITH_MMX_SUPPORT
+       sbc_init_primitives_mmx(state);
+#endif
+
+       /* ARM optimizations */
+#ifdef SBC_BUILD_WITH_ARMV6_SUPPORT
+       sbc_init_primitives_armv6(state);
+#endif
+#ifdef SBC_BUILD_WITH_IWMMXT_SUPPORT
+       sbc_init_primitives_iwmmxt(state);
+#endif
+#ifdef SBC_BUILD_WITH_NEON_SUPPORT
+       sbc_init_primitives_neon(state);
+#endif
+}
diff --git a/sbc/sbc_primitives.h b/sbc/sbc_primitives.h
new file mode 100644 (file)
index 0000000..17ad4f7
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_H
+#define __SBC_PRIMITIVES_H
+
+#define SCALE_OUT_BITS 15
+#define SBC_X_BUFFER_SIZE 328
+
+#ifdef __GNUC__
+#define SBC_ALWAYS_INLINE inline __attribute__((always_inline))
+#else
+#define SBC_ALWAYS_INLINE inline
+#endif
+
+struct sbc_encoder_state {
+       int position;
+       int16_t SBC_ALIGNED X[2][SBC_X_BUFFER_SIZE];
+       /* Polyphase analysis filter for 4 subbands configuration,
+        * it handles 4 blocks at once */
+       void (*sbc_analyze_4b_4s)(int16_t *x, int32_t *out, int out_stride);
+       /* Polyphase analysis filter for 8 subbands configuration,
+        * it handles 4 blocks at once */
+       void (*sbc_analyze_4b_8s)(int16_t *x, int32_t *out, int out_stride);
+       /* Process input data (deinterleave, endian conversion, reordering),
+        * depending on the number of subbands and input data byte order */
+       int (*sbc_enc_process_input_4s_le)(int position,
+                       const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+                       int nsamples, int nchannels);
+       int (*sbc_enc_process_input_4s_be)(int position,
+                       const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+                       int nsamples, int nchannels);
+       int (*sbc_enc_process_input_8s_le)(int position,
+                       const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+                       int nsamples, int nchannels);
+       int (*sbc_enc_process_input_8s_be)(int position,
+                       const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+                       int nsamples, int nchannels);
+       /* Scale factors calculation */
+       void (*sbc_calc_scalefactors)(int32_t sb_sample_f[16][2][8],
+                       uint32_t scale_factor[2][8],
+                       int blocks, int channels, int subbands);
+       /* Scale factors calculation with joint stereo support */
+       int (*sbc_calc_scalefactors_j)(int32_t sb_sample_f[16][2][8],
+                       uint32_t scale_factor[2][8],
+                       int blocks, int subbands);
+       const char *implementation_info;
+};
+
+/*
+ * Initialize pointers to the functions which are the basic "building bricks"
+ * of SBC codec. Best implementation is selected based on target CPU
+ * capabilities.
+ */
+void sbc_init_primitives(struct sbc_encoder_state *encoder_state);
+
+#endif
diff --git a/sbc/sbc_primitives_armv6.c b/sbc/sbc_primitives_armv6.c
new file mode 100644 (file)
index 0000000..b321272
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_armv6.h"
+
+/*
+ * ARMv6 optimizations. The instructions are scheduled for ARM11 pipeline.
+ */
+
+#ifdef SBC_BUILD_WITH_ARMV6_SUPPORT
+
+static void __attribute__((naked)) sbc_analyze_four_armv6()
+{
+       /* r0 = in, r1 = out, r2 = consts */
+       __asm__ volatile (
+               "push   {r1, r4-r7, lr}\n"
+               "push   {r8-r11}\n"
+               "ldrd   r4,  r5,  [r0, #0]\n"
+               "ldrd   r6,  r7,  [r2, #0]\n"
+               "ldrd   r8,  r9,  [r0, #16]\n"
+               "ldrd   r10, r11, [r2, #16]\n"
+               "mov    r14, #0x8000\n"
+               "smlad  r3,  r4,  r6,  r14\n"
+               "smlad  r12, r5,  r7,  r14\n"
+               "ldrd   r4,  r5,  [r0, #32]\n"
+               "ldrd   r6,  r7,  [r2, #32]\n"
+               "smlad  r3,  r8,  r10, r3\n"
+               "smlad  r12, r9,  r11, r12\n"
+               "ldrd   r8,  r9,  [r0, #48]\n"
+               "ldrd   r10, r11, [r2, #48]\n"
+               "smlad  r3,  r4,  r6,  r3\n"
+               "smlad  r12, r5,  r7,  r12\n"
+               "ldrd   r4,  r5,  [r0, #64]\n"
+               "ldrd   r6,  r7,  [r2, #64]\n"
+               "smlad  r3,  r8,  r10, r3\n"
+               "smlad  r12, r9,  r11, r12\n"
+               "ldrd   r8,  r9,  [r0, #8]\n"
+               "ldrd   r10, r11, [r2, #8]\n"
+               "smlad  r3,  r4,  r6,  r3\n"      /* t1[0] is done */
+               "smlad  r12, r5,  r7,  r12\n"     /* t1[1] is done */
+               "ldrd   r4,  r5,  [r0, #24]\n"
+               "ldrd   r6,  r7,  [r2, #24]\n"
+               "pkhtb  r3,  r12, r3, asr #16\n"  /* combine t1[0] and t1[1] */
+               "smlad  r12, r8,  r10, r14\n"
+               "smlad  r14, r9,  r11, r14\n"
+               "ldrd   r8,  r9,  [r0, #40]\n"
+               "ldrd   r10, r11, [r2, #40]\n"
+               "smlad  r12, r4,  r6,  r12\n"
+               "smlad  r14, r5,  r7,  r14\n"
+               "ldrd   r4,  r5,  [r0, #56]\n"
+               "ldrd   r6,  r7,  [r2, #56]\n"
+               "smlad  r12, r8,  r10, r12\n"
+               "smlad  r14, r9,  r11, r14\n"
+               "ldrd   r8,  r9,  [r0, #72]\n"
+               "ldrd   r10, r11, [r2, #72]\n"
+               "smlad  r12, r4,  r6,  r12\n"
+               "smlad  r14, r5,  r7,  r14\n"
+               "ldrd   r4,  r5,  [r2, #80]\n"    /* start loading cos table */
+               "smlad  r12, r8,  r10, r12\n"     /* t1[2] is done */
+               "smlad  r14, r9,  r11, r14\n"     /* t1[3] is done */
+               "ldrd   r6,  r7,  [r2, #88]\n"
+               "ldrd   r8,  r9,  [r2, #96]\n"
+               "ldrd   r10, r11, [r2, #104]\n"   /* cos table fully loaded */
+               "pkhtb  r12, r14, r12, asr #16\n" /* combine t1[2] and t1[3] */
+               "smuad  r4,  r3,  r4\n"
+               "smuad  r5,  r3,  r5\n"
+               "smlad  r4,  r12, r8,  r4\n"
+               "smlad  r5,  r12, r9,  r5\n"
+               "smuad  r6,  r3,  r6\n"
+               "smuad  r7,  r3,  r7\n"
+               "smlad  r6,  r12, r10, r6\n"
+               "smlad  r7,  r12, r11, r7\n"
+               "pop    {r8-r11}\n"
+               "stmia  r1, {r4, r5, r6, r7}\n"
+               "pop    {r1, r4-r7, pc}\n"
+       );
+}
+
+#define sbc_analyze_four(in, out, consts) \
+       ((void (*)(int16_t *, int32_t *, const FIXED_T*)) \
+               sbc_analyze_four_armv6)((in), (out), (consts))
+
+static void __attribute__((naked)) sbc_analyze_eight_armv6()
+{
+       /* r0 = in, r1 = out, r2 = consts */
+       __asm__ volatile (
+               "push   {r1, r4-r7, lr}\n"
+               "push   {r8-r11}\n"
+               "ldrd   r4,  r5,  [r0, #24]\n"
+               "ldrd   r6,  r7,  [r2, #24]\n"
+               "ldrd   r8,  r9,  [r0, #56]\n"
+               "ldrd   r10, r11, [r2, #56]\n"
+               "mov    r14, #0x8000\n"
+               "smlad  r3,  r4,  r6,  r14\n"
+               "smlad  r12, r5,  r7,  r14\n"
+               "ldrd   r4,  r5,  [r0, #88]\n"
+               "ldrd   r6,  r7,  [r2, #88]\n"
+               "smlad  r3,  r8,  r10, r3\n"
+               "smlad  r12, r9,  r11, r12\n"
+               "ldrd   r8,  r9,  [r0, #120]\n"
+               "ldrd   r10, r11, [r2, #120]\n"
+               "smlad  r3,  r4,  r6,  r3\n"
+               "smlad  r12, r5,  r7,  r12\n"
+               "ldrd   r4,  r5,  [r0, #152]\n"
+               "ldrd   r6,  r7,  [r2, #152]\n"
+               "smlad  r3,  r8,  r10, r3\n"
+               "smlad  r12, r9,  r11, r12\n"
+               "ldrd   r8,  r9,  [r0, #16]\n"
+               "ldrd   r10, r11, [r2, #16]\n"
+               "smlad  r3,  r4,  r6,  r3\n"      /* t1[6] is done */
+               "smlad  r12, r5,  r7,  r12\n"     /* t1[7] is done */
+               "ldrd   r4,  r5,  [r0, #48]\n"
+               "ldrd   r6,  r7,  [r2, #48]\n"
+               "pkhtb  r3,  r12, r3, asr #16\n"  /* combine t1[6] and t1[7] */
+               "str    r3,  [sp, #-4]!\n"        /* save to stack */
+               "smlad  r3,  r8,  r10, r14\n"
+               "smlad  r12, r9,  r11, r14\n"
+               "ldrd   r8,  r9,  [r0, #80]\n"
+               "ldrd   r10, r11, [r2, #80]\n"
+               "smlad  r3,  r4,  r6,  r3\n"
+               "smlad  r12, r5,  r7,  r12\n"
+               "ldrd   r4,  r5,  [r0, #112]\n"
+               "ldrd   r6,  r7,  [r2, #112]\n"
+               "smlad  r3,  r8,  r10, r3\n"
+               "smlad  r12, r9,  r11, r12\n"
+               "ldrd   r8,  r9,  [r0, #144]\n"
+               "ldrd   r10, r11, [r2, #144]\n"
+               "smlad  r3,  r4,  r6,  r3\n"
+               "smlad  r12, r5,  r7,  r12\n"
+               "ldrd   r4,  r5,  [r0, #0]\n"
+               "ldrd   r6,  r7,  [r2, #0]\n"
+               "smlad  r3,  r8,  r10, r3\n"      /* t1[4] is done */
+               "smlad  r12, r9,  r11, r12\n"     /* t1[5] is done */
+               "ldrd   r8,  r9,  [r0, #32]\n"
+               "ldrd   r10, r11, [r2, #32]\n"
+               "pkhtb  r3,  r12, r3, asr #16\n"  /* combine t1[4] and t1[5] */
+               "str    r3,  [sp, #-4]!\n"        /* save to stack */
+               "smlad  r3,  r4,  r6,  r14\n"
+               "smlad  r12, r5,  r7,  r14\n"
+               "ldrd   r4,  r5,  [r0, #64]\n"
+               "ldrd   r6,  r7,  [r2, #64]\n"
+               "smlad  r3,  r8,  r10, r3\n"
+               "smlad  r12, r9,  r11, r12\n"
+               "ldrd   r8,  r9,  [r0, #96]\n"
+               "ldrd   r10, r11, [r2, #96]\n"
+               "smlad  r3,  r4,  r6,  r3\n"
+               "smlad  r12, r5,  r7,  r12\n"
+               "ldrd   r4,  r5,  [r0, #128]\n"
+               "ldrd   r6,  r7,  [r2, #128]\n"
+               "smlad  r3,  r8,  r10, r3\n"
+               "smlad  r12, r9,  r11, r12\n"
+               "ldrd   r8,  r9,  [r0, #8]\n"
+               "ldrd   r10, r11, [r2, #8]\n"
+               "smlad  r3,  r4,  r6,  r3\n"      /* t1[0] is done */
+               "smlad  r12, r5,  r7,  r12\n"     /* t1[1] is done */
+               "ldrd   r4,  r5,  [r0, #40]\n"
+               "ldrd   r6,  r7,  [r2, #40]\n"
+               "pkhtb  r3,  r12, r3, asr #16\n"  /* combine t1[0] and t1[1] */
+               "smlad  r12, r8,  r10, r14\n"
+               "smlad  r14, r9,  r11, r14\n"
+               "ldrd   r8,  r9,  [r0, #72]\n"
+               "ldrd   r10, r11, [r2, #72]\n"
+               "smlad  r12, r4,  r6,  r12\n"
+               "smlad  r14, r5,  r7,  r14\n"
+               "ldrd   r4,  r5,  [r0, #104]\n"
+               "ldrd   r6,  r7,  [r2, #104]\n"
+               "smlad  r12, r8,  r10, r12\n"
+               "smlad  r14, r9,  r11, r14\n"
+               "ldrd   r8,  r9,  [r0, #136]\n"
+               "ldrd   r10, r11, [r2, #136]!\n"
+               "smlad  r12, r4,  r6,  r12\n"
+               "smlad  r14, r5,  r7,  r14\n"
+               "ldrd   r4,  r5,  [r2, #(160 - 136 + 0)]\n"
+               "smlad  r12, r8,  r10, r12\n"     /* t1[2] is done */
+               "smlad  r14, r9,  r11, r14\n"     /* t1[3] is done */
+               "ldrd   r6,  r7,  [r2, #(160 - 136 + 8)]\n"
+               "smuad  r4,  r3,  r4\n"
+               "smuad  r5,  r3,  r5\n"
+               "pkhtb  r12, r14, r12, asr #16\n" /* combine t1[2] and t1[3] */
+                                                 /* r3  = t2[0:1] */
+                                                 /* r12 = t2[2:3] */
+               "pop    {r0, r14}\n"              /* t2[4:5], t2[6:7] */
+               "ldrd   r8,  r9,  [r2, #(160 - 136 + 32)]\n"
+               "smuad  r6,  r3,  r6\n"
+               "smuad  r7,  r3,  r7\n"
+               "ldrd   r10, r11, [r2, #(160 - 136 + 40)]\n"
+               "smlad  r4,  r12, r8,  r4\n"
+               "smlad  r5,  r12, r9,  r5\n"
+               "ldrd   r8,  r9,  [r2, #(160 - 136 + 64)]\n"
+               "smlad  r6,  r12, r10, r6\n"
+               "smlad  r7,  r12, r11, r7\n"
+               "ldrd   r10, r11, [r2, #(160 - 136 + 72)]\n"
+               "smlad  r4,  r0,  r8,  r4\n"
+               "smlad  r5,  r0,  r9,  r5\n"
+               "ldrd   r8,  r9,  [r2, #(160 - 136 + 96)]\n"
+               "smlad  r6,  r0,  r10, r6\n"
+               "smlad  r7,  r0,  r11, r7\n"
+               "ldrd   r10, r11, [r2, #(160 - 136 + 104)]\n"
+               "smlad  r4,  r14, r8,  r4\n"
+               "smlad  r5,  r14, r9,  r5\n"
+               "ldrd   r8,  r9,  [r2, #(160 - 136 + 16 + 0)]\n"
+               "smlad  r6,  r14, r10, r6\n"
+               "smlad  r7,  r14, r11, r7\n"
+               "ldrd   r10, r11, [r2, #(160 - 136 + 16 + 8)]\n"
+               "stmia  r1!, {r4, r5}\n"
+               "smuad  r4,  r3,  r8\n"
+               "smuad  r5,  r3,  r9\n"
+               "ldrd   r8,  r9,  [r2, #(160 - 136 + 16 + 32)]\n"
+               "stmia  r1!, {r6, r7}\n"
+               "smuad  r6,  r3,  r10\n"
+               "smuad  r7,  r3,  r11\n"
+               "ldrd   r10, r11, [r2, #(160 - 136 + 16 + 40)]\n"
+               "smlad  r4,  r12, r8,  r4\n"
+               "smlad  r5,  r12, r9,  r5\n"
+               "ldrd   r8,  r9,  [r2, #(160 - 136 + 16 + 64)]\n"
+               "smlad  r6,  r12, r10, r6\n"
+               "smlad  r7,  r12, r11, r7\n"
+               "ldrd   r10, r11, [r2, #(160 - 136 + 16 + 72)]\n"
+               "smlad  r4,  r0,  r8,  r4\n"
+               "smlad  r5,  r0,  r9,  r5\n"
+               "ldrd   r8,  r9,  [r2, #(160 - 136 + 16 + 96)]\n"
+               "smlad  r6,  r0,  r10, r6\n"
+               "smlad  r7,  r0,  r11, r7\n"
+               "ldrd   r10, r11, [r2, #(160 - 136 + 16 + 104)]\n"
+               "smlad  r4,  r14, r8,  r4\n"
+               "smlad  r5,  r14, r9,  r5\n"
+               "smlad  r6,  r14, r10, r6\n"
+               "smlad  r7,  r14, r11, r7\n"
+               "pop    {r8-r11}\n"
+               "stmia  r1!, {r4, r5, r6, r7}\n"
+               "pop    {r1, r4-r7, pc}\n"
+       );
+}
+
+#define sbc_analyze_eight(in, out, consts) \
+       ((void (*)(int16_t *, int32_t *, const FIXED_T*)) \
+               sbc_analyze_eight_armv6)((in), (out), (consts))
+
+static void sbc_analyze_4b_4s_armv6(int16_t *x, int32_t *out, int out_stride)
+{
+       /* Analyze blocks */
+       sbc_analyze_four(x + 12, out, analysis_consts_fixed4_simd_odd);
+       out += out_stride;
+       sbc_analyze_four(x + 8, out, analysis_consts_fixed4_simd_even);
+       out += out_stride;
+       sbc_analyze_four(x + 4, out, analysis_consts_fixed4_simd_odd);
+       out += out_stride;
+       sbc_analyze_four(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static void sbc_analyze_4b_8s_armv6(int16_t *x, int32_t *out, int out_stride)
+{
+       /* Analyze blocks */
+       sbc_analyze_eight(x + 24, out, analysis_consts_fixed8_simd_odd);
+       out += out_stride;
+       sbc_analyze_eight(x + 16, out, analysis_consts_fixed8_simd_even);
+       out += out_stride;
+       sbc_analyze_eight(x + 8, out, analysis_consts_fixed8_simd_odd);
+       out += out_stride;
+       sbc_analyze_eight(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+void sbc_init_primitives_armv6(struct sbc_encoder_state *state)
+{
+       state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_armv6;
+       state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_armv6;
+       state->implementation_info = "ARMv6 SIMD";
+}
+
+#endif
diff --git a/sbc/sbc_primitives_armv6.h b/sbc/sbc_primitives_armv6.h
new file mode 100644 (file)
index 0000000..6a9efe5
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_ARMV6_H
+#define __SBC_PRIMITIVES_ARMV6_H
+
+#include "sbc_primitives.h"
+
+#if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \
+       defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \
+       defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || \
+       defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_7__) || \
+       defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || \
+       defined(__ARM_ARCH_7M__)
+#define SBC_HAVE_ARMV6 1
+#endif
+
+#if !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15) && \
+       defined(__GNUC__) && defined(SBC_HAVE_ARMV6) && \
+       defined(__ARM_EABI__) && !defined(__ARM_NEON__) && \
+       (!defined(__thumb__) || defined(__thumb2__))
+
+#define SBC_BUILD_WITH_ARMV6_SUPPORT
+
+void sbc_init_primitives_armv6(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/sbc/sbc_primitives_iwmmxt.c b/sbc/sbc_primitives_iwmmxt.c
new file mode 100644 (file)
index 0000000..e0bd060
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2010 Keith Mok <ek9852@gmail.com>
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_iwmmxt.h"
+
+/*
+ * IWMMXT optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_IWMMXT_SUPPORT
+
+static inline void sbc_analyze_four_iwmmxt(const int16_t *in, int32_t *out,
+                                       const FIXED_T *consts)
+{
+       __asm__ volatile (
+               "wldrd        wr0, [%0]\n"
+               "tbcstw       wr4, %2\n"
+               "wldrd        wr2, [%1]\n"
+               "wldrd        wr1, [%0, #8]\n"
+               "wldrd        wr3, [%1, #8]\n"
+               "wmadds       wr0, wr2, wr0\n"
+               " wldrd       wr6, [%0, #16]\n"
+               "wmadds       wr1, wr3, wr1\n"
+               " wldrd       wr7, [%0, #24]\n"
+               "waddwss      wr0, wr0, wr4\n"
+               " wldrd       wr8, [%1, #16]\n"
+               "waddwss      wr1, wr1, wr4\n"
+               " wldrd       wr9, [%1, #24]\n"
+               " wmadds      wr6, wr8, wr6\n"
+               "  wldrd      wr2, [%0, #32]\n"
+               " wmadds      wr7, wr9, wr7\n"
+               "  wldrd      wr3, [%0, #40]\n"
+               " waddwss     wr0, wr6, wr0\n"
+               "  wldrd      wr4, [%1, #32]\n"
+               " waddwss     wr1, wr7, wr1\n"
+               "  wldrd      wr5, [%1, #40]\n"
+               "  wmadds     wr2, wr4, wr2\n"
+               "wldrd        wr6, [%0, #48]\n"
+               "  wmadds     wr3, wr5, wr3\n"
+               "wldrd        wr7, [%0, #56]\n"
+               "  waddwss    wr0, wr2, wr0\n"
+               "wldrd        wr8, [%1, #48]\n"
+               "  waddwss    wr1, wr3, wr1\n"
+               "wldrd        wr9, [%1, #56]\n"
+               "wmadds       wr6, wr8, wr6\n"
+               " wldrd       wr2, [%0, #64]\n"
+               "wmadds       wr7, wr9, wr7\n"
+               " wldrd       wr3, [%0, #72]\n"
+               "waddwss      wr0, wr6, wr0\n"
+               " wldrd       wr4, [%1, #64]\n"
+               "waddwss      wr1, wr7, wr1\n"
+               " wldrd       wr5, [%1, #72]\n"
+               " wmadds      wr2, wr4, wr2\n"
+               "tmcr       wcgr0, %4\n"
+               " wmadds      wr3, wr5, wr3\n"
+               " waddwss     wr0, wr2, wr0\n"
+               " waddwss     wr1, wr3, wr1\n"
+               "\n"
+               "wsrawg       wr0, wr0, wcgr0\n"
+               " wldrd       wr4, [%1, #80]\n"
+               "wsrawg       wr1, wr1, wcgr0\n"
+               " wldrd       wr5, [%1, #88]\n"
+               "wpackwss     wr0, wr0, wr0\n"
+               " wldrd       wr6, [%1, #96]\n"
+               "wpackwss     wr1, wr1, wr1\n"
+               "wmadds       wr2, wr5, wr0\n"
+               " wldrd       wr7, [%1, #104]\n"
+               "wmadds       wr0, wr4, wr0\n"
+               "\n"
+               " wmadds      wr3, wr7, wr1\n"
+               " wmadds      wr1, wr6, wr1\n"
+               " waddwss     wr2, wr3, wr2\n"
+               " waddwss     wr0, wr1, wr0\n"
+               "\n"
+               "wstrd        wr0, [%3]\n"
+               "wstrd        wr2, [%3, #8]\n"
+               :
+               : "r" (in), "r" (consts),
+                       "r" (1 << (SBC_PROTO_FIXED4_SCALE - 1)), "r" (out),
+                       "r" (SBC_PROTO_FIXED4_SCALE)
+               : "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7",
+                 "wr8", "wr9", "wcgr0", "memory");
+}
+
+static inline void sbc_analyze_eight_iwmmxt(const int16_t *in, int32_t *out,
+                                                       const FIXED_T *consts)
+{
+       __asm__ volatile (
+               "wldrd        wr0, [%0]\n"
+               "tbcstw       wr15, %2\n"
+               "wldrd        wr1, [%0, #8]\n"
+               "wldrd        wr2, [%0, #16]\n"
+               "wldrd        wr3, [%0, #24]\n"
+               "wldrd        wr4, [%1]\n"
+               "wldrd        wr5, [%1, #8]\n"
+               "wldrd        wr6, [%1, #16]\n"
+               "wldrd        wr7, [%1, #24]\n"
+               "wmadds       wr0, wr0, wr4\n"
+               " wldrd       wr8, [%1, #32]\n"
+               "wmadds       wr1, wr1, wr5\n"
+               " wldrd       wr9, [%1, #40]\n"
+               "wmadds       wr2, wr2, wr6\n"
+               " wldrd      wr10, [%1, #48]\n"
+               "wmadds       wr3, wr3, wr7\n"
+               " wldrd      wr11, [%1, #56]\n"
+               "waddwss      wr0, wr0, wr15\n"
+               " wldrd       wr4, [%0, #32]\n"
+               "waddwss      wr1, wr1, wr15\n"
+               " wldrd       wr5, [%0, #40]\n"
+               "waddwss      wr2, wr2, wr15\n"
+               " wldrd       wr6, [%0, #48]\n"
+               "waddwss      wr3, wr3, wr15\n"
+               " wldrd       wr7, [%0, #56]\n"
+               " wmadds      wr4, wr4, wr8\n"
+               "  wldrd     wr12, [%0, #64]\n"
+               " wmadds      wr5, wr5, wr9\n"
+               "  wldrd     wr13, [%0, #72]\n"
+               " wmadds      wr6, wr6, wr10\n"
+               "  wldrd     wr14, [%0, #80]\n"
+               " wmadds      wr7, wr7, wr11\n"
+               "  wldrd     wr15, [%0, #88]\n"
+               " waddwss     wr0, wr4, wr0\n"
+               "  wldrd      wr8, [%1, #64]\n"
+               " waddwss     wr1, wr5, wr1\n"
+               "  wldrd      wr9, [%1, #72]\n"
+               " waddwss     wr2, wr6, wr2\n"
+               "  wldrd     wr10, [%1, #80]\n"
+               " waddwss     wr3, wr7, wr3\n"
+               "  wldrd     wr11, [%1, #88]\n"
+               "  wmadds    wr12, wr12, wr8\n"
+               "wldrd        wr4, [%0, #96]\n"
+               "  wmadds    wr13, wr13, wr9\n"
+               "wldrd        wr5, [%0, #104]\n"
+               "  wmadds    wr14, wr14, wr10\n"
+               "wldrd        wr6, [%0, #112]\n"
+               "  wmadds    wr15, wr15, wr11\n"
+               "wldrd        wr7, [%0, #120]\n"
+               "  waddwss    wr0, wr12, wr0\n"
+               "wldrd        wr8, [%1, #96]\n"
+               "  waddwss    wr1, wr13, wr1\n"
+               "wldrd        wr9, [%1, #104]\n"
+               "  waddwss    wr2, wr14, wr2\n"
+               "wldrd       wr10, [%1, #112]\n"
+               "  waddwss    wr3, wr15, wr3\n"
+               "wldrd       wr11, [%1, #120]\n"
+               "wmadds       wr4, wr4, wr8\n"
+               " wldrd      wr12, [%0, #128]\n"
+               "wmadds       wr5, wr5, wr9\n"
+               " wldrd      wr13, [%0, #136]\n"
+               "wmadds       wr6, wr6, wr10\n"
+               " wldrd      wr14, [%0, #144]\n"
+               "wmadds       wr7, wr7, wr11\n"
+               " wldrd      wr15, [%0, #152]\n"
+               "waddwss      wr0, wr4, wr0\n"
+               " wldrd       wr8, [%1, #128]\n"
+               "waddwss      wr1, wr5, wr1\n"
+               " wldrd       wr9, [%1, #136]\n"
+               "waddwss      wr2, wr6, wr2\n"
+               " wldrd      wr10, [%1, #144]\n"
+               " waddwss     wr3, wr7, wr3\n"
+               " wldrd     wr11, [%1, #152]\n"
+               " wmadds     wr12, wr12, wr8\n"
+               "tmcr       wcgr0, %4\n"
+               " wmadds     wr13, wr13, wr9\n"
+               " wmadds     wr14, wr14, wr10\n"
+               " wmadds     wr15, wr15, wr11\n"
+               " waddwss     wr0, wr12, wr0\n"
+               " waddwss     wr1, wr13, wr1\n"
+               " waddwss     wr2, wr14, wr2\n"
+               " waddwss     wr3, wr15, wr3\n"
+               "\n"
+               "wsrawg       wr0, wr0, wcgr0\n"
+               "wsrawg       wr1, wr1, wcgr0\n"
+               "wsrawg       wr2, wr2, wcgr0\n"
+               "wsrawg       wr3, wr3, wcgr0\n"
+               "\n"
+               "wpackwss     wr0, wr0, wr0\n"
+               "wpackwss     wr1, wr1, wr1\n"
+               " wldrd       wr4, [%1, #160]\n"
+               "wpackwss     wr2, wr2, wr2\n"
+               " wldrd       wr5, [%1, #168]\n"
+               "wpackwss     wr3, wr3, wr3\n"
+               "  wldrd      wr6, [%1, #192]\n"
+               " wmadds      wr4, wr4, wr0\n"
+               "  wldrd      wr7, [%1, #200]\n"
+               " wmadds      wr5, wr5, wr0\n"
+               "   wldrd     wr8, [%1, #224]\n"
+               "  wmadds     wr6, wr6, wr1\n"
+               "   wldrd     wr9, [%1, #232]\n"
+               "  wmadds     wr7, wr7, wr1\n"
+               "  waddwss    wr4, wr6, wr4\n"
+               "  waddwss    wr5, wr7, wr5\n"
+               "   wmadds    wr8, wr8, wr2\n"
+               "wldrd        wr6, [%1, #256]\n"
+               "   wmadds    wr9, wr9, wr2\n"
+               "wldrd        wr7, [%1, #264]\n"
+               "waddwss      wr4, wr8, wr4\n"
+               "   waddwss   wr5, wr9, wr5\n"
+               "wmadds       wr6, wr6, wr3\n"
+               "wmadds       wr7, wr7, wr3\n"
+               "waddwss      wr4, wr6, wr4\n"
+               "waddwss      wr5, wr7, wr5\n"
+               "\n"
+               "wstrd        wr4, [%3]\n"
+               "wstrd        wr5, [%3, #8]\n"
+               "\n"
+               "wldrd        wr6, [%1, #176]\n"
+               "wldrd        wr5, [%1, #184]\n"
+               "wmadds       wr5, wr5, wr0\n"
+               "wldrd        wr8, [%1, #208]\n"
+               "wmadds       wr0, wr6, wr0\n"
+               "wldrd        wr9, [%1, #216]\n"
+               "wmadds       wr9, wr9, wr1\n"
+               "wldrd        wr6, [%1, #240]\n"
+               "wmadds       wr1, wr8, wr1\n"
+               "wldrd        wr7, [%1, #248]\n"
+               "waddwss      wr0, wr1, wr0\n"
+               "waddwss      wr5, wr9, wr5\n"
+               "wmadds       wr7, wr7, wr2\n"
+               "wldrd        wr8, [%1, #272]\n"
+               "wmadds       wr2, wr6, wr2\n"
+               "wldrd        wr9, [%1, #280]\n"
+               "waddwss      wr0, wr2, wr0\n"
+               "waddwss      wr5, wr7, wr5\n"
+               "wmadds       wr9, wr9, wr3\n"
+               "wmadds       wr3, wr8, wr3\n"
+               "waddwss      wr0, wr3, wr0\n"
+               "waddwss      wr5, wr9, wr5\n"
+               "\n"
+               "wstrd        wr0, [%3, #16]\n"
+               "wstrd        wr5, [%3, #24]\n"
+               :
+               : "r" (in), "r" (consts),
+                       "r" (1 << (SBC_PROTO_FIXED8_SCALE - 1)), "r" (out),
+                       "r" (SBC_PROTO_FIXED8_SCALE)
+               : "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7",
+                 "wr8", "wr9", "wr10", "wr11", "wr12", "wr13", "wr14", "wr15",
+                 "wcgr0", "memory");
+}
+
+static inline void sbc_analyze_4b_4s_iwmmxt(int16_t *x, int32_t *out,
+                                               int out_stride)
+{
+       /* Analyze blocks */
+       sbc_analyze_four_iwmmxt(x + 12, out, analysis_consts_fixed4_simd_odd);
+       out += out_stride;
+       sbc_analyze_four_iwmmxt(x + 8, out, analysis_consts_fixed4_simd_even);
+       out += out_stride;
+       sbc_analyze_four_iwmmxt(x + 4, out, analysis_consts_fixed4_simd_odd);
+       out += out_stride;
+       sbc_analyze_four_iwmmxt(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_iwmmxt(int16_t *x, int32_t *out,
+                                               int out_stride)
+{
+       /* Analyze blocks */
+       sbc_analyze_eight_iwmmxt(x + 24, out, analysis_consts_fixed8_simd_odd);
+       out += out_stride;
+       sbc_analyze_eight_iwmmxt(x + 16, out, analysis_consts_fixed8_simd_even);
+       out += out_stride;
+       sbc_analyze_eight_iwmmxt(x + 8, out, analysis_consts_fixed8_simd_odd);
+       out += out_stride;
+       sbc_analyze_eight_iwmmxt(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+void sbc_init_primitives_iwmmxt(struct sbc_encoder_state *state)
+{
+       state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_iwmmxt;
+       state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_iwmmxt;
+       state->implementation_info = "IWMMXT";
+}
+
+#endif
diff --git a/sbc/sbc_primitives_iwmmxt.h b/sbc/sbc_primitives_iwmmxt.h
new file mode 100644 (file)
index 0000000..b535e68
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2010 Keith Mok <ek9852@gmail.com>
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_IWMMXT_H
+#define __SBC_PRIMITIVES_IWMMXT_H
+
+#include "sbc_primitives.h"
+
+#if defined(__GNUC__) && defined(__IWMMXT__) && \
+               !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15)
+
+#define SBC_BUILD_WITH_IWMMXT_SUPPORT
+
+void sbc_init_primitives_iwmmxt(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/sbc/sbc_primitives_mmx.c b/sbc/sbc_primitives_mmx.c
new file mode 100644 (file)
index 0000000..27e9a56
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_mmx.h"
+
+/*
+ * MMX optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_MMX_SUPPORT
+
+static inline void sbc_analyze_four_mmx(const int16_t *in, int32_t *out,
+                                       const FIXED_T *consts)
+{
+       static const SBC_ALIGNED int32_t round_c[2] = {
+               1 << (SBC_PROTO_FIXED4_SCALE - 1),
+               1 << (SBC_PROTO_FIXED4_SCALE - 1),
+       };
+       __asm__ volatile (
+               "movq        (%0), %%mm0\n"
+               "movq       8(%0), %%mm1\n"
+               "pmaddwd     (%1), %%mm0\n"
+               "pmaddwd    8(%1), %%mm1\n"
+               "paddd       (%2), %%mm0\n"
+               "paddd       (%2), %%mm1\n"
+               "\n"
+               "movq      16(%0), %%mm2\n"
+               "movq      24(%0), %%mm3\n"
+               "pmaddwd   16(%1), %%mm2\n"
+               "pmaddwd   24(%1), %%mm3\n"
+               "paddd      %%mm2, %%mm0\n"
+               "paddd      %%mm3, %%mm1\n"
+               "\n"
+               "movq      32(%0), %%mm2\n"
+               "movq      40(%0), %%mm3\n"
+               "pmaddwd   32(%1), %%mm2\n"
+               "pmaddwd   40(%1), %%mm3\n"
+               "paddd      %%mm2, %%mm0\n"
+               "paddd      %%mm3, %%mm1\n"
+               "\n"
+               "movq      48(%0), %%mm2\n"
+               "movq      56(%0), %%mm3\n"
+               "pmaddwd   48(%1), %%mm2\n"
+               "pmaddwd   56(%1), %%mm3\n"
+               "paddd      %%mm2, %%mm0\n"
+               "paddd      %%mm3, %%mm1\n"
+               "\n"
+               "movq      64(%0), %%mm2\n"
+               "movq      72(%0), %%mm3\n"
+               "pmaddwd   64(%1), %%mm2\n"
+               "pmaddwd   72(%1), %%mm3\n"
+               "paddd      %%mm2, %%mm0\n"
+               "paddd      %%mm3, %%mm1\n"
+               "\n"
+               "psrad         %4, %%mm0\n"
+               "psrad         %4, %%mm1\n"
+               "packssdw   %%mm0, %%mm0\n"
+               "packssdw   %%mm1, %%mm1\n"
+               "\n"
+               "movq       %%mm0, %%mm2\n"
+               "pmaddwd   80(%1), %%mm0\n"
+               "pmaddwd   88(%1), %%mm2\n"
+               "\n"
+               "movq       %%mm1, %%mm3\n"
+               "pmaddwd   96(%1), %%mm1\n"
+               "pmaddwd  104(%1), %%mm3\n"
+               "paddd      %%mm1, %%mm0\n"
+               "paddd      %%mm3, %%mm2\n"
+               "\n"
+               "movq       %%mm0, (%3)\n"
+               "movq       %%mm2, 8(%3)\n"
+               :
+               : "r" (in), "r" (consts), "r" (&round_c), "r" (out),
+                       "i" (SBC_PROTO_FIXED4_SCALE)
+               : "cc", "memory");
+}
+
+static inline void sbc_analyze_eight_mmx(const int16_t *in, int32_t *out,
+                                                       const FIXED_T *consts)
+{
+       static const SBC_ALIGNED int32_t round_c[2] = {
+               1 << (SBC_PROTO_FIXED8_SCALE - 1),
+               1 << (SBC_PROTO_FIXED8_SCALE - 1),
+       };
+       __asm__ volatile (
+               "movq        (%0), %%mm0\n"
+               "movq       8(%0), %%mm1\n"
+               "movq      16(%0), %%mm2\n"
+               "movq      24(%0), %%mm3\n"
+               "pmaddwd     (%1), %%mm0\n"
+               "pmaddwd    8(%1), %%mm1\n"
+               "pmaddwd   16(%1), %%mm2\n"
+               "pmaddwd   24(%1), %%mm3\n"
+               "paddd       (%2), %%mm0\n"
+               "paddd       (%2), %%mm1\n"
+               "paddd       (%2), %%mm2\n"
+               "paddd       (%2), %%mm3\n"
+               "\n"
+               "movq      32(%0), %%mm4\n"
+               "movq      40(%0), %%mm5\n"
+               "movq      48(%0), %%mm6\n"
+               "movq      56(%0), %%mm7\n"
+               "pmaddwd   32(%1), %%mm4\n"
+               "pmaddwd   40(%1), %%mm5\n"
+               "pmaddwd   48(%1), %%mm6\n"
+               "pmaddwd   56(%1), %%mm7\n"
+               "paddd      %%mm4, %%mm0\n"
+               "paddd      %%mm5, %%mm1\n"
+               "paddd      %%mm6, %%mm2\n"
+               "paddd      %%mm7, %%mm3\n"
+               "\n"
+               "movq      64(%0), %%mm4\n"
+               "movq      72(%0), %%mm5\n"
+               "movq      80(%0), %%mm6\n"
+               "movq      88(%0), %%mm7\n"
+               "pmaddwd   64(%1), %%mm4\n"
+               "pmaddwd   72(%1), %%mm5\n"
+               "pmaddwd   80(%1), %%mm6\n"
+               "pmaddwd   88(%1), %%mm7\n"
+               "paddd      %%mm4, %%mm0\n"
+               "paddd      %%mm5, %%mm1\n"
+               "paddd      %%mm6, %%mm2\n"
+               "paddd      %%mm7, %%mm3\n"
+               "\n"
+               "movq      96(%0), %%mm4\n"
+               "movq     104(%0), %%mm5\n"
+               "movq     112(%0), %%mm6\n"
+               "movq     120(%0), %%mm7\n"
+               "pmaddwd   96(%1), %%mm4\n"
+               "pmaddwd  104(%1), %%mm5\n"
+               "pmaddwd  112(%1), %%mm6\n"
+               "pmaddwd  120(%1), %%mm7\n"
+               "paddd      %%mm4, %%mm0\n"
+               "paddd      %%mm5, %%mm1\n"
+               "paddd      %%mm6, %%mm2\n"
+               "paddd      %%mm7, %%mm3\n"
+               "\n"
+               "movq     128(%0), %%mm4\n"
+               "movq     136(%0), %%mm5\n"
+               "movq     144(%0), %%mm6\n"
+               "movq     152(%0), %%mm7\n"
+               "pmaddwd  128(%1), %%mm4\n"
+               "pmaddwd  136(%1), %%mm5\n"
+               "pmaddwd  144(%1), %%mm6\n"
+               "pmaddwd  152(%1), %%mm7\n"
+               "paddd      %%mm4, %%mm0\n"
+               "paddd      %%mm5, %%mm1\n"
+               "paddd      %%mm6, %%mm2\n"
+               "paddd      %%mm7, %%mm3\n"
+               "\n"
+               "psrad         %4, %%mm0\n"
+               "psrad         %4, %%mm1\n"
+               "psrad         %4, %%mm2\n"
+               "psrad         %4, %%mm3\n"
+               "\n"
+               "packssdw   %%mm0, %%mm0\n"
+               "packssdw   %%mm1, %%mm1\n"
+               "packssdw   %%mm2, %%mm2\n"
+               "packssdw   %%mm3, %%mm3\n"
+               "\n"
+               "movq       %%mm0, %%mm4\n"
+               "movq       %%mm0, %%mm5\n"
+               "pmaddwd  160(%1), %%mm4\n"
+               "pmaddwd  168(%1), %%mm5\n"
+               "\n"
+               "movq       %%mm1, %%mm6\n"
+               "movq       %%mm1, %%mm7\n"
+               "pmaddwd  192(%1), %%mm6\n"
+               "pmaddwd  200(%1), %%mm7\n"
+               "paddd      %%mm6, %%mm4\n"
+               "paddd      %%mm7, %%mm5\n"
+               "\n"
+               "movq       %%mm2, %%mm6\n"
+               "movq       %%mm2, %%mm7\n"
+               "pmaddwd  224(%1), %%mm6\n"
+               "pmaddwd  232(%1), %%mm7\n"
+               "paddd      %%mm6, %%mm4\n"
+               "paddd      %%mm7, %%mm5\n"
+               "\n"
+               "movq       %%mm3, %%mm6\n"
+               "movq       %%mm3, %%mm7\n"
+               "pmaddwd  256(%1), %%mm6\n"
+               "pmaddwd  264(%1), %%mm7\n"
+               "paddd      %%mm6, %%mm4\n"
+               "paddd      %%mm7, %%mm5\n"
+               "\n"
+               "movq       %%mm4, (%3)\n"
+               "movq       %%mm5, 8(%3)\n"
+               "\n"
+               "movq       %%mm0, %%mm5\n"
+               "pmaddwd  176(%1), %%mm0\n"
+               "pmaddwd  184(%1), %%mm5\n"
+               "\n"
+               "movq       %%mm1, %%mm7\n"
+               "pmaddwd  208(%1), %%mm1\n"
+               "pmaddwd  216(%1), %%mm7\n"
+               "paddd      %%mm1, %%mm0\n"
+               "paddd      %%mm7, %%mm5\n"
+               "\n"
+               "movq       %%mm2, %%mm7\n"
+               "pmaddwd  240(%1), %%mm2\n"
+               "pmaddwd  248(%1), %%mm7\n"
+               "paddd      %%mm2, %%mm0\n"
+               "paddd      %%mm7, %%mm5\n"
+               "\n"
+               "movq       %%mm3, %%mm7\n"
+               "pmaddwd  272(%1), %%mm3\n"
+               "pmaddwd  280(%1), %%mm7\n"
+               "paddd      %%mm3, %%mm0\n"
+               "paddd      %%mm7, %%mm5\n"
+               "\n"
+               "movq       %%mm0, 16(%3)\n"
+               "movq       %%mm5, 24(%3)\n"
+               :
+               : "r" (in), "r" (consts), "r" (&round_c), "r" (out),
+                       "i" (SBC_PROTO_FIXED8_SCALE)
+               : "cc", "memory");
+}
+
+static inline void sbc_analyze_4b_4s_mmx(int16_t *x, int32_t *out,
+                                               int out_stride)
+{
+       /* Analyze blocks */
+       sbc_analyze_four_mmx(x + 12, out, analysis_consts_fixed4_simd_odd);
+       out += out_stride;
+       sbc_analyze_four_mmx(x + 8, out, analysis_consts_fixed4_simd_even);
+       out += out_stride;
+       sbc_analyze_four_mmx(x + 4, out, analysis_consts_fixed4_simd_odd);
+       out += out_stride;
+       sbc_analyze_four_mmx(x + 0, out, analysis_consts_fixed4_simd_even);
+
+       __asm__ volatile ("emms\n");
+}
+
+static inline void sbc_analyze_4b_8s_mmx(int16_t *x, int32_t *out,
+                                               int out_stride)
+{
+       /* Analyze blocks */
+       sbc_analyze_eight_mmx(x + 24, out, analysis_consts_fixed8_simd_odd);
+       out += out_stride;
+       sbc_analyze_eight_mmx(x + 16, out, analysis_consts_fixed8_simd_even);
+       out += out_stride;
+       sbc_analyze_eight_mmx(x + 8, out, analysis_consts_fixed8_simd_odd);
+       out += out_stride;
+       sbc_analyze_eight_mmx(x + 0, out, analysis_consts_fixed8_simd_even);
+
+       __asm__ volatile ("emms\n");
+}
+
+static void sbc_calc_scalefactors_mmx(
+       int32_t sb_sample_f[16][2][8],
+       uint32_t scale_factor[2][8],
+       int blocks, int channels, int subbands)
+{
+       static const SBC_ALIGNED int32_t consts[2] = {
+               1 << SCALE_OUT_BITS,
+               1 << SCALE_OUT_BITS,
+       };
+       int ch, sb;
+       intptr_t blk;
+       for (ch = 0; ch < channels; ch++) {
+               for (sb = 0; sb < subbands; sb += 2) {
+                       blk = (blocks - 1) * (((char *) &sb_sample_f[1][0][0] -
+                               (char *) &sb_sample_f[0][0][0]));
+                       __asm__ volatile (
+                               "movq         (%4), %%mm0\n"
+                       "1:\n"
+                               "movq     (%1, %0), %%mm1\n"
+                               "pxor        %%mm2, %%mm2\n"
+                               "pcmpgtd     %%mm2, %%mm1\n"
+                               "paddd    (%1, %0), %%mm1\n"
+                               "pcmpgtd     %%mm1, %%mm2\n"
+                               "pxor        %%mm2, %%mm1\n"
+
+                               "por         %%mm1, %%mm0\n"
+
+                               "sub            %2, %0\n"
+                               "jns            1b\n"
+
+                               "movd        %%mm0, %k0\n"
+                               "psrlq         $32, %%mm0\n"
+                               "bsrl          %k0, %k0\n"
+                               "subl           %5, %k0\n"
+                               "movl          %k0, (%3)\n"
+
+                               "movd        %%mm0, %k0\n"
+                               "bsrl          %k0, %k0\n"
+                               "subl           %5, %k0\n"
+                               "movl          %k0, 4(%3)\n"
+                       : "+r" (blk)
+                       : "r" (&sb_sample_f[0][ch][sb]),
+                               "i" ((char *) &sb_sample_f[1][0][0] -
+                                       (char *) &sb_sample_f[0][0][0]),
+                               "r" (&scale_factor[ch][sb]),
+                               "r" (&consts),
+                               "i" (SCALE_OUT_BITS)
+                       : "cc", "memory");
+               }
+       }
+       __asm__ volatile ("emms\n");
+}
+
+static int check_mmx_support(void)
+{
+#ifdef __amd64__
+       return 1; /* We assume that all 64-bit processors have MMX support */
+#else
+       int cpuid_feature_information;
+       __asm__ volatile (
+               /* According to Intel manual, CPUID instruction is supported
+                * if the value of ID bit (bit 21) in EFLAGS can be modified */
+               "pushf\n"
+               "movl     (%%esp),   %0\n"
+               "xorl     $0x200000, (%%esp)\n" /* try to modify ID bit */
+               "popf\n"
+               "pushf\n"
+               "xorl     (%%esp),   %0\n"      /* check if ID bit changed */
+               "jz       1f\n"
+               "push     %%eax\n"
+               "push     %%ebx\n"
+               "push     %%ecx\n"
+               "mov      $1,        %%eax\n"
+               "cpuid\n"
+               "pop      %%ecx\n"
+               "pop      %%ebx\n"
+               "pop      %%eax\n"
+               "1:\n"
+               "popf\n"
+               : "=d" (cpuid_feature_information)
+               :
+               : "cc");
+    return cpuid_feature_information & (1 << 23);
+#endif
+}
+
+void sbc_init_primitives_mmx(struct sbc_encoder_state *state)
+{
+       if (check_mmx_support()) {
+               state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_mmx;
+               state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_mmx;
+               state->sbc_calc_scalefactors = sbc_calc_scalefactors_mmx;
+               state->implementation_info = "MMX";
+       }
+}
+
+#endif
diff --git a/sbc/sbc_primitives_mmx.h b/sbc/sbc_primitives_mmx.h
new file mode 100644 (file)
index 0000000..e0e728b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_MMX_H
+#define __SBC_PRIMITIVES_MMX_H
+
+#include "sbc_primitives.h"
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__)) && \
+               !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15)
+
+#define SBC_BUILD_WITH_MMX_SUPPORT
+
+void sbc_init_primitives_mmx(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/sbc/sbc_primitives_neon.c b/sbc/sbc_primitives_neon.c
new file mode 100644 (file)
index 0000000..5d4d0e3
--- /dev/null
@@ -0,0 +1,893 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_neon.h"
+
+/*
+ * ARM NEON optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_NEON_SUPPORT
+
+static inline void _sbc_analyze_four_neon(const int16_t *in, int32_t *out,
+                                                       const FIXED_T *consts)
+{
+       /* TODO: merge even and odd cases (or even merge all four calls to this
+        * function) in order to have only aligned reads from 'in' array
+        * and reduce number of load instructions */
+       __asm__ volatile (
+               "vld1.16    {d4, d5}, [%0, :64]!\n"
+               "vld1.16    {d8, d9}, [%1, :128]!\n"
+
+               "vmull.s16  q0, d4, d8\n"
+               "vld1.16    {d6,  d7}, [%0, :64]!\n"
+               "vmull.s16  q1, d5, d9\n"
+               "vld1.16    {d10, d11}, [%1, :128]!\n"
+
+               "vmlal.s16  q0, d6, d10\n"
+               "vld1.16    {d4, d5}, [%0, :64]!\n"
+               "vmlal.s16  q1, d7, d11\n"
+               "vld1.16    {d8, d9}, [%1, :128]!\n"
+
+               "vmlal.s16  q0, d4, d8\n"
+               "vld1.16    {d6,  d7}, [%0, :64]!\n"
+               "vmlal.s16  q1, d5, d9\n"
+               "vld1.16    {d10, d11}, [%1, :128]!\n"
+
+               "vmlal.s16  q0, d6, d10\n"
+               "vld1.16    {d4, d5}, [%0, :64]!\n"
+               "vmlal.s16  q1, d7, d11\n"
+               "vld1.16    {d8, d9}, [%1, :128]!\n"
+
+               "vmlal.s16  q0, d4, d8\n"
+               "vmlal.s16  q1, d5, d9\n"
+
+               "vpadd.s32  d0, d0, d1\n"
+               "vpadd.s32  d1, d2, d3\n"
+
+               "vrshrn.s32 d0, q0, %3\n"
+
+               "vld1.16    {d2, d3, d4, d5}, [%1, :128]!\n"
+
+               "vdup.i32   d1, d0[1]\n"  /* TODO: can be eliminated */
+               "vdup.i32   d0, d0[0]\n"  /* TODO: can be eliminated */
+
+               "vmull.s16  q3, d2, d0\n"
+               "vmull.s16  q4, d3, d0\n"
+               "vmlal.s16  q3, d4, d1\n"
+               "vmlal.s16  q4, d5, d1\n"
+
+               "vpadd.s32  d0, d6, d7\n" /* TODO: can be eliminated */
+               "vpadd.s32  d1, d8, d9\n" /* TODO: can be eliminated */
+
+               "vst1.32    {d0, d1}, [%2, :128]\n"
+               : "+r" (in), "+r" (consts)
+               : "r" (out),
+                       "i" (SBC_PROTO_FIXED4_SCALE)
+               : "memory",
+                       "d0", "d1", "d2", "d3", "d4", "d5",
+                       "d6", "d7", "d8", "d9", "d10", "d11");
+}
+
+static inline void _sbc_analyze_eight_neon(const int16_t *in, int32_t *out,
+                                                       const FIXED_T *consts)
+{
+       /* TODO: merge even and odd cases (or even merge all four calls to this
+        * function) in order to have only aligned reads from 'in' array
+        * and reduce number of load instructions */
+       __asm__ volatile (
+               "vld1.16    {d4, d5}, [%0, :64]!\n"
+               "vld1.16    {d8, d9}, [%1, :128]!\n"
+
+               "vmull.s16  q6, d4, d8\n"
+               "vld1.16    {d6,  d7}, [%0, :64]!\n"
+               "vmull.s16  q7, d5, d9\n"
+               "vld1.16    {d10, d11}, [%1, :128]!\n"
+               "vmull.s16  q8, d6, d10\n"
+               "vld1.16    {d4, d5}, [%0, :64]!\n"
+               "vmull.s16  q9, d7, d11\n"
+               "vld1.16    {d8, d9}, [%1, :128]!\n"
+
+               "vmlal.s16  q6, d4, d8\n"
+               "vld1.16    {d6,  d7}, [%0, :64]!\n"
+               "vmlal.s16  q7, d5, d9\n"
+               "vld1.16    {d10, d11}, [%1, :128]!\n"
+               "vmlal.s16  q8, d6, d10\n"
+               "vld1.16    {d4, d5}, [%0, :64]!\n"
+               "vmlal.s16  q9, d7, d11\n"
+               "vld1.16    {d8, d9}, [%1, :128]!\n"
+
+               "vmlal.s16  q6, d4, d8\n"
+               "vld1.16    {d6,  d7}, [%0, :64]!\n"
+               "vmlal.s16  q7, d5, d9\n"
+               "vld1.16    {d10, d11}, [%1, :128]!\n"
+               "vmlal.s16  q8, d6, d10\n"
+               "vld1.16    {d4, d5}, [%0, :64]!\n"
+               "vmlal.s16  q9, d7, d11\n"
+               "vld1.16    {d8, d9}, [%1, :128]!\n"
+
+               "vmlal.s16  q6, d4, d8\n"
+               "vld1.16    {d6,  d7}, [%0, :64]!\n"
+               "vmlal.s16  q7, d5, d9\n"
+               "vld1.16    {d10, d11}, [%1, :128]!\n"
+               "vmlal.s16  q8, d6, d10\n"
+               "vld1.16    {d4, d5}, [%0, :64]!\n"
+               "vmlal.s16  q9, d7, d11\n"
+               "vld1.16    {d8, d9}, [%1, :128]!\n"
+
+               "vmlal.s16  q6, d4, d8\n"
+               "vld1.16    {d6,  d7}, [%0, :64]!\n"
+               "vmlal.s16  q7, d5, d9\n"
+               "vld1.16    {d10, d11}, [%1, :128]!\n"
+
+               "vmlal.s16  q8, d6, d10\n"
+               "vmlal.s16  q9, d7, d11\n"
+
+               "vpadd.s32  d0, d12, d13\n"
+               "vpadd.s32  d1, d14, d15\n"
+               "vpadd.s32  d2, d16, d17\n"
+               "vpadd.s32  d3, d18, d19\n"
+
+               "vrshr.s32 q0, q0, %3\n"
+               "vrshr.s32 q1, q1, %3\n"
+               "vmovn.s32 d0, q0\n"
+               "vmovn.s32 d1, q1\n"
+
+               "vdup.i32   d3, d1[1]\n"  /* TODO: can be eliminated */
+               "vdup.i32   d2, d1[0]\n"  /* TODO: can be eliminated */
+               "vdup.i32   d1, d0[1]\n"  /* TODO: can be eliminated */
+               "vdup.i32   d0, d0[0]\n"  /* TODO: can be eliminated */
+
+               "vld1.16    {d4, d5}, [%1, :128]!\n"
+               "vmull.s16  q6, d4, d0\n"
+               "vld1.16    {d6, d7}, [%1, :128]!\n"
+               "vmull.s16  q7, d5, d0\n"
+               "vmull.s16  q8, d6, d0\n"
+               "vmull.s16  q9, d7, d0\n"
+
+               "vld1.16    {d4, d5}, [%1, :128]!\n"
+               "vmlal.s16  q6, d4, d1\n"
+               "vld1.16    {d6, d7}, [%1, :128]!\n"
+               "vmlal.s16  q7, d5, d1\n"
+               "vmlal.s16  q8, d6, d1\n"
+               "vmlal.s16  q9, d7, d1\n"
+
+               "vld1.16    {d4, d5}, [%1, :128]!\n"
+               "vmlal.s16  q6, d4, d2\n"
+               "vld1.16    {d6, d7}, [%1, :128]!\n"
+               "vmlal.s16  q7, d5, d2\n"
+               "vmlal.s16  q8, d6, d2\n"
+               "vmlal.s16  q9, d7, d2\n"
+
+               "vld1.16    {d4, d5}, [%1, :128]!\n"
+               "vmlal.s16  q6, d4, d3\n"
+               "vld1.16    {d6, d7}, [%1, :128]!\n"
+               "vmlal.s16  q7, d5, d3\n"
+               "vmlal.s16  q8, d6, d3\n"
+               "vmlal.s16  q9, d7, d3\n"
+
+               "vpadd.s32  d0, d12, d13\n" /* TODO: can be eliminated */
+               "vpadd.s32  d1, d14, d15\n" /* TODO: can be eliminated */
+               "vpadd.s32  d2, d16, d17\n" /* TODO: can be eliminated */
+               "vpadd.s32  d3, d18, d19\n" /* TODO: can be eliminated */
+
+               "vst1.32    {d0, d1, d2, d3}, [%2, :128]\n"
+               : "+r" (in), "+r" (consts)
+               : "r" (out),
+                       "i" (SBC_PROTO_FIXED8_SCALE)
+               : "memory",
+                       "d0", "d1", "d2", "d3", "d4", "d5",
+                       "d6", "d7", "d8", "d9", "d10", "d11",
+                       "d12", "d13", "d14", "d15", "d16", "d17",
+                       "d18", "d19");
+}
+
+static inline void sbc_analyze_4b_4s_neon(int16_t *x,
+                                               int32_t *out, int out_stride)
+{
+       /* Analyze blocks */
+       _sbc_analyze_four_neon(x + 12, out, analysis_consts_fixed4_simd_odd);
+       out += out_stride;
+       _sbc_analyze_four_neon(x + 8, out, analysis_consts_fixed4_simd_even);
+       out += out_stride;
+       _sbc_analyze_four_neon(x + 4, out, analysis_consts_fixed4_simd_odd);
+       out += out_stride;
+       _sbc_analyze_four_neon(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_neon(int16_t *x,
+                                               int32_t *out, int out_stride)
+{
+       /* Analyze blocks */
+       _sbc_analyze_eight_neon(x + 24, out, analysis_consts_fixed8_simd_odd);
+       out += out_stride;
+       _sbc_analyze_eight_neon(x + 16, out, analysis_consts_fixed8_simd_even);
+       out += out_stride;
+       _sbc_analyze_eight_neon(x + 8, out, analysis_consts_fixed8_simd_odd);
+       out += out_stride;
+       _sbc_analyze_eight_neon(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+static void sbc_calc_scalefactors_neon(
+       int32_t sb_sample_f[16][2][8],
+       uint32_t scale_factor[2][8],
+       int blocks, int channels, int subbands)
+{
+       int ch, sb;
+       for (ch = 0; ch < channels; ch++) {
+               for (sb = 0; sb < subbands; sb += 4) {
+                       int blk = blocks;
+                       int32_t *in = &sb_sample_f[0][ch][sb];
+                       __asm__ volatile (
+                               "vmov.s32  q0, #0\n"
+                               "vmov.s32  q1, %[c1]\n"
+                               "vmov.s32  q14, #1\n"
+                               "vmov.s32  q15, %[c2]\n"
+                               "vadd.s32  q1, q1, q14\n"
+                       "1:\n"
+                               "vld1.32   {d16, d17}, [%[in], :128], %[inc]\n"
+                               "vabs.s32  q8,  q8\n"
+                               "vld1.32   {d18, d19}, [%[in], :128], %[inc]\n"
+                               "vabs.s32  q9,  q9\n"
+                               "vld1.32   {d20, d21}, [%[in], :128], %[inc]\n"
+                               "vabs.s32  q10, q10\n"
+                               "vld1.32   {d22, d23}, [%[in], :128], %[inc]\n"
+                               "vabs.s32  q11, q11\n"
+                               "vmax.s32  q0,  q0,  q8\n"
+                               "vmax.s32  q1,  q1,  q9\n"
+                               "vmax.s32  q0,  q0,  q10\n"
+                               "vmax.s32  q1,  q1,  q11\n"
+                               "subs      %[blk], %[blk], #4\n"
+                               "bgt       1b\n"
+                               "vmax.s32  q0,  q0,  q1\n"
+                               "vsub.s32  q0,  q0,  q14\n"
+                               "vclz.s32  q0,  q0\n"
+                               "vsub.s32  q0,  q15, q0\n"
+                               "vst1.32   {d0, d1}, [%[out], :128]\n"
+                       :
+                         [blk]    "+r" (blk),
+                         [in]     "+r" (in)
+                       :
+                         [inc]     "r" ((char *) &sb_sample_f[1][0][0] -
+                                        (char *) &sb_sample_f[0][0][0]),
+                         [out]     "r" (&scale_factor[ch][sb]),
+                         [c1]      "i" (1 << SCALE_OUT_BITS),
+                         [c2]      "i" (31 - SCALE_OUT_BITS)
+                       : "d0", "d1", "d2", "d3", "d16", "d17", "d18", "d19",
+                         "d20", "d21", "d22", "d23", "d24", "d25", "d26",
+                         "d27", "d28", "d29", "d30", "d31", "cc", "memory");
+               }
+       }
+}
+
+int sbc_calc_scalefactors_j_neon(
+       int32_t sb_sample_f[16][2][8],
+       uint32_t scale_factor[2][8],
+       int blocks, int subbands)
+{
+       static SBC_ALIGNED int32_t joint_bits_mask[8] = {
+               8,   4,  2,  1, 128, 64, 32, 16
+       };
+       int joint, i;
+       int32_t  *in0, *in1;
+       int32_t  *in = &sb_sample_f[0][0][0];
+       uint32_t *out0, *out1;
+       uint32_t *out = &scale_factor[0][0];
+       int32_t  *consts = joint_bits_mask;
+
+       i = subbands;
+
+       __asm__ volatile (
+               /*
+                * constants: q13 = (31 - SCALE_OUT_BITS), q14 = 1
+                * input:     q0  = ((1 << SCALE_OUT_BITS) + 1)
+                *            %[in0] - samples for channel 0
+                *            %[in1] - samples for shannel 1
+                * output:    q0, q1 - scale factors without joint stereo
+                *            q2, q3 - scale factors with joint stereo
+                *            q15    - joint stereo selection mask
+                */
+               ".macro calc_scalefactors\n"
+                       "vmov.s32  q1, q0\n"
+                       "vmov.s32  q2, q0\n"
+                       "vmov.s32  q3, q0\n"
+                       "mov       %[i], %[blocks]\n"
+               "1:\n"
+                       "vld1.32   {d18, d19}, [%[in1], :128], %[inc]\n"
+                       "vbic.s32  q11, q9,  q14\n"
+                       "vld1.32   {d16, d17}, [%[in0], :128], %[inc]\n"
+                       "vhadd.s32 q10, q8,  q11\n"
+                       "vhsub.s32 q11, q8,  q11\n"
+                       "vabs.s32  q8,  q8\n"
+                       "vabs.s32  q9,  q9\n"
+                       "vabs.s32  q10, q10\n"
+                       "vabs.s32  q11, q11\n"
+                       "vmax.s32  q0,  q0,  q8\n"
+                       "vmax.s32  q1,  q1,  q9\n"
+                       "vmax.s32  q2,  q2,  q10\n"
+                       "vmax.s32  q3,  q3,  q11\n"
+                       "subs      %[i], %[i], #1\n"
+                       "bgt       1b\n"
+                       "vsub.s32  q0,  q0,  q14\n"
+                       "vsub.s32  q1,  q1,  q14\n"
+                       "vsub.s32  q2,  q2,  q14\n"
+                       "vsub.s32  q3,  q3,  q14\n"
+                       "vclz.s32  q0,  q0\n"
+                       "vclz.s32  q1,  q1\n"
+                       "vclz.s32  q2,  q2\n"
+                       "vclz.s32  q3,  q3\n"
+                       "vsub.s32  q0,  q13, q0\n"
+                       "vsub.s32  q1,  q13, q1\n"
+                       "vsub.s32  q2,  q13, q2\n"
+                       "vsub.s32  q3,  q13, q3\n"
+               ".endm\n"
+               /*
+                * constants: q14 = 1
+                * input: q15    - joint stereo selection mask
+                *        %[in0] - value set by calc_scalefactors macro
+                *        %[in1] - value set by calc_scalefactors macro
+                */
+               ".macro update_joint_stereo_samples\n"
+                       "sub       %[out1], %[in1], %[inc]\n"
+                       "sub       %[out0], %[in0], %[inc]\n"
+                       "sub       %[in1], %[in1], %[inc], asl #1\n"
+                       "sub       %[in0], %[in0], %[inc], asl #1\n"
+                       "vld1.32   {d18, d19}, [%[in1], :128]\n"
+                       "vbic.s32  q11, q9,  q14\n"
+                       "vld1.32   {d16, d17}, [%[in0], :128]\n"
+                       "vld1.32   {d2, d3}, [%[out1], :128]\n"
+                       "vbic.s32  q3,  q1,  q14\n"
+                       "vld1.32   {d0, d1}, [%[out0], :128]\n"
+                       "vhsub.s32 q10, q8,  q11\n"
+                       "vhadd.s32 q11, q8,  q11\n"
+                       "vhsub.s32 q2,  q0,  q3\n"
+                       "vhadd.s32 q3,  q0,  q3\n"
+                       "vbif.s32  q10, q9,  q15\n"
+                       "vbif.s32  d22, d16, d30\n"
+                       "sub       %[inc], %[zero], %[inc], asl #1\n"
+                       "sub       %[i], %[blocks], #2\n"
+               "2:\n"
+                       "vbif.s32  d23, d17, d31\n"
+                       "vst1.32   {d20, d21}, [%[in1], :128], %[inc]\n"
+                       "vbif.s32  d4,  d2,  d30\n"
+                       "vld1.32   {d18, d19}, [%[in1], :128]\n"
+                       "vbif.s32  d5,  d3,  d31\n"
+                       "vst1.32   {d22, d23}, [%[in0], :128], %[inc]\n"
+                       "vbif.s32  d6,  d0,  d30\n"
+                       "vld1.32   {d16, d17}, [%[in0], :128]\n"
+                       "vbif.s32  d7,  d1,  d31\n"
+                       "vst1.32   {d4, d5}, [%[out1], :128], %[inc]\n"
+                       "vbic.s32  q11, q9,  q14\n"
+                       "vld1.32   {d2, d3}, [%[out1], :128]\n"
+                       "vst1.32   {d6, d7}, [%[out0], :128], %[inc]\n"
+                       "vbic.s32  q3,  q1,  q14\n"
+                       "vld1.32   {d0, d1}, [%[out0], :128]\n"
+                       "vhsub.s32 q10, q8,  q11\n"
+                       "vhadd.s32 q11, q8,  q11\n"
+                       "vhsub.s32 q2,  q0,  q3\n"
+                       "vhadd.s32 q3,  q0,  q3\n"
+                       "vbif.s32  q10, q9,  q15\n"
+                       "vbif.s32  d22, d16, d30\n"
+                       "subs      %[i], %[i], #2\n"
+                       "bgt       2b\n"
+                       "sub       %[inc], %[zero], %[inc], asr #1\n"
+                       "vbif.s32  d23, d17, d31\n"
+                       "vst1.32   {d20, d21}, [%[in1], :128]\n"
+                       "vbif.s32  q2,  q1,  q15\n"
+                       "vst1.32   {d22, d23}, [%[in0], :128]\n"
+                       "vbif.s32  q3,  q0,  q15\n"
+                       "vst1.32   {d4, d5}, [%[out1], :128]\n"
+                       "vst1.32   {d6, d7}, [%[out0], :128]\n"
+               ".endm\n"
+
+               "vmov.s32  q14, #1\n"
+               "vmov.s32  q13, %[c2]\n"
+
+               "cmp   %[i], #4\n"
+               "bne   8f\n"
+
+       "4:\n" /* 4 subbands */
+               "add   %[in0], %[in], #0\n"
+               "add   %[in1], %[in], #32\n"
+               "add   %[out0], %[out], #0\n"
+               "add   %[out1], %[out], #32\n"
+               "vmov.s32  q0, %[c1]\n"
+               "vadd.s32  q0, q0, q14\n"
+
+               "calc_scalefactors\n"
+
+               /* check whether to use joint stereo for subbands 0, 1, 2 */
+               "vadd.s32  q15, q0,  q1\n"
+               "vadd.s32  q9,  q2,  q3\n"
+               "vmov.s32  d31[1], %[zero]\n" /* last subband -> no joint */
+               "vld1.32   {d16, d17}, [%[consts], :128]!\n"
+               "vcgt.s32  q15, q15, q9\n"
+
+               /* calculate and save to memory 'joint' variable */
+               /* update and save scale factors to memory */
+               "  vand.s32  q8, q8, q15\n"
+               "vbit.s32  q0,  q2,  q15\n"
+               "  vpadd.s32 d16, d16, d17\n"
+               "vbit.s32  q1,  q3,  q15\n"
+               "  vpadd.s32 d16, d16, d16\n"
+               "vst1.32   {d0, d1}, [%[out0], :128]\n"
+               "vst1.32   {d2, d3}, [%[out1], :128]\n"
+               "  vst1.32   {d16[0]}, [%[joint]]\n"
+
+               "update_joint_stereo_samples\n"
+               "b     9f\n"
+
+       "8:\n" /* 8 subbands */
+               "add   %[in0], %[in], #16\n\n"
+               "add   %[in1], %[in], #48\n"
+               "add   %[out0], %[out], #16\n\n"
+               "add   %[out1], %[out], #48\n"
+               "vmov.s32  q0, %[c1]\n"
+               "vadd.s32  q0, q0, q14\n"
+
+               "calc_scalefactors\n"
+
+               /* check whether to use joint stereo for subbands 4, 5, 6 */
+               "vadd.s32  q15, q0,  q1\n"
+               "vadd.s32  q9,  q2,  q3\n"
+               "vmov.s32  d31[1], %[zero]\n"  /* last subband -> no joint */
+               "vld1.32   {d16, d17}, [%[consts], :128]!\n"
+               "vcgt.s32  q15, q15, q9\n"
+
+               /* calculate part of 'joint' variable and save it to d24 */
+               /* update and save scale factors to memory */
+               "  vand.s32  q8, q8, q15\n"
+               "vbit.s32  q0,  q2,  q15\n"
+               "  vpadd.s32 d16, d16, d17\n"
+               "vbit.s32  q1,  q3,  q15\n"
+               "vst1.32   {d0, d1}, [%[out0], :128]\n"
+               "vst1.32   {d2, d3}, [%[out1], :128]\n"
+               "  vpadd.s32 d24, d16, d16\n"
+
+               "update_joint_stereo_samples\n"
+
+               "add   %[in0], %[in], #0\n"
+               "add   %[in1], %[in], #32\n"
+               "add   %[out0], %[out], #0\n\n"
+               "add   %[out1], %[out], #32\n"
+               "vmov.s32  q0, %[c1]\n"
+               "vadd.s32  q0, q0, q14\n"
+
+               "calc_scalefactors\n"
+
+               /* check whether to use joint stereo for subbands 0, 1, 2, 3 */
+               "vadd.s32  q15, q0,  q1\n"
+               "vadd.s32  q9,  q2,  q3\n"
+               "vld1.32   {d16, d17}, [%[consts], :128]!\n"
+               "vcgt.s32  q15, q15, q9\n"
+
+               /* combine last part of 'joint' with d24 and save to memory */
+               /* update and save scale factors to memory */
+               "  vand.s32  q8, q8, q15\n"
+               "vbit.s32  q0,  q2,  q15\n"
+               "  vpadd.s32 d16, d16, d17\n"
+               "vbit.s32  q1,  q3,  q15\n"
+               "  vpadd.s32 d16, d16, d16\n"
+               "vst1.32   {d0, d1}, [%[out0], :128]\n"
+               "  vadd.s32  d16, d16, d24\n"
+               "vst1.32   {d2, d3}, [%[out1], :128]\n"
+               "  vst1.32   {d16[0]}, [%[joint]]\n"
+
+               "update_joint_stereo_samples\n"
+       "9:\n"
+               ".purgem calc_scalefactors\n"
+               ".purgem update_joint_stereo_samples\n"
+               :
+                 [i]      "+&r" (i),
+                 [in]     "+&r" (in),
+                 [in0]    "=&r" (in0),
+                 [in1]    "=&r" (in1),
+                 [out]    "+&r" (out),
+                 [out0]   "=&r" (out0),
+                 [out1]   "=&r" (out1),
+                 [consts] "+&r" (consts)
+               :
+                 [inc]      "r" ((char *) &sb_sample_f[1][0][0] -
+                                (char *) &sb_sample_f[0][0][0]),
+                 [blocks]   "r" (blocks),
+                 [joint]    "r" (&joint),
+                 [c1]       "i" (1 << SCALE_OUT_BITS),
+                 [c2]       "i" (31 - SCALE_OUT_BITS),
+                 [zero]     "r" (0)
+               : "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+                 "d16", "d17", "d18", "d19", "d20", "d21", "d22",
+                 "d23", "d24", "d25", "d26", "d27", "d28", "d29",
+                 "d30", "d31", "cc", "memory");
+
+       return joint;
+}
+
+#define PERM_BE(a, b, c, d) {             \
+               (a * 2) + 1, (a * 2) + 0, \
+               (b * 2) + 1, (b * 2) + 0, \
+               (c * 2) + 1, (c * 2) + 0, \
+               (d * 2) + 1, (d * 2) + 0  \
+       }
+#define PERM_LE(a, b, c, d) {             \
+               (a * 2) + 0, (a * 2) + 1, \
+               (b * 2) + 0, (b * 2) + 1, \
+               (c * 2) + 0, (c * 2) + 1, \
+               (d * 2) + 0, (d * 2) + 1  \
+       }
+
+static SBC_ALWAYS_INLINE int sbc_enc_process_input_4s_neon_internal(
+       int position,
+       const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+       int nsamples, int nchannels, int big_endian)
+{
+       static SBC_ALIGNED uint8_t perm_be[2][8] = {
+               PERM_BE(7, 3, 6, 4),
+               PERM_BE(0, 2, 1, 5)
+       };
+       static SBC_ALIGNED uint8_t perm_le[2][8] = {
+               PERM_LE(7, 3, 6, 4),
+               PERM_LE(0, 2, 1, 5)
+       };
+       /* handle X buffer wraparound */
+       if (position < nsamples) {
+               int16_t *dst = &X[0][SBC_X_BUFFER_SIZE - 40];
+               int16_t *src = &X[0][position];
+               __asm__ volatile (
+                       "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                       "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                       "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                       "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                       "vld1.16 {d0}, [%[src], :64]!\n"
+                       "vst1.16 {d0}, [%[dst], :64]!\n"
+                       :
+                         [dst] "+r" (dst),
+                         [src] "+r" (src)
+                       : : "memory", "d0", "d1", "d2", "d3");
+               if (nchannels > 1) {
+                       dst = &X[1][SBC_X_BUFFER_SIZE - 40];
+                       src = &X[1][position];
+                       __asm__ volatile (
+                               "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                               "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                               "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                               "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                               "vld1.16 {d0}, [%[src], :64]!\n"
+                               "vst1.16 {d0}, [%[dst], :64]!\n"
+                               :
+                                 [dst] "+r" (dst),
+                                 [src] "+r" (src)
+                               : : "memory", "d0", "d1", "d2", "d3");
+               }
+               position = SBC_X_BUFFER_SIZE - 40;
+       }
+
+       if ((nchannels > 1) && ((uintptr_t)pcm & 1)) {
+               /* poor 'pcm' alignment */
+               int16_t *x = &X[0][position];
+               int16_t *y = &X[1][position];
+               __asm__ volatile (
+                       "vld1.8  {d0, d1}, [%[perm], :128]\n"
+               "1:\n"
+                       "sub     %[x], %[x], #16\n"
+                       "sub     %[y], %[y], #16\n"
+                       "sub     %[position], %[position], #8\n"
+                       "vld1.8  {d4, d5}, [%[pcm]]!\n"
+                       "vuzp.16 d4,  d5\n"
+                       "vld1.8  {d20, d21}, [%[pcm]]!\n"
+                       "vuzp.16 d20, d21\n"
+                       "vswp    d5,  d20\n"
+                       "vtbl.8  d16, {d4, d5}, d0\n"
+                       "vtbl.8  d17, {d4, d5}, d1\n"
+                       "vtbl.8  d18, {d20, d21}, d0\n"
+                       "vtbl.8  d19, {d20, d21}, d1\n"
+                       "vst1.16 {d16, d17}, [%[x], :128]\n"
+                       "vst1.16 {d18, d19}, [%[y], :128]\n"
+                       "subs    %[nsamples], %[nsamples], #8\n"
+                       "bgt     1b\n"
+                       :
+                         [x]        "+r" (x),
+                         [y]        "+r" (y),
+                         [pcm]      "+r" (pcm),
+                         [nsamples] "+r" (nsamples),
+                         [position] "+r" (position)
+                       :
+                         [perm]      "r" (big_endian ? perm_be : perm_le)
+                       : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+                         "d5", "d6", "d7", "d16", "d17", "d18", "d19",
+                         "d20", "d21", "d22", "d23");
+       } else if (nchannels > 1) {
+               /* proper 'pcm' alignment */
+               int16_t *x = &X[0][position];
+               int16_t *y = &X[1][position];
+               __asm__ volatile (
+                       "vld1.8  {d0, d1}, [%[perm], :128]\n"
+               "1:\n"
+                       "sub     %[x], %[x], #16\n"
+                       "sub     %[y], %[y], #16\n"
+                       "sub     %[position], %[position], #8\n"
+                       "vld2.16 {d4, d5}, [%[pcm]]!\n"
+                       "vld2.16 {d20, d21}, [%[pcm]]!\n"
+                       "vswp    d5, d20\n"
+                       "vtbl.8  d16, {d4, d5}, d0\n"
+                       "vtbl.8  d17, {d4, d5}, d1\n"
+                       "vtbl.8  d18, {d20, d21}, d0\n"
+                       "vtbl.8  d19, {d20, d21}, d1\n"
+                       "vst1.16 {d16, d17}, [%[x], :128]\n"
+                       "vst1.16 {d18, d19}, [%[y], :128]\n"
+                       "subs    %[nsamples], %[nsamples], #8\n"
+                       "bgt     1b\n"
+                       :
+                         [x]        "+r" (x),
+                         [y]        "+r" (y),
+                         [pcm]      "+r" (pcm),
+                         [nsamples] "+r" (nsamples),
+                         [position] "+r" (position)
+                       :
+                         [perm]      "r" (big_endian ? perm_be : perm_le)
+                       : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+                         "d5", "d6", "d7", "d16", "d17", "d18", "d19",
+                         "d20", "d21", "d22", "d23");
+       } else {
+               int16_t *x = &X[0][position];
+               __asm__ volatile (
+                       "vld1.8  {d0, d1}, [%[perm], :128]\n"
+               "1:\n"
+                       "sub     %[x], %[x], #16\n"
+                       "sub     %[position], %[position], #8\n"
+                       "vld1.8  {d4, d5}, [%[pcm]]!\n"
+                       "vtbl.8  d16, {d4, d5}, d0\n"
+                       "vtbl.8  d17, {d4, d5}, d1\n"
+                       "vst1.16 {d16, d17}, [%[x], :128]\n"
+                       "subs    %[nsamples], %[nsamples], #8\n"
+                       "bgt     1b\n"
+                       :
+                         [x]        "+r" (x),
+                         [pcm]      "+r" (pcm),
+                         [nsamples] "+r" (nsamples),
+                         [position] "+r" (position)
+                       :
+                         [perm]      "r" (big_endian ? perm_be : perm_le)
+                       : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+                         "d5", "d6", "d7", "d16", "d17", "d18", "d19");
+       }
+       return position;
+}
+
+static SBC_ALWAYS_INLINE int sbc_enc_process_input_8s_neon_internal(
+       int position,
+       const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+       int nsamples, int nchannels, int big_endian)
+{
+       static SBC_ALIGNED uint8_t perm_be[4][8] = {
+               PERM_BE(15, 7, 14, 8),
+               PERM_BE(13, 9, 12, 10),
+               PERM_BE(11, 3, 6,  0),
+               PERM_BE(5,  1, 4,  2)
+       };
+       static SBC_ALIGNED uint8_t perm_le[4][8] = {
+               PERM_LE(15, 7, 14, 8),
+               PERM_LE(13, 9, 12, 10),
+               PERM_LE(11, 3, 6,  0),
+               PERM_LE(5,  1, 4,  2)
+       };
+       /* handle X buffer wraparound */
+       if (position < nsamples) {
+               int16_t *dst = &X[0][SBC_X_BUFFER_SIZE - 72];
+               int16_t *src = &X[0][position];
+               __asm__ volatile (
+                       "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                       "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                       "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                       "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                       "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                       "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                       "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                       "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                       "vld1.16 {d0, d1}, [%[src], :128]!\n"
+                       "vst1.16 {d0, d1}, [%[dst], :128]!\n"
+                       :
+                         [dst] "+r" (dst),
+                         [src] "+r" (src)
+                       : : "memory", "d0", "d1", "d2", "d3");
+               if (nchannels > 1) {
+                       dst = &X[1][SBC_X_BUFFER_SIZE - 72];
+                       src = &X[1][position];
+                       __asm__ volatile (
+                               "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                               "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                               "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                               "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                               "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                               "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                               "vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
+                               "vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
+                               "vld1.16 {d0, d1}, [%[src], :128]!\n"
+                               "vst1.16 {d0, d1}, [%[dst], :128]!\n"
+                               :
+                                 [dst] "+r" (dst),
+                                 [src] "+r" (src)
+                               : : "memory", "d0", "d1", "d2", "d3");
+               }
+               position = SBC_X_BUFFER_SIZE - 72;
+       }
+
+       if ((nchannels > 1) && ((uintptr_t)pcm & 1)) {
+               /* poor 'pcm' alignment */
+               int16_t *x = &X[0][position];
+               int16_t *y = &X[1][position];
+               __asm__ volatile (
+                       "vld1.8  {d0, d1, d2, d3}, [%[perm], :128]\n"
+               "1:\n"
+                       "sub     %[x], %[x], #32\n"
+                       "sub     %[y], %[y], #32\n"
+                       "sub     %[position], %[position], #16\n"
+                       "vld1.8  {d4, d5, d6, d7}, [%[pcm]]!\n"
+                       "vuzp.16 q2,  q3\n"
+                       "vld1.8  {d20, d21, d22, d23}, [%[pcm]]!\n"
+                       "vuzp.16 q10, q11\n"
+                       "vswp    q3,  q10\n"
+                       "vtbl.8  d16, {d4, d5, d6, d7}, d0\n"
+                       "vtbl.8  d17, {d4, d5, d6, d7}, d1\n"
+                       "vtbl.8  d18, {d4, d5, d6, d7}, d2\n"
+                       "vtbl.8  d19, {d4, d5, d6, d7}, d3\n"
+                       "vst1.16 {d16, d17, d18, d19}, [%[x], :128]\n"
+                       "vtbl.8  d16, {d20, d21, d22, d23}, d0\n"
+                       "vtbl.8  d17, {d20, d21, d22, d23}, d1\n"
+                       "vtbl.8  d18, {d20, d21, d22, d23}, d2\n"
+                       "vtbl.8  d19, {d20, d21, d22, d23}, d3\n"
+                       "vst1.16 {d16, d17, d18, d19}, [%[y], :128]\n"
+                       "subs    %[nsamples], %[nsamples], #16\n"
+                       "bgt     1b\n"
+                       :
+                         [x]        "+r" (x),
+                         [y]        "+r" (y),
+                         [pcm]      "+r" (pcm),
+                         [nsamples] "+r" (nsamples),
+                         [position] "+r" (position)
+                       :
+                         [perm]      "r" (big_endian ? perm_be : perm_le)
+                       : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+                         "d5", "d6", "d7", "d16", "d17", "d18", "d19",
+                         "d20", "d21", "d22", "d23");
+       } else if (nchannels > 1) {
+               /* proper 'pcm' alignment */
+               int16_t *x = &X[0][position];
+               int16_t *y = &X[1][position];
+               __asm__ volatile (
+                       "vld1.8  {d0, d1, d2, d3}, [%[perm], :128]\n"
+               "1:\n"
+                       "sub     %[x], %[x], #32\n"
+                       "sub     %[y], %[y], #32\n"
+                       "sub     %[position], %[position], #16\n"
+                       "vld2.16  {d4, d5, d6, d7}, [%[pcm]]!\n"
+                       "vld2.16  {d20, d21, d22, d23}, [%[pcm]]!\n"
+                       "vswp    q3, q10\n"
+                       "vtbl.8  d16, {d4, d5, d6, d7}, d0\n"
+                       "vtbl.8  d17, {d4, d5, d6, d7}, d1\n"
+                       "vtbl.8  d18, {d4, d5, d6, d7}, d2\n"
+                       "vtbl.8  d19, {d4, d5, d6, d7}, d3\n"
+                       "vst1.16 {d16, d17, d18, d19}, [%[x], :128]\n"
+                       "vtbl.8  d16, {d20, d21, d22, d23}, d0\n"
+                       "vtbl.8  d17, {d20, d21, d22, d23}, d1\n"
+                       "vtbl.8  d18, {d20, d21, d22, d23}, d2\n"
+                       "vtbl.8  d19, {d20, d21, d22, d23}, d3\n"
+                       "vst1.16 {d16, d17, d18, d19}, [%[y], :128]\n"
+                       "subs    %[nsamples], %[nsamples], #16\n"
+                       "bgt     1b\n"
+                       :
+                         [x]        "+r" (x),
+                         [y]        "+r" (y),
+                         [pcm]      "+r" (pcm),
+                         [nsamples] "+r" (nsamples),
+                         [position] "+r" (position)
+                       :
+                         [perm]      "r" (big_endian ? perm_be : perm_le)
+                       : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+                         "d5", "d6", "d7", "d16", "d17", "d18", "d19",
+                         "d20", "d21", "d22", "d23");
+       } else {
+               int16_t *x = &X[0][position];
+               __asm__ volatile (
+                       "vld1.8  {d0, d1, d2, d3}, [%[perm], :128]\n"
+               "1:\n"
+                       "sub     %[x], %[x], #32\n"
+                       "sub     %[position], %[position], #16\n"
+                       "vld1.8  {d4, d5, d6, d7}, [%[pcm]]!\n"
+                       "vtbl.8  d16, {d4, d5, d6, d7}, d0\n"
+                       "vtbl.8  d17, {d4, d5, d6, d7}, d1\n"
+                       "vtbl.8  d18, {d4, d5, d6, d7}, d2\n"
+                       "vtbl.8  d19, {d4, d5, d6, d7}, d3\n"
+                       "vst1.16 {d16, d17, d18, d19}, [%[x], :128]\n"
+                       "subs    %[nsamples], %[nsamples], #16\n"
+                       "bgt     1b\n"
+                       :
+                         [x]        "+r" (x),
+                         [pcm]      "+r" (pcm),
+                         [nsamples] "+r" (nsamples),
+                         [position] "+r" (position)
+                       :
+                         [perm]      "r" (big_endian ? perm_be : perm_le)
+                       : "cc", "memory", "d0", "d1", "d2", "d3", "d4",
+                         "d5", "d6", "d7", "d16", "d17", "d18", "d19");
+       }
+       return position;
+}
+
+#undef PERM_BE
+#undef PERM_LE
+
+static int sbc_enc_process_input_4s_be_neon(int position, const uint8_t *pcm,
+                                       int16_t X[2][SBC_X_BUFFER_SIZE],
+                                       int nsamples, int nchannels)
+{
+       return sbc_enc_process_input_4s_neon_internal(
+               position, pcm, X, nsamples, nchannels, 1);
+}
+
+static int sbc_enc_process_input_4s_le_neon(int position, const uint8_t *pcm,
+                                       int16_t X[2][SBC_X_BUFFER_SIZE],
+                                       int nsamples, int nchannels)
+{
+       return sbc_enc_process_input_4s_neon_internal(
+               position, pcm, X, nsamples, nchannels, 0);
+}
+
+static int sbc_enc_process_input_8s_be_neon(int position, const uint8_t *pcm,
+                                       int16_t X[2][SBC_X_BUFFER_SIZE],
+                                       int nsamples, int nchannels)
+{
+       return sbc_enc_process_input_8s_neon_internal(
+               position, pcm, X, nsamples, nchannels, 1);
+}
+
+static int sbc_enc_process_input_8s_le_neon(int position, const uint8_t *pcm,
+                                       int16_t X[2][SBC_X_BUFFER_SIZE],
+                                       int nsamples, int nchannels)
+{
+       return sbc_enc_process_input_8s_neon_internal(
+               position, pcm, X, nsamples, nchannels, 0);
+}
+
+void sbc_init_primitives_neon(struct sbc_encoder_state *state)
+{
+       state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_neon;
+       state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_neon;
+       state->sbc_calc_scalefactors = sbc_calc_scalefactors_neon;
+       state->sbc_calc_scalefactors_j = sbc_calc_scalefactors_j_neon;
+       state->sbc_enc_process_input_4s_le = sbc_enc_process_input_4s_le_neon;
+       state->sbc_enc_process_input_4s_be = sbc_enc_process_input_4s_be_neon;
+       state->sbc_enc_process_input_8s_le = sbc_enc_process_input_8s_le_neon;
+       state->sbc_enc_process_input_8s_be = sbc_enc_process_input_8s_be_neon;
+       state->implementation_info = "NEON";
+}
+
+#endif
diff --git a/sbc/sbc_primitives_neon.h b/sbc/sbc_primitives_neon.h
new file mode 100644 (file)
index 0000000..ea3da06
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_NEON_H
+#define __SBC_PRIMITIVES_NEON_H
+
+#include "sbc_primitives.h"
+
+#if defined(__GNUC__) && defined(__ARM_NEON__) && \
+               !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15)
+
+#define SBC_BUILD_WITH_NEON_SUPPORT
+
+void sbc_init_primitives_neon(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/sbc/sbc_tables.h b/sbc/sbc_tables.h
new file mode 100644 (file)
index 0000000..25e24e6
--- /dev/null
@@ -0,0 +1,662 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk@ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ *  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
+ *
+ */
+
+/* A2DP specification: Appendix B, page 69 */
+static const int sbc_offset4[4][4] = {
+       { -1, 0, 0, 0 },
+       { -2, 0, 0, 1 },
+       { -2, 0, 0, 1 },
+       { -2, 0, 0, 1 }
+};
+
+/* A2DP specification: Appendix B, page 69 */
+static const int sbc_offset8[4][8] = {
+       { -2, 0, 0, 0, 0, 0, 0, 1 },
+       { -3, 0, 0, 0, 0, 0, 1, 2 },
+       { -4, 0, 0, 0, 0, 0, 1, 2 },
+       { -4, 0, 0, 0, 0, 0, 1, 2 }
+};
+
+/* extra bits of precision for the synthesis filter input data */
+#define SBCDEC_FIXED_EXTRA_BITS 2
+
+#define SS4(val) ASR(val, SCALE_SPROTO4_TBL)
+#define SS8(val) ASR(val, SCALE_SPROTO8_TBL)
+#define SN4(val) ASR(val, SCALE_NPROTO4_TBL + 1 + SBCDEC_FIXED_EXTRA_BITS)
+#define SN8(val) ASR(val, SCALE_NPROTO8_TBL + 1 + SBCDEC_FIXED_EXTRA_BITS)
+
+static const int32_t sbc_proto_4_40m0[] = {
+       SS4(0x00000000), SS4(0xffa6982f), SS4(0xfba93848), SS4(0x0456c7b8),
+       SS4(0x005967d1), SS4(0xfffb9ac7), SS4(0xff589157), SS4(0xf9c2a8d8),
+       SS4(0x027c1434), SS4(0x0019118b), SS4(0xfff3c74c), SS4(0xff137330),
+       SS4(0xf81b8d70), SS4(0x00ec1b8b), SS4(0xfff0b71a), SS4(0xffe99b00),
+       SS4(0xfef84470), SS4(0xf6fb4370), SS4(0xffcdc351), SS4(0xffe01dc7)
+};
+
+static const int32_t sbc_proto_4_40m1[] = {
+       SS4(0xffe090ce), SS4(0xff2c0475), SS4(0xf694f800), SS4(0xff2c0475),
+       SS4(0xffe090ce), SS4(0xffe01dc7), SS4(0xffcdc351), SS4(0xf6fb4370),
+       SS4(0xfef84470), SS4(0xffe99b00), SS4(0xfff0b71a), SS4(0x00ec1b8b),
+       SS4(0xf81b8d70), SS4(0xff137330), SS4(0xfff3c74c), SS4(0x0019118b),
+       SS4(0x027c1434), SS4(0xf9c2a8d8), SS4(0xff589157), SS4(0xfffb9ac7)
+};
+
+static const int32_t sbc_proto_8_80m0[] = {
+       SS8(0x00000000), SS8(0xfe8d1970), SS8(0xee979f00), SS8(0x11686100),
+       SS8(0x0172e690), SS8(0xfff5bd1a), SS8(0xfdf1c8d4), SS8(0xeac182c0),
+       SS8(0x0d9daee0), SS8(0x00e530da), SS8(0xffe9811d), SS8(0xfd52986c),
+       SS8(0xe7054ca0), SS8(0x0a00d410), SS8(0x006c1de4), SS8(0xffdba705),
+       SS8(0xfcbc98e8), SS8(0xe3889d20), SS8(0x06af2308), SS8(0x000bb7db),
+       SS8(0xffca00ed), SS8(0xfc3fbb68), SS8(0xe071bc00), SS8(0x03bf7948),
+       SS8(0xffc4e05c), SS8(0xffb54b3b), SS8(0xfbedadc0), SS8(0xdde26200),
+       SS8(0x0142291c), SS8(0xff960e94), SS8(0xff9f3e17), SS8(0xfbd8f358),
+       SS8(0xdbf79400), SS8(0xff405e01), SS8(0xff7d4914), SS8(0xff8b1a31),
+       SS8(0xfc1417b8), SS8(0xdac7bb40), SS8(0xfdbb828c), SS8(0xff762170)
+};
+
+static const int32_t sbc_proto_8_80m1[] = {
+       SS8(0xff7c272c), SS8(0xfcb02620), SS8(0xda612700), SS8(0xfcb02620),
+       SS8(0xff7c272c), SS8(0xff762170), SS8(0xfdbb828c), SS8(0xdac7bb40),
+       SS8(0xfc1417b8), SS8(0xff8b1a31), SS8(0xff7d4914), SS8(0xff405e01),
+       SS8(0xdbf79400), SS8(0xfbd8f358), SS8(0xff9f3e17), SS8(0xff960e94),
+       SS8(0x0142291c), SS8(0xdde26200), SS8(0xfbedadc0), SS8(0xffb54b3b),
+       SS8(0xffc4e05c), SS8(0x03bf7948), SS8(0xe071bc00), SS8(0xfc3fbb68),
+       SS8(0xffca00ed), SS8(0x000bb7db), SS8(0x06af2308), SS8(0xe3889d20),
+       SS8(0xfcbc98e8), SS8(0xffdba705), SS8(0x006c1de4), SS8(0x0a00d410),
+       SS8(0xe7054ca0), SS8(0xfd52986c), SS8(0xffe9811d), SS8(0x00e530da),
+       SS8(0x0d9daee0), SS8(0xeac182c0), SS8(0xfdf1c8d4), SS8(0xfff5bd1a)
+};
+
+static const int32_t synmatrix4[8][4] = {
+       { SN4(0x05a82798), SN4(0xfa57d868), SN4(0xfa57d868), SN4(0x05a82798) },
+       { SN4(0x030fbc54), SN4(0xf89be510), SN4(0x07641af0), SN4(0xfcf043ac) },
+       { SN4(0x00000000), SN4(0x00000000), SN4(0x00000000), SN4(0x00000000) },
+       { SN4(0xfcf043ac), SN4(0x07641af0), SN4(0xf89be510), SN4(0x030fbc54) },
+       { SN4(0xfa57d868), SN4(0x05a82798), SN4(0x05a82798), SN4(0xfa57d868) },
+       { SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) },
+       { SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000), SN4(0xf8000000) },
+       { SN4(0xf89be510), SN4(0xfcf043ac), SN4(0x030fbc54), SN4(0x07641af0) }
+};
+
+static const int32_t synmatrix8[16][8] = {
+       { SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798),
+         SN8(0x05a82798), SN8(0xfa57d868), SN8(0xfa57d868), SN8(0x05a82798) },
+       { SN8(0x0471ced0), SN8(0xf8275a10), SN8(0x018f8b84), SN8(0x06a6d988),
+         SN8(0xf9592678), SN8(0xfe70747c), SN8(0x07d8a5f0), SN8(0xfb8e3130) },
+       { SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac),
+         SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54) },
+       { SN8(0x018f8b84), SN8(0xfb8e3130), SN8(0x06a6d988), SN8(0xf8275a10),
+         SN8(0x07d8a5f0), SN8(0xf9592678), SN8(0x0471ced0), SN8(0xfe70747c) },
+       { SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000),
+         SN8(0x00000000), SN8(0x00000000), SN8(0x00000000), SN8(0x00000000) },
+       { SN8(0xfe70747c), SN8(0x0471ced0), SN8(0xf9592678), SN8(0x07d8a5f0),
+         SN8(0xf8275a10), SN8(0x06a6d988), SN8(0xfb8e3130), SN8(0x018f8b84) },
+       { SN8(0xfcf043ac), SN8(0x07641af0), SN8(0xf89be510), SN8(0x030fbc54),
+         SN8(0x030fbc54), SN8(0xf89be510), SN8(0x07641af0), SN8(0xfcf043ac) },
+       { SN8(0xfb8e3130), SN8(0x07d8a5f0), SN8(0xfe70747c), SN8(0xf9592678),
+         SN8(0x06a6d988), SN8(0x018f8b84), SN8(0xf8275a10), SN8(0x0471ced0) },
+       { SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868),
+         SN8(0xfa57d868), SN8(0x05a82798), SN8(0x05a82798), SN8(0xfa57d868) },
+       { SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
+         SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) },
+       { SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0),
+         SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) },
+       { SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c),
+         SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) },
+       { SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000),
+         SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000), SN8(0xf8000000) },
+       { SN8(0xf8275a10), SN8(0xf9592678), SN8(0xfb8e3130), SN8(0xfe70747c),
+         SN8(0x018f8b84), SN8(0x0471ced0), SN8(0x06a6d988), SN8(0x07d8a5f0) },
+       { SN8(0xf89be510), SN8(0xfcf043ac), SN8(0x030fbc54), SN8(0x07641af0),
+         SN8(0x07641af0), SN8(0x030fbc54), SN8(0xfcf043ac), SN8(0xf89be510) },
+       { SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
+         SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) }
+};
+
+/* Uncomment the following line to enable high precision build of SBC encoder */
+
+/* #define SBC_HIGH_PRECISION */
+
+#ifdef SBC_HIGH_PRECISION
+#define FIXED_A int64_t /* data type for fixed point accumulator */
+#define FIXED_T int32_t /* data type for fixed point constants */
+#define SBC_FIXED_EXTRA_BITS 16
+#else
+#define FIXED_A int32_t /* data type for fixed point accumulator */
+#define FIXED_T int16_t /* data type for fixed point constants */
+#define SBC_FIXED_EXTRA_BITS 0
+#endif
+
+/* A2DP specification: Section 12.8 Tables
+ *
+ * Original values are premultiplied by 2 for better precision (that is the
+ * maximum which is possible without overflows)
+ *
+ * Note: in each block of 8 numbers sign was changed for elements 2 and 7
+ * in order to compensate the same change applied to cos_table_fixed_4
+ */
+#define SBC_PROTO_FIXED4_SCALE \
+       ((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 1)
+#define F_PROTO4(x) (FIXED_A) ((x * 2) * \
+       ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_PROTO4(x)
+static const FIXED_T _sbc_proto_fixed4[40] = {
+       F(0.00000000E+00),  F(5.36548976E-04),
+       -F(1.49188357E-03),  F(2.73370904E-03),
+       F(3.83720193E-03),  F(3.89205149E-03),
+       F(1.86581691E-03),  F(3.06012286E-03),
+
+       F(1.09137620E-02),  F(2.04385087E-02),
+       -F(2.88757392E-02),  F(3.21939290E-02),
+       F(2.58767811E-02),  F(6.13245186E-03),
+       -F(2.88217274E-02),  F(7.76463494E-02),
+
+       F(1.35593274E-01),  F(1.94987841E-01),
+       -F(2.46636662E-01),  F(2.81828203E-01),
+       F(2.94315332E-01),  F(2.81828203E-01),
+       F(2.46636662E-01), -F(1.94987841E-01),
+
+       -F(1.35593274E-01), -F(7.76463494E-02),
+       F(2.88217274E-02),  F(6.13245186E-03),
+       F(2.58767811E-02),  F(3.21939290E-02),
+       F(2.88757392E-02), -F(2.04385087E-02),
+
+       -F(1.09137620E-02), -F(3.06012286E-03),
+       -F(1.86581691E-03),  F(3.89205149E-03),
+       F(3.83720193E-03),  F(2.73370904E-03),
+       F(1.49188357E-03), -F(5.36548976E-04),
+};
+#undef F
+
+/*
+ * To produce this cosine matrix in Octave:
+ *
+ * b = zeros(4, 8);
+ * for i = 0:3
+ * for j = 0:7 b(i+1, j+1) = cos((i + 0.5) * (j - 2) * (pi/4))
+ * endfor
+ * endfor;
+ * printf("%.10f, ", b');
+ *
+ * Note: in each block of 8 numbers sign was changed for elements 2 and 7
+ *
+ * Change of sign for element 2 allows to replace constant 1.0 (not
+ * representable in Q15 format) with -1.0 (fine with Q15).
+ * Changed sign for element 7 allows to have more similar constants
+ * and simplify subband filter function code.
+ */
+#define SBC_COS_TABLE_FIXED4_SCALE \
+       ((sizeof(FIXED_T) * CHAR_BIT - 1) + SBC_FIXED_EXTRA_BITS)
+#define F_COS4(x) (FIXED_A) ((x) * \
+       ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_COS4(x)
+static const FIXED_T cos_table_fixed_4[32] = {
+       F(0.7071067812),  F(0.9238795325), -F(1.0000000000),  F(0.9238795325),
+       F(0.7071067812),  F(0.3826834324),  F(0.0000000000),  F(0.3826834324),
+
+       -F(0.7071067812),  F(0.3826834324), -F(1.0000000000),  F(0.3826834324),
+       -F(0.7071067812), -F(0.9238795325), -F(0.0000000000), -F(0.9238795325),
+
+       -F(0.7071067812), -F(0.3826834324), -F(1.0000000000), -F(0.3826834324),
+       -F(0.7071067812),  F(0.9238795325),  F(0.0000000000),  F(0.9238795325),
+
+       F(0.7071067812), -F(0.9238795325), -F(1.0000000000), -F(0.9238795325),
+       F(0.7071067812), -F(0.3826834324), -F(0.0000000000), -F(0.3826834324),
+};
+#undef F
+
+/* A2DP specification: Section 12.8 Tables
+ *
+ * Original values are premultiplied by 4 for better precision (that is the
+ * maximum which is possible without overflows)
+ *
+ * Note: in each block of 16 numbers sign was changed for elements 4, 13, 14, 15
+ * in order to compensate the same change applied to cos_table_fixed_8
+ */
+#define SBC_PROTO_FIXED8_SCALE \
+       ((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 1)
+#define F_PROTO8(x) (FIXED_A) ((x * 2) * \
+       ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_PROTO8(x)
+static const FIXED_T _sbc_proto_fixed8[80] = {
+       F(0.00000000E+00),  F(1.56575398E-04),
+       F(3.43256425E-04),  F(5.54620202E-04),
+       -F(8.23919506E-04),  F(1.13992507E-03),
+       F(1.47640169E-03),  F(1.78371725E-03),
+       F(2.01182542E-03),  F(2.10371989E-03),
+       F(1.99454554E-03),  F(1.61656283E-03),
+       F(9.02154502E-04),  F(1.78805361E-04),
+       F(1.64973098E-03),  F(3.49717454E-03),
+
+       F(5.65949473E-03),  F(8.02941163E-03),
+       F(1.04584443E-02),  F(1.27472335E-02),
+       -F(1.46525263E-02),  F(1.59045603E-02),
+       F(1.62208471E-02),  F(1.53184106E-02),
+       F(1.29371806E-02),  F(8.85757540E-03),
+       F(2.92408442E-03), -F(4.91578024E-03),
+       -F(1.46404076E-02),  F(2.61098752E-02),
+       F(3.90751381E-02),  F(5.31873032E-02),
+
+       F(6.79989431E-02),  F(8.29847578E-02),
+       F(9.75753918E-02),  F(1.11196689E-01),
+       -F(1.23264548E-01),  F(1.33264415E-01),
+       F(1.40753505E-01),  F(1.45389847E-01),
+       F(1.46955068E-01),  F(1.45389847E-01),
+       F(1.40753505E-01),  F(1.33264415E-01),
+       F(1.23264548E-01), -F(1.11196689E-01),
+       -F(9.75753918E-02), -F(8.29847578E-02),
+
+       -F(6.79989431E-02), -F(5.31873032E-02),
+       -F(3.90751381E-02), -F(2.61098752E-02),
+       F(1.46404076E-02), -F(4.91578024E-03),
+       F(2.92408442E-03),  F(8.85757540E-03),
+       F(1.29371806E-02),  F(1.53184106E-02),
+       F(1.62208471E-02),  F(1.59045603E-02),
+       F(1.46525263E-02), -F(1.27472335E-02),
+       -F(1.04584443E-02), -F(8.02941163E-03),
+
+       -F(5.65949473E-03), -F(3.49717454E-03),
+       -F(1.64973098E-03), -F(1.78805361E-04),
+       -F(9.02154502E-04),  F(1.61656283E-03),
+       F(1.99454554E-03),  F(2.10371989E-03),
+       F(2.01182542E-03),  F(1.78371725E-03),
+       F(1.47640169E-03),  F(1.13992507E-03),
+       F(8.23919506E-04), -F(5.54620202E-04),
+       -F(3.43256425E-04), -F(1.56575398E-04),
+};
+#undef F
+
+/*
+ * To produce this cosine matrix in Octave:
+ *
+ * b = zeros(8, 16);
+ * for i = 0:7
+ * for j = 0:15 b(i+1, j+1) = cos((i + 0.5) * (j - 4) * (pi/8))
+ * endfor endfor;
+ * printf("%.10f, ", b');
+ *
+ * Note: in each block of 16 numbers sign was changed for elements 4, 13, 14, 15
+ *
+ * Change of sign for element 4 allows to replace constant 1.0 (not
+ * representable in Q15 format) with -1.0 (fine with Q15).
+ * Changed signs for elements 13, 14, 15 allow to have more similar constants
+ * and simplify subband filter function code.
+ */
+#define SBC_COS_TABLE_FIXED8_SCALE \
+       ((sizeof(FIXED_T) * CHAR_BIT - 1) + SBC_FIXED_EXTRA_BITS)
+#define F_COS8(x) (FIXED_A) ((x) * \
+       ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_COS8(x)
+static const FIXED_T cos_table_fixed_8[128] = {
+       F(0.7071067812),  F(0.8314696123),  F(0.9238795325),  F(0.9807852804),
+       -F(1.0000000000),  F(0.9807852804),  F(0.9238795325),  F(0.8314696123),
+       F(0.7071067812),  F(0.5555702330),  F(0.3826834324),  F(0.1950903220),
+       F(0.0000000000),  F(0.1950903220),  F(0.3826834324),  F(0.5555702330),
+
+       -F(0.7071067812), -F(0.1950903220),  F(0.3826834324),  F(0.8314696123),
+       -F(1.0000000000),  F(0.8314696123),  F(0.3826834324), -F(0.1950903220),
+       -F(0.7071067812), -F(0.9807852804), -F(0.9238795325), -F(0.5555702330),
+       -F(0.0000000000), -F(0.5555702330), -F(0.9238795325), -F(0.9807852804),
+
+       -F(0.7071067812), -F(0.9807852804), -F(0.3826834324),  F(0.5555702330),
+       -F(1.0000000000),  F(0.5555702330), -F(0.3826834324), -F(0.9807852804),
+       -F(0.7071067812),  F(0.1950903220),  F(0.9238795325),  F(0.8314696123),
+       F(0.0000000000),  F(0.8314696123),  F(0.9238795325),  F(0.1950903220),
+
+       F(0.7071067812), -F(0.5555702330), -F(0.9238795325),  F(0.1950903220),
+       -F(1.0000000000),  F(0.1950903220), -F(0.9238795325), -F(0.5555702330),
+       F(0.7071067812),  F(0.8314696123), -F(0.3826834324), -F(0.9807852804),
+       -F(0.0000000000), -F(0.9807852804), -F(0.3826834324),  F(0.8314696123),
+
+       F(0.7071067812),  F(0.5555702330), -F(0.9238795325), -F(0.1950903220),
+       -F(1.0000000000), -F(0.1950903220), -F(0.9238795325),  F(0.5555702330),
+       F(0.7071067812), -F(0.8314696123), -F(0.3826834324),  F(0.9807852804),
+       F(0.0000000000),  F(0.9807852804), -F(0.3826834324), -F(0.8314696123),
+
+       -F(0.7071067812),  F(0.9807852804), -F(0.3826834324), -F(0.5555702330),
+       -F(1.0000000000), -F(0.5555702330), -F(0.3826834324),  F(0.9807852804),
+       -F(0.7071067812), -F(0.1950903220),  F(0.9238795325), -F(0.8314696123),
+       -F(0.0000000000), -F(0.8314696123),  F(0.9238795325), -F(0.1950903220),
+
+       -F(0.7071067812),  F(0.1950903220),  F(0.3826834324), -F(0.8314696123),
+       -F(1.0000000000), -F(0.8314696123),  F(0.3826834324),  F(0.1950903220),
+       -F(0.7071067812),  F(0.9807852804), -F(0.9238795325),  F(0.5555702330),
+       -F(0.0000000000),  F(0.5555702330), -F(0.9238795325),  F(0.9807852804),
+
+       F(0.7071067812), -F(0.8314696123),  F(0.9238795325), -F(0.9807852804),
+       -F(1.0000000000), -F(0.9807852804),  F(0.9238795325), -F(0.8314696123),
+       F(0.7071067812), -F(0.5555702330),  F(0.3826834324), -F(0.1950903220),
+       -F(0.0000000000), -F(0.1950903220),  F(0.3826834324), -F(0.5555702330),
+};
+#undef F
+
+/*
+ * Enforce 16 byte alignment for the data, which is supposed to be used
+ * with SIMD optimized code.
+ */
+
+#define SBC_ALIGN_BITS 4
+#define SBC_ALIGN_MASK ((1 << (SBC_ALIGN_BITS)) - 1)
+
+#ifdef __GNUC__
+#define SBC_ALIGNED __attribute__((aligned(1 << (SBC_ALIGN_BITS))))
+#else
+#define SBC_ALIGNED
+#endif
+
+/*
+ * Constant tables for the use in SIMD optimized analysis filters
+ * Each table consists of two parts:
+ * 1. reordered "proto" table
+ * 2. reordered "cos" table
+ *
+ * Due to non-symmetrical reordering, separate tables for "even"
+ * and "odd" cases are needed
+ */
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed4_simd_even[40 + 16] = {
+#define C0 1.0932568993
+#define C1 1.3056875580
+#define C2 1.3056875580
+#define C3 1.6772280856
+
+#define F(x) F_PROTO4(x)
+        F(0.00000000E+00 * C0),  F(3.83720193E-03 * C0),
+        F(5.36548976E-04 * C1),  F(2.73370904E-03 * C1),
+        F(3.06012286E-03 * C2),  F(3.89205149E-03 * C2),
+        F(0.00000000E+00 * C3), -F(1.49188357E-03 * C3),
+        F(1.09137620E-02 * C0),  F(2.58767811E-02 * C0),
+        F(2.04385087E-02 * C1),  F(3.21939290E-02 * C1),
+        F(7.76463494E-02 * C2),  F(6.13245186E-03 * C2),
+        F(0.00000000E+00 * C3), -F(2.88757392E-02 * C3),
+        F(1.35593274E-01 * C0),  F(2.94315332E-01 * C0),
+        F(1.94987841E-01 * C1),  F(2.81828203E-01 * C1),
+       -F(1.94987841E-01 * C2),  F(2.81828203E-01 * C2),
+        F(0.00000000E+00 * C3), -F(2.46636662E-01 * C3),
+       -F(1.35593274E-01 * C0),  F(2.58767811E-02 * C0),
+       -F(7.76463494E-02 * C1),  F(6.13245186E-03 * C1),
+       -F(2.04385087E-02 * C2),  F(3.21939290E-02 * C2),
+        F(0.00000000E+00 * C3),  F(2.88217274E-02 * C3),
+       -F(1.09137620E-02 * C0),  F(3.83720193E-03 * C0),
+       -F(3.06012286E-03 * C1),  F(3.89205149E-03 * C1),
+       -F(5.36548976E-04 * C2),  F(2.73370904E-03 * C2),
+        F(0.00000000E+00 * C3), -F(1.86581691E-03 * C3),
+#undef F
+#define F(x) F_COS4(x)
+        F(0.7071067812 / C0),  F(0.9238795325 / C1),
+       -F(0.7071067812 / C0),  F(0.3826834324 / C1),
+       -F(0.7071067812 / C0), -F(0.3826834324 / C1),
+        F(0.7071067812 / C0), -F(0.9238795325 / C1),
+        F(0.3826834324 / C2), -F(1.0000000000 / C3),
+       -F(0.9238795325 / C2), -F(1.0000000000 / C3),
+        F(0.9238795325 / C2), -F(1.0000000000 / C3),
+       -F(0.3826834324 / C2), -F(1.0000000000 / C3),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed4_simd_odd[40 + 16] = {
+#define C0 1.3056875580
+#define C1 1.6772280856
+#define C2 1.0932568993
+#define C3 1.3056875580
+
+#define F(x) F_PROTO4(x)
+        F(2.73370904E-03 * C0),  F(5.36548976E-04 * C0),
+       -F(1.49188357E-03 * C1),  F(0.00000000E+00 * C1),
+        F(3.83720193E-03 * C2),  F(1.09137620E-02 * C2),
+        F(3.89205149E-03 * C3),  F(3.06012286E-03 * C3),
+        F(3.21939290E-02 * C0),  F(2.04385087E-02 * C0),
+       -F(2.88757392E-02 * C1),  F(0.00000000E+00 * C1),
+        F(2.58767811E-02 * C2),  F(1.35593274E-01 * C2),
+        F(6.13245186E-03 * C3),  F(7.76463494E-02 * C3),
+        F(2.81828203E-01 * C0),  F(1.94987841E-01 * C0),
+       -F(2.46636662E-01 * C1),  F(0.00000000E+00 * C1),
+        F(2.94315332E-01 * C2), -F(1.35593274E-01 * C2),
+        F(2.81828203E-01 * C3), -F(1.94987841E-01 * C3),
+        F(6.13245186E-03 * C0), -F(7.76463494E-02 * C0),
+        F(2.88217274E-02 * C1),  F(0.00000000E+00 * C1),
+        F(2.58767811E-02 * C2), -F(1.09137620E-02 * C2),
+        F(3.21939290E-02 * C3), -F(2.04385087E-02 * C3),
+        F(3.89205149E-03 * C0), -F(3.06012286E-03 * C0),
+       -F(1.86581691E-03 * C1),  F(0.00000000E+00 * C1),
+        F(3.83720193E-03 * C2),  F(0.00000000E+00 * C2),
+        F(2.73370904E-03 * C3), -F(5.36548976E-04 * C3),
+#undef F
+#define F(x) F_COS4(x)
+        F(0.9238795325 / C0), -F(1.0000000000 / C1),
+        F(0.3826834324 / C0), -F(1.0000000000 / C1),
+       -F(0.3826834324 / C0), -F(1.0000000000 / C1),
+       -F(0.9238795325 / C0), -F(1.0000000000 / C1),
+        F(0.7071067812 / C2),  F(0.3826834324 / C3),
+       -F(0.7071067812 / C2), -F(0.9238795325 / C3),
+       -F(0.7071067812 / C2),  F(0.9238795325 / C3),
+        F(0.7071067812 / C2), -F(0.3826834324 / C3),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed8_simd_even[80 + 64] = {
+#define C0 2.7906148894
+#define C1 2.4270044280
+#define C2 2.8015616024
+#define C3 3.1710363741
+#define C4 2.5377944043
+#define C5 2.4270044280
+#define C6 2.8015616024
+#define C7 3.1710363741
+
+#define F(x) F_PROTO8(x)
+        F(0.00000000E+00 * C0),  F(2.01182542E-03 * C0),
+        F(1.56575398E-04 * C1),  F(1.78371725E-03 * C1),
+        F(3.43256425E-04 * C2),  F(1.47640169E-03 * C2),
+        F(5.54620202E-04 * C3),  F(1.13992507E-03 * C3),
+       -F(8.23919506E-04 * C4),  F(0.00000000E+00 * C4),
+        F(2.10371989E-03 * C5),  F(3.49717454E-03 * C5),
+        F(1.99454554E-03 * C6),  F(1.64973098E-03 * C6),
+        F(1.61656283E-03 * C7),  F(1.78805361E-04 * C7),
+        F(5.65949473E-03 * C0),  F(1.29371806E-02 * C0),
+        F(8.02941163E-03 * C1),  F(1.53184106E-02 * C1),
+        F(1.04584443E-02 * C2),  F(1.62208471E-02 * C2),
+        F(1.27472335E-02 * C3),  F(1.59045603E-02 * C3),
+       -F(1.46525263E-02 * C4),  F(0.00000000E+00 * C4),
+        F(8.85757540E-03 * C5),  F(5.31873032E-02 * C5),
+        F(2.92408442E-03 * C6),  F(3.90751381E-02 * C6),
+       -F(4.91578024E-03 * C7),  F(2.61098752E-02 * C7),
+        F(6.79989431E-02 * C0),  F(1.46955068E-01 * C0),
+        F(8.29847578E-02 * C1),  F(1.45389847E-01 * C1),
+        F(9.75753918E-02 * C2),  F(1.40753505E-01 * C2),
+        F(1.11196689E-01 * C3),  F(1.33264415E-01 * C3),
+       -F(1.23264548E-01 * C4),  F(0.00000000E+00 * C4),
+        F(1.45389847E-01 * C5), -F(8.29847578E-02 * C5),
+        F(1.40753505E-01 * C6), -F(9.75753918E-02 * C6),
+        F(1.33264415E-01 * C7), -F(1.11196689E-01 * C7),
+       -F(6.79989431E-02 * C0),  F(1.29371806E-02 * C0),
+       -F(5.31873032E-02 * C1),  F(8.85757540E-03 * C1),
+       -F(3.90751381E-02 * C2),  F(2.92408442E-03 * C2),
+       -F(2.61098752E-02 * C3), -F(4.91578024E-03 * C3),
+        F(1.46404076E-02 * C4),  F(0.00000000E+00 * C4),
+        F(1.53184106E-02 * C5), -F(8.02941163E-03 * C5),
+        F(1.62208471E-02 * C6), -F(1.04584443E-02 * C6),
+        F(1.59045603E-02 * C7), -F(1.27472335E-02 * C7),
+       -F(5.65949473E-03 * C0),  F(2.01182542E-03 * C0),
+       -F(3.49717454E-03 * C1),  F(2.10371989E-03 * C1),
+       -F(1.64973098E-03 * C2),  F(1.99454554E-03 * C2),
+       -F(1.78805361E-04 * C3),  F(1.61656283E-03 * C3),
+       -F(9.02154502E-04 * C4),  F(0.00000000E+00 * C4),
+        F(1.78371725E-03 * C5), -F(1.56575398E-04 * C5),
+        F(1.47640169E-03 * C6), -F(3.43256425E-04 * C6),
+        F(1.13992507E-03 * C7), -F(5.54620202E-04 * C7),
+#undef F
+#define F(x) F_COS8(x)
+        F(0.7071067812 / C0),  F(0.8314696123 / C1),
+       -F(0.7071067812 / C0), -F(0.1950903220 / C1),
+       -F(0.7071067812 / C0), -F(0.9807852804 / C1),
+        F(0.7071067812 / C0), -F(0.5555702330 / C1),
+        F(0.7071067812 / C0),  F(0.5555702330 / C1),
+       -F(0.7071067812 / C0),  F(0.9807852804 / C1),
+       -F(0.7071067812 / C0),  F(0.1950903220 / C1),
+        F(0.7071067812 / C0), -F(0.8314696123 / C1),
+        F(0.9238795325 / C2),  F(0.9807852804 / C3),
+        F(0.3826834324 / C2),  F(0.8314696123 / C3),
+       -F(0.3826834324 / C2),  F(0.5555702330 / C3),
+       -F(0.9238795325 / C2),  F(0.1950903220 / C3),
+       -F(0.9238795325 / C2), -F(0.1950903220 / C3),
+       -F(0.3826834324 / C2), -F(0.5555702330 / C3),
+        F(0.3826834324 / C2), -F(0.8314696123 / C3),
+        F(0.9238795325 / C2), -F(0.9807852804 / C3),
+       -F(1.0000000000 / C4),  F(0.5555702330 / C5),
+       -F(1.0000000000 / C4), -F(0.9807852804 / C5),
+       -F(1.0000000000 / C4),  F(0.1950903220 / C5),
+       -F(1.0000000000 / C4),  F(0.8314696123 / C5),
+       -F(1.0000000000 / C4), -F(0.8314696123 / C5),
+       -F(1.0000000000 / C4), -F(0.1950903220 / C5),
+       -F(1.0000000000 / C4),  F(0.9807852804 / C5),
+       -F(1.0000000000 / C4), -F(0.5555702330 / C5),
+        F(0.3826834324 / C6),  F(0.1950903220 / C7),
+       -F(0.9238795325 / C6), -F(0.5555702330 / C7),
+        F(0.9238795325 / C6),  F(0.8314696123 / C7),
+       -F(0.3826834324 / C6), -F(0.9807852804 / C7),
+       -F(0.3826834324 / C6),  F(0.9807852804 / C7),
+        F(0.9238795325 / C6), -F(0.8314696123 / C7),
+       -F(0.9238795325 / C6),  F(0.5555702330 / C7),
+        F(0.3826834324 / C6), -F(0.1950903220 / C7),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed8_simd_odd[80 + 64] = {
+#define C0 2.5377944043
+#define C1 2.4270044280
+#define C2 2.8015616024
+#define C3 3.1710363741
+#define C4 2.7906148894
+#define C5 2.4270044280
+#define C6 2.8015616024
+#define C7 3.1710363741
+
+#define F(x) F_PROTO8(x)
+        F(0.00000000E+00 * C0), -F(8.23919506E-04 * C0),
+        F(1.56575398E-04 * C1),  F(1.78371725E-03 * C1),
+        F(3.43256425E-04 * C2),  F(1.47640169E-03 * C2),
+        F(5.54620202E-04 * C3),  F(1.13992507E-03 * C3),
+        F(2.01182542E-03 * C4),  F(5.65949473E-03 * C4),
+        F(2.10371989E-03 * C5),  F(3.49717454E-03 * C5),
+        F(1.99454554E-03 * C6),  F(1.64973098E-03 * C6),
+        F(1.61656283E-03 * C7),  F(1.78805361E-04 * C7),
+        F(0.00000000E+00 * C0), -F(1.46525263E-02 * C0),
+        F(8.02941163E-03 * C1),  F(1.53184106E-02 * C1),
+        F(1.04584443E-02 * C2),  F(1.62208471E-02 * C2),
+        F(1.27472335E-02 * C3),  F(1.59045603E-02 * C3),
+        F(1.29371806E-02 * C4),  F(6.79989431E-02 * C4),
+        F(8.85757540E-03 * C5),  F(5.31873032E-02 * C5),
+        F(2.92408442E-03 * C6),  F(3.90751381E-02 * C6),
+       -F(4.91578024E-03 * C7),  F(2.61098752E-02 * C7),
+        F(0.00000000E+00 * C0), -F(1.23264548E-01 * C0),
+        F(8.29847578E-02 * C1),  F(1.45389847E-01 * C1),
+        F(9.75753918E-02 * C2),  F(1.40753505E-01 * C2),
+        F(1.11196689E-01 * C3),  F(1.33264415E-01 * C3),
+        F(1.46955068E-01 * C4), -F(6.79989431E-02 * C4),
+        F(1.45389847E-01 * C5), -F(8.29847578E-02 * C5),
+        F(1.40753505E-01 * C6), -F(9.75753918E-02 * C6),
+        F(1.33264415E-01 * C7), -F(1.11196689E-01 * C7),
+        F(0.00000000E+00 * C0),  F(1.46404076E-02 * C0),
+       -F(5.31873032E-02 * C1),  F(8.85757540E-03 * C1),
+       -F(3.90751381E-02 * C2),  F(2.92408442E-03 * C2),
+       -F(2.61098752E-02 * C3), -F(4.91578024E-03 * C3),
+        F(1.29371806E-02 * C4), -F(5.65949473E-03 * C4),
+        F(1.53184106E-02 * C5), -F(8.02941163E-03 * C5),
+        F(1.62208471E-02 * C6), -F(1.04584443E-02 * C6),
+        F(1.59045603E-02 * C7), -F(1.27472335E-02 * C7),
+        F(0.00000000E+00 * C0), -F(9.02154502E-04 * C0),
+       -F(3.49717454E-03 * C1),  F(2.10371989E-03 * C1),
+       -F(1.64973098E-03 * C2),  F(1.99454554E-03 * C2),
+       -F(1.78805361E-04 * C3),  F(1.61656283E-03 * C3),
+        F(2.01182542E-03 * C4),  F(0.00000000E+00 * C4),
+        F(1.78371725E-03 * C5), -F(1.56575398E-04 * C5),
+        F(1.47640169E-03 * C6), -F(3.43256425E-04 * C6),
+        F(1.13992507E-03 * C7), -F(5.54620202E-04 * C7),
+#undef F
+#define F(x) F_COS8(x)
+       -F(1.0000000000 / C0),  F(0.8314696123 / C1),
+       -F(1.0000000000 / C0), -F(0.1950903220 / C1),
+       -F(1.0000000000 / C0), -F(0.9807852804 / C1),
+       -F(1.0000000000 / C0), -F(0.5555702330 / C1),
+       -F(1.0000000000 / C0),  F(0.5555702330 / C1),
+       -F(1.0000000000 / C0),  F(0.9807852804 / C1),
+       -F(1.0000000000 / C0),  F(0.1950903220 / C1),
+       -F(1.0000000000 / C0), -F(0.8314696123 / C1),
+        F(0.9238795325 / C2),  F(0.9807852804 / C3),
+        F(0.3826834324 / C2),  F(0.8314696123 / C3),
+       -F(0.3826834324 / C2),  F(0.5555702330 / C3),
+       -F(0.9238795325 / C2),  F(0.1950903220 / C3),
+       -F(0.9238795325 / C2), -F(0.1950903220 / C3),
+       -F(0.3826834324 / C2), -F(0.5555702330 / C3),
+        F(0.3826834324 / C2), -F(0.8314696123 / C3),
+        F(0.9238795325 / C2), -F(0.9807852804 / C3),
+        F(0.7071067812 / C4),  F(0.5555702330 / C5),
+       -F(0.7071067812 / C4), -F(0.9807852804 / C5),
+       -F(0.7071067812 / C4),  F(0.1950903220 / C5),
+        F(0.7071067812 / C4),  F(0.8314696123 / C5),
+        F(0.7071067812 / C4), -F(0.8314696123 / C5),
+       -F(0.7071067812 / C4), -F(0.1950903220 / C5),
+       -F(0.7071067812 / C4),  F(0.9807852804 / C5),
+        F(0.7071067812 / C4), -F(0.5555702330 / C5),
+        F(0.3826834324 / C6),  F(0.1950903220 / C7),
+       -F(0.9238795325 / C6), -F(0.5555702330 / C7),
+        F(0.9238795325 / C6),  F(0.8314696123 / C7),
+       -F(0.3826834324 / C6), -F(0.9807852804 / C7),
+       -F(0.3826834324 / C6),  F(0.9807852804 / C7),
+        F(0.9238795325 / C6), -F(0.8314696123 / C7),
+       -F(0.9238795325 / C6),  F(0.5555702330 / C7),
+        F(0.3826834324 / C6), -F(0.1950903220 / C7),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+};
diff --git a/sbc/sbcdec.c b/sbc/sbcdec.c
new file mode 100644 (file)
index 0000000..7008e88
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) decoder
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#include "sbc.h"
+#include "formats.h"
+
+#define BUF_SIZE 8192
+
+static int verbose = 0;
+
+static void decode(char *filename, char *output, int tofile)
+{
+       unsigned char buf[BUF_SIZE], *stream;
+       struct stat st;
+       sbc_t sbc;
+       int fd, ad, pos, streamlen, framelen, count;
+       size_t len;
+       int format = AFMT_S16_BE, frequency, channels;
+       ssize_t written;
+
+       if (stat(filename, &st) < 0) {
+               fprintf(stderr, "Can't get size of file %s: %s\n",
+                                               filename, strerror(errno));
+               return;
+       }
+
+       stream = malloc(st.st_size);
+
+       if (!stream) {
+               fprintf(stderr, "Can't allocate memory for %s: %s\n",
+                                               filename, strerror(errno));
+               return;
+       }
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "Can't open file %s: %s\n",
+                                               filename, strerror(errno));
+               goto free;
+       }
+
+       if (read(fd, stream, st.st_size) != st.st_size) {
+               fprintf(stderr, "Can't read content of %s: %s\n",
+                                               filename, strerror(errno));
+               close(fd);
+               goto free;
+       }
+
+       close(fd);
+
+       pos = 0;
+       streamlen = st.st_size;
+
+       if (tofile)
+               ad = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+       else
+               ad = open(output, O_WRONLY, 0);
+
+       if (ad < 0) {
+               fprintf(stderr, "Can't open output %s: %s\n",
+                                               output, strerror(errno));
+               goto free;
+       }
+
+       sbc_init(&sbc, 0L);
+       sbc.endian = SBC_BE;
+
+       framelen = sbc_decode(&sbc, stream, streamlen, buf, sizeof(buf), &len);
+       channels = sbc.mode == SBC_MODE_MONO ? 1 : 2;
+       switch (sbc.frequency) {
+       case SBC_FREQ_16000:
+               frequency = 16000;
+               break;
+
+       case SBC_FREQ_32000:
+               frequency = 32000;
+               break;
+
+       case SBC_FREQ_44100:
+               frequency = 44100;
+               break;
+
+       case SBC_FREQ_48000:
+               frequency = 48000;
+               break;
+       default:
+               frequency = 0;
+       }
+
+       if (verbose) {
+               fprintf(stderr,"decoding %s with rate %d, %d subbands, "
+                       "%d bits, allocation method %s and mode %s\n",
+                       filename, frequency, sbc.subbands * 4 + 4, sbc.bitpool,
+                       sbc.allocation == SBC_AM_SNR ? "SNR" : "LOUDNESS",
+                       sbc.mode == SBC_MODE_MONO ? "MONO" :
+                                       sbc.mode == SBC_MODE_STEREO ?
+                                               "STEREO" : "JOINTSTEREO");
+       }
+
+       if (tofile) {
+               struct au_header au_hdr;
+
+               au_hdr.magic       = AU_MAGIC;
+               au_hdr.hdr_size    = BE_INT(24);
+               au_hdr.data_size   = BE_INT(0);
+               au_hdr.encoding    = BE_INT(AU_FMT_LIN16);
+               au_hdr.sample_rate = BE_INT(frequency);
+               au_hdr.channels    = BE_INT(channels);
+
+               written = write(ad, &au_hdr, sizeof(au_hdr));
+               if (written < (ssize_t) sizeof(au_hdr)) {
+                       fprintf(stderr, "Failed to write header\n");
+                       goto close;
+               }
+       } else {
+               if (ioctl(ad, SNDCTL_DSP_SETFMT, &format) < 0) {
+                       fprintf(stderr, "Can't set audio format on %s: %s\n",
+                                               output, strerror(errno));
+                       goto close;
+               }
+
+               if (ioctl(ad, SNDCTL_DSP_CHANNELS, &channels) < 0) {
+                       fprintf(stderr, "Can't set number of channels on %s: %s\n",
+                                               output, strerror(errno));
+                       goto close;
+               }
+
+               if (ioctl(ad, SNDCTL_DSP_SPEED, &frequency) < 0) {
+                       fprintf(stderr, "Can't set audio rate on %s: %s\n",
+                                               output, strerror(errno));
+                       goto close;
+               }
+       }
+
+       count = len;
+
+       while (framelen > 0) {
+               /* we have completed an sbc_decode at this point sbc.len is the
+                * length of the frame we just decoded count is the number of
+                * decoded bytes yet to be written */
+
+               if (count + len >= BUF_SIZE) {
+                       /* buffer is too full to stuff decoded audio in so it
+                        * must be written to the device */
+                       written = write(ad, buf, count);
+                       if (written > 0)
+                               count -= written;
+               }
+
+               /* sanity check */
+               if (count + len >= BUF_SIZE) {
+                       fprintf(stderr,
+                               "buffer size of %d is too small for decoded"
+                               " data (%lu)\n", BUF_SIZE, (unsigned long) (len + count));
+                       exit(1);
+               }
+
+               /* push the pointer in the file forward to the next bit to be
+                * decoded tell the decoder to decode up to the remaining
+                * length of the file (!) */
+               pos += framelen;
+               framelen = sbc_decode(&sbc, stream + pos, streamlen - pos,
+                                       buf + count, sizeof(buf) - count, &len);
+
+               /* increase the count */
+               count += len;
+       }
+
+       if (count > 0) {
+               written = write(ad, buf, count);
+               if (written > 0)
+                       count -= written;
+       }
+
+close:
+       sbc_finish(&sbc);
+
+       close(ad);
+
+free:
+       free(stream);
+}
+
+static void usage(void)
+{
+       printf("SBC decoder utility ver %s\n", VERSION);
+       printf("Copyright (c) 2004-2010  Marcel Holtmann\n\n");
+
+       printf("Usage:\n"
+               "\tsbcdec [options] file(s)\n"
+               "\n");
+
+       printf("Options:\n"
+               "\t-h, --help           Display help\n"
+               "\t-v, --verbose        Verbose mode\n"
+               "\t-d, --device <dsp>   Sound device\n"
+               "\t-f, --file <file>    Decode to a file\n"
+               "\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "device",     1, 0, 'd' },
+       { "verbose",    0, 0, 'v' },
+       { "file",       1, 0, 'f' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       char *output = NULL;
+       int i, opt, tofile = 0;
+
+       while ((opt = getopt_long(argc, argv, "+hvd:f:",
+                                               main_options, NULL)) != -1) {
+               switch(opt) {
+               case 'h':
+                       usage();
+                       exit(0);
+
+               case 'v':
+                       verbose = 1;
+                       break;
+
+               case 'd':
+                       free(output);
+                       output = strdup(optarg);
+                       tofile = 0;
+                       break;
+
+               case 'f' :
+                       free(output);
+                       output = strdup(optarg);
+                       tofile = 1;
+                       break;
+
+               default:
+                       exit(1);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       for (i = 0; i < argc; i++)
+               decode(argv[i], output ? output : "/dev/dsp", tofile);
+
+       free(output);
+
+       return 0;
+}
diff --git a/sbc/sbcenc.c b/sbc/sbcenc.c
new file mode 100644 (file)
index 0000000..3d3a7dc
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) encoder
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/stat.h>
+
+#include "sbc.h"
+#include "formats.h"
+
+static int verbose = 0;
+
+#define BUF_SIZE 32768
+static unsigned char input[BUF_SIZE], output[BUF_SIZE + BUF_SIZE / 4];
+
+static void encode(char *filename, int subbands, int bitpool, int joint,
+                                       int dualchannel, int snr, int blocks)
+{
+       struct au_header au_hdr;
+       sbc_t sbc;
+       int fd, size, srate, codesize, nframes;
+       ssize_t encoded;
+       ssize_t len;
+
+       if (sizeof(au_hdr) != 24) {
+               /* Sanity check just in case */
+               fprintf(stderr, "FIXME: sizeof(au_hdr) != 24\n");
+               return;
+       }
+
+       if (strcmp(filename, "-")) {
+               fd = open(filename, O_RDONLY);
+               if (fd < 0) {
+                       fprintf(stderr, "Can't open file %s: %s\n",
+                                               filename, strerror(errno));
+                       return;
+               }
+       } else
+               fd = fileno(stdin);
+
+       len = read(fd, &au_hdr, sizeof(au_hdr));
+       if (len < (ssize_t) sizeof(au_hdr)) {
+               if (fd > fileno(stderr))
+                       fprintf(stderr, "Can't read header from file %s: %s\n",
+                                               filename, strerror(errno));
+               else
+                       perror("Can't read audio header");
+               goto done;
+       }
+
+       if (au_hdr.magic != AU_MAGIC ||
+                       BE_INT(au_hdr.hdr_size) > 128 ||
+                       BE_INT(au_hdr.hdr_size) < sizeof(au_hdr) ||
+                       BE_INT(au_hdr.encoding) != AU_FMT_LIN16) {
+               fprintf(stderr, "Not in Sun/NeXT audio S16_BE format\n");
+               goto done;
+       }
+
+       sbc_init(&sbc, 0L);
+
+       switch (BE_INT(au_hdr.sample_rate)) {
+       case 16000:
+               sbc.frequency = SBC_FREQ_16000;
+               break;
+       case 32000:
+               sbc.frequency = SBC_FREQ_32000;
+               break;
+       case 44100:
+               sbc.frequency = SBC_FREQ_44100;
+               break;
+       case 48000:
+               sbc.frequency = SBC_FREQ_48000;
+               break;
+       }
+
+       srate = BE_INT(au_hdr.sample_rate);
+
+       sbc.subbands = subbands == 4 ? SBC_SB_4 : SBC_SB_8;
+
+       if (BE_INT(au_hdr.channels) == 1) {
+               sbc.mode = SBC_MODE_MONO;
+               if (joint || dualchannel) {
+                       fprintf(stderr, "Audio is mono but joint or "
+                               "dualchannel mode has been specified\n");
+                       goto done;
+               }
+       } else if (joint && !dualchannel)
+               sbc.mode = SBC_MODE_JOINT_STEREO;
+       else if (!joint && dualchannel)
+               sbc.mode = SBC_MODE_DUAL_CHANNEL;
+       else if (!joint && !dualchannel)
+               sbc.mode = SBC_MODE_STEREO;
+       else {
+               fprintf(stderr, "Both joint and dualchannel mode have been "
+                                                               "specified\n");
+               goto done;
+       }
+
+       sbc.endian = SBC_BE;
+       /* Skip extra bytes of the header if any */
+       if (read(fd, input, BE_INT(au_hdr.hdr_size) - len) < 0)
+               goto done;
+
+       sbc.bitpool = bitpool;
+       sbc.allocation = snr ? SBC_AM_SNR : SBC_AM_LOUDNESS;
+       sbc.blocks = blocks == 4 ? SBC_BLK_4 :
+                       blocks == 8 ? SBC_BLK_8 :
+                               blocks == 12 ? SBC_BLK_12 : SBC_BLK_16;
+
+       if (verbose) {
+               fprintf(stderr, "encoding %s with rate %d, %d blocks, "
+                       "%d subbands, %d bits, allocation method %s, "
+                                                       "and mode %s\n",
+                       filename, srate, blocks, subbands, bitpool,
+                       sbc.allocation == SBC_AM_SNR ? "SNR" : "LOUDNESS",
+                       sbc.mode == SBC_MODE_MONO ? "MONO" :
+                                       sbc.mode == SBC_MODE_STEREO ?
+                                               "STEREO" : "JOINTSTEREO");
+       }
+
+       codesize = sbc_get_codesize(&sbc);
+       nframes = sizeof(input) / codesize;
+       while (1) {
+               unsigned char *inp, *outp;
+               /* read data for up to 'nframes' frames of input data */
+               size = read(fd, input, codesize * nframes);
+               if (size < 0) {
+                       /* Something really bad happened */
+                       perror("Can't read audio data");
+                       break;
+               }
+               if (size < codesize) {
+                       /* Not enough data for encoding even a single frame */
+                       break;
+               }
+               /* encode all the data from the input buffer in a loop */
+               inp = input;
+               outp = output;
+               while (size >= codesize) {
+                       len = sbc_encode(&sbc, inp, codesize,
+                               outp, sizeof(output) - (outp - output),
+                               &encoded);
+                       if (len != codesize || encoded <= 0) {
+                               fprintf(stderr,
+                                       "sbc_encode fail, len=%zd, encoded=%lu\n",
+                                       len, (unsigned long) encoded);
+                               break;
+                       }
+                       size -= len;
+                       inp += len;
+                       outp += encoded;
+               }
+               len = write(fileno(stdout), output, outp - output);
+               if (len != outp - output) {
+                       perror("Can't write SBC output");
+                       break;
+               }
+               if (size != 0) {
+                       /*
+                        * sbc_encode failure has been detected earlier or end
+                        * of file reached (have trailing partial data which is
+                        * insufficient to encode SBC frame)
+                        */
+                       break;
+               }
+       }
+
+       sbc_finish(&sbc);
+
+done:
+       if (fd > fileno(stderr))
+               close(fd);
+}
+
+static void usage(void)
+{
+       printf("SBC encoder utility ver %s\n", VERSION);
+       printf("Copyright (c) 2004-2010  Marcel Holtmann\n\n");
+
+       printf("Usage:\n"
+               "\tsbcenc [options] file(s)\n"
+               "\n");
+
+       printf("Options:\n"
+               "\t-h, --help           Display help\n"
+               "\t-v, --verbose        Verbose mode\n"
+               "\t-s, --subbands       Number of subbands to use (4 or 8)\n"
+               "\t-b, --bitpool        Bitpool value (default is 32)\n"
+               "\t-j, --joint          Joint stereo\n"
+               "\t-d, --dualchannel    Dual channel\n"
+               "\t-S, --snr            Use SNR mode (default is loudness)\n"
+               "\t-B, --blocks         Number of blocks (4, 8, 12 or 16)\n"
+               "\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "verbose",    0, 0, 'v' },
+       { "subbands",   1, 0, 's' },
+       { "bitpool",    1, 0, 'b' },
+       { "joint",      0, 0, 'j' },
+       { "dualchannel",0, 0, 'd' },
+       { "snr",        0, 0, 'S' },
+       { "blocks",     1, 0, 'B' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       int i, opt, subbands = 8, bitpool = 32, joint = 0, dualchannel = 0;
+       int snr = 0, blocks = 16;
+
+       while ((opt = getopt_long(argc, argv, "+hvs:b:jdSB:",
+                                               main_options, NULL)) != -1) {
+               switch(opt) {
+               case 'h':
+                       usage();
+                       exit(0);
+
+               case 'v':
+                       verbose = 1;
+                       break;
+
+               case 's':
+                       subbands = atoi(optarg);
+                       if (subbands != 8 && subbands != 4) {
+                               fprintf(stderr, "Invalid subbands\n");
+                               exit(1);
+                       }
+                       break;
+
+               case 'b':
+                       bitpool = atoi(optarg);
+                       break;
+
+               case 'j':
+                       joint = 1;
+                       break;
+
+               case 'd':
+                       dualchannel = 1;
+                       break;
+
+               case 'S':
+                       snr = 1;
+                       break;
+
+               case 'B':
+                       blocks = atoi(optarg);
+                       if (blocks != 16 && blocks != 12 &&
+                                               blocks != 8 && blocks != 4) {
+                               fprintf(stderr, "Invalid blocks\n");
+                               exit(1);
+                       }
+                       break;
+
+               default:
+                       usage();
+                       exit(1);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       for (i = 0; i < argc; i++)
+               encode(argv[i], subbands, bitpool, joint, dualchannel,
+                                                               snr, blocks);
+
+       return 0;
+}
diff --git a/sbc/sbcinfo.c b/sbc/sbcinfo.c
new file mode 100644 (file)
index 0000000..8cfb54a
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <libgen.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+struct sbc_frame_hdr {
+       uint8_t syncword:8;             /* Sync word */
+       uint8_t subbands:1;             /* Subbands */
+       uint8_t allocation_method:1;    /* Allocation method */
+       uint8_t channel_mode:2;         /* Channel mode */
+       uint8_t blocks:2;               /* Blocks */
+       uint8_t sampling_frequency:2;   /* Sampling frequency */
+       uint8_t bitpool:8;              /* Bitpool */
+       uint8_t crc_check:8;            /* CRC check */
+} __attribute__ ((packed));
+#elif __BYTE_ORDER == __BIG_ENDIAN
+struct sbc_frame_hdr {
+       uint8_t syncword:8;             /* Sync word */
+       uint8_t sampling_frequency:2;   /* Sampling frequency */
+       uint8_t blocks:2;               /* Blocks */
+       uint8_t channel_mode:2;         /* Channel mode */
+       uint8_t allocation_method:1;    /* Allocation method */
+       uint8_t subbands:1;             /* Subbands */
+       uint8_t bitpool:8;              /* Bitpool */
+       uint8_t crc_check:8;            /* CRC check */
+} __attribute__ ((packed));
+#else
+#error "Unknown byte order"
+#endif
+
+static int calc_frame_len(struct sbc_frame_hdr *hdr)
+{
+       int tmp, nrof_subbands, nrof_blocks;
+
+       nrof_subbands = (hdr->subbands + 1) * 4;
+       nrof_blocks = (hdr->blocks + 1) * 4;
+
+       switch (hdr->channel_mode) {
+       case 0x00:
+               nrof_subbands /= 2;
+               tmp = nrof_blocks * hdr->bitpool;
+               break;
+       case 0x01:
+               tmp = nrof_blocks * hdr->bitpool * 2;
+               break;
+       case 0x02:
+               tmp = nrof_blocks * hdr->bitpool;
+               break;
+       case 0x03:
+               tmp = nrof_blocks * hdr->bitpool + nrof_subbands;
+               break;
+       default:
+               return 0;
+       }
+
+       return (nrof_subbands + ((tmp + 7) / 8));
+}
+
+static double calc_bit_rate(struct sbc_frame_hdr *hdr)
+{
+       int nrof_subbands, nrof_blocks;
+       double f;
+
+       nrof_subbands = (hdr->subbands + 1) * 4;
+       nrof_blocks = (hdr->blocks + 1) * 4;
+
+       switch (hdr->sampling_frequency) {
+       case 0:
+               f = 16;
+               break;
+       case 1:
+               f = 32;
+               break;
+       case 2:
+               f = 44.1;
+               break;
+       case 3:
+               f = 48;
+               break;
+       default:
+               return 0;
+       }
+
+       return ((8 * (calc_frame_len(hdr) + 4) * f) /
+                       (nrof_subbands * nrof_blocks));
+}
+
+static char *freq2str(uint8_t freq)
+{
+       switch (freq) {
+       case 0:
+               return "16 kHz";
+       case 1:
+               return "32 kHz";
+       case 2:
+               return "44.1 kHz";
+       case 3:
+               return "48 kHz";
+       default:
+               return "Unknown";
+       }
+}
+
+static char *mode2str(uint8_t mode)
+{
+       switch (mode) {
+       case 0:
+               return "Mono";
+       case 1:
+               return "Dual Channel";
+       case 2:
+               return "Stereo";
+       case 3:
+               return "Joint Stereo";
+       default:
+               return "Unknown";
+       }
+}
+
+static ssize_t __read(int fd, void *buf, size_t count)
+{
+       ssize_t len, pos = 0;
+
+       while (count > 0) {
+               len = read(fd, buf + pos, count);
+               if (len <= 0)
+                       return len;
+
+               count -= len;
+               pos   += len;
+       }
+
+       return pos;
+}
+
+#define SIZE 32
+
+static int analyze_file(char *filename)
+{
+       struct sbc_frame_hdr hdr;
+       unsigned char buf[64];
+       double rate;
+       int bitpool[SIZE], frame_len[SIZE];
+       int subbands, blocks, freq, method;
+       int n, p1, p2, fd, size, num;
+       ssize_t len;
+       unsigned int count;
+
+       if (strcmp(filename, "-")) {
+               printf("Filename\t\t%s\n", basename(filename));
+
+               fd = open(filename, O_RDONLY);
+               if (fd < 0) {
+                       perror("Can't open file");
+                       return -1;
+               }
+       } else
+               fd = fileno(stdin);
+
+       len = __read(fd, &hdr, sizeof(hdr));
+       if (len != sizeof(hdr) || hdr.syncword != 0x9c) {
+               fprintf(stderr, "Not a SBC audio file\n");
+               return -1;
+       }
+
+       subbands = (hdr.subbands + 1) * 4;
+       blocks = (hdr.blocks + 1) * 4;
+       freq = hdr.sampling_frequency;
+       method = hdr.allocation_method;
+
+       count = calc_frame_len(&hdr);
+
+       bitpool[0] = hdr.bitpool;
+       frame_len[0] = count + 4;
+
+       for (n = 1; n < SIZE; n++) {
+               bitpool[n] = 0;
+               frame_len[n] = 0;
+       }
+
+       if (lseek(fd, 0, SEEK_SET) < 0) {
+               num = 1;
+               rate = calc_bit_rate(&hdr);
+               while (count) {
+                       size = count > sizeof(buf) ? sizeof(buf) : count;
+                       len = __read(fd, buf, size);
+                       if (len < 0)
+                               break;
+                       count -= len;
+               }
+       } else {
+               num = 0;
+               rate = 0;
+       }
+
+       while (1) {
+               len = __read(fd, &hdr, sizeof(hdr));
+               if (len < 0) {
+                       fprintf(stderr, "Unable to read frame header"
+                                       " (error %d)\n", errno);
+                       break;
+               }
+
+               if (len == 0)
+                       break;
+
+               if ((size_t) len < sizeof(hdr) || hdr.syncword != 0x9c) {
+                       fprintf(stderr, "Corrupted SBC stream "
+                                       "(len %zd syncword 0x%02x)\n",
+                                       len, hdr.syncword);
+                       break;
+               }
+
+               count = calc_frame_len(&hdr);
+               len = count + 4;
+
+               p1 = -1;
+               p2 = -1;
+               for (n = 0; n < SIZE; n++) {
+                       if (p1 < 0 && (bitpool[n] == 0 || bitpool[n] == hdr.bitpool))
+                               p1 = n;
+                       if (p2 < 0 && (frame_len[n] == 0 || frame_len[n] == len))
+                               p2 = n;
+               }
+               if (p1 >= 0)
+                       bitpool[p1] = hdr.bitpool;
+               if (p2 >= 0)
+                       frame_len[p2] = len;
+
+               while (count) {
+                       size = count > sizeof(buf) ? sizeof(buf) : count;
+
+                       len = __read(fd, buf, size);
+                       if (len != size) {
+                               fprintf(stderr, "Unable to read frame data "
+                                               "(error %d)\n", errno);
+                               break;
+                       }
+
+                       count -= len;
+               }
+
+               rate += calc_bit_rate(&hdr);
+               num++;
+       }
+
+       printf("Subbands\t\t%d\n", subbands);
+       printf("Block length\t\t%d\n", blocks);
+       printf("Sampling frequency\t%s\n", freq2str(freq));
+       printf("Channel mode\t\t%s\n", mode2str(hdr.channel_mode));
+       printf("Allocation method\t%s\n", method ? "SNR" : "Loudness");
+       printf("Bitpool\t\t\t%d", bitpool[0]);
+       for (n = 1; n < SIZE; n++)
+               if (bitpool[n] > 0)
+                       printf(", %d", bitpool[n]);
+       printf("\n");
+       printf("Number of frames\t%d\n", num);
+       printf("Frame length\t\t%d", frame_len[0]);
+       for (n = 1; n < SIZE; n++)
+               if (frame_len[n] > 0)
+                       printf(", %d", frame_len[n]);
+       printf(" Bytes\n");
+       if (num > 0)
+               printf("Bit rate\t\t%.3f kbps\n", rate / num);
+
+       if (fd > fileno(stderr))
+               close(fd);
+
+       printf("\n");
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int i;
+
+       if (argc < 2) {
+               fprintf(stderr, "Usage: sbcinfo <file>\n");
+               exit(1);
+       }
+
+       for (i = 0; i < argc - 1; i++)
+               if (analyze_file(argv[i + 1]) < 0)
+                       exit(1);
+
+       return 0;
+}
diff --git a/sbc/sbctester.c b/sbc/sbctester.c
new file mode 100644 (file)
index 0000000..c08c22a
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ *
+ *  Bluetooth low-complexity, subband codec (SBC) library
+ *
+ *  Copyright (C) 2008-2010  Nokia Corporation
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2007-2008  Frederic Dalleau <fdalleau@free.fr>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sndfile.h>
+#include <math.h>
+#include <string.h>
+
+#define MAXCHANNELS 2
+#define DEFACCURACY 7
+
+static double sampletobits(short sample16, int verbose)
+{
+       double bits = 0;
+       unsigned short bit;
+       int i;
+
+       if (verbose)
+               printf("===> sampletobits(%hd, %04hX)\n", sample16, sample16);
+
+       /* Bit 0 is MSB */
+       if (sample16 < 0)
+               bits = -1;
+
+       if (verbose)
+               printf("%d", (sample16 < 0) ? 1 : 0);
+
+       /* Bit 15 is LSB */
+       for (i = 1; i < 16; i++) {
+               bit = (unsigned short) sample16;
+               bit >>= 15 - i;
+               bit %= 2;
+
+               if (verbose)
+                       printf("%d", bit);
+
+               if (bit)
+                       bits += (1.0 / pow(2.0, i));
+       }
+
+       if (verbose)
+               printf("\n");
+
+       return bits;
+}
+
+static int calculate_rms_level(SNDFILE * sndref, SF_INFO * infosref,
+                               SNDFILE * sndtst, SF_INFO * infostst,
+                                               int accuracy, char *csvname)
+{
+       short refsample[MAXCHANNELS], tstsample[MAXCHANNELS];
+       double refbits, tstbits;
+       double rms_accu[MAXCHANNELS];
+       double rms_level[MAXCHANNELS];
+       double rms_limit = 1.0 / (pow(2.0, accuracy - 1) * pow(12.0, 0.5));
+       FILE *csv = NULL;
+       int i, j, r1, r2, verdict;
+
+       if (csvname)
+               csv = fopen(csvname, "wt");
+
+       if (csv) {
+               fprintf(csv, "num;");
+               for (j = 0; j < infostst->channels; j++)
+                       fprintf(csv, "ref channel %d;tst channel %d;", j, j);
+               fprintf(csv, "\r\n");
+       }
+
+       sf_seek(sndref, 0, SEEK_SET);
+       sf_seek(sndtst, 0, SEEK_SET);
+
+       memset(rms_accu, 0, sizeof(rms_accu));
+       memset(rms_level, 0, sizeof(rms_level));
+
+       for (i = 0; i < infostst->frames; i++) {
+               if (csv)
+                       fprintf(csv, "%d;", i);
+
+               r1 = sf_read_short(sndref, refsample, infostst->channels);
+               if (r1 != infostst->channels) {
+                       printf("Failed to read reference data: %s "
+                                       "(r1=%d, channels=%d)",
+                                       sf_strerror(sndref), r1,
+                                       infostst->channels);
+                       if (csv)
+                               fclose(csv);
+                       return -1;
+               }
+
+               r2 = sf_read_short(sndtst, tstsample, infostst->channels);
+               if (r2 != infostst->channels) {
+                       printf("Failed to read test data: %s "
+                                       "(r2=%d, channels=%d)\n",
+                                       sf_strerror(sndtst), r2,
+                                       infostst->channels);
+                       if (csv)
+                               fclose(csv);
+                       return -1;
+               }
+
+               for (j = 0; j < infostst->channels; j++) {
+                       if (csv)
+                               fprintf(csv, "%d;%d;", refsample[j],
+                                               tstsample[j]);
+
+                       refbits = sampletobits(refsample[j], 0);
+                       tstbits = sampletobits(tstsample[j], 0);
+
+                       rms_accu[j] += pow(tstbits - refbits, 2.0);
+               }
+
+               if (csv)
+                       fprintf(csv, "\r\n");
+       }
+
+       printf("Limit: %f\n", rms_limit);
+
+       for (j = 0; j < infostst->channels; j++) {
+               printf("Channel %d\n", j);
+               printf("Accumulated %f\n", rms_accu[j]);
+               rms_accu[j] /= (double) infostst->frames;
+               printf("Accumulated / %f = %f\n", (double) infostst->frames,
+                               rms_accu[j]);
+               rms_level[j] = sqrt(rms_accu[j]);
+               printf("Level = %f (%f x %f = %f)\n",
+                               rms_level[j], rms_level[j], rms_level[j],
+                                               rms_level[j] * rms_level[j]);
+       }
+
+       verdict = 1;
+
+       for (j = 0; j < infostst->channels; j++) {
+               printf("Channel %d: %f\n", j, rms_level[j]);
+
+               if (rms_level[j] > rms_limit)
+                       verdict = 0;
+       }
+
+       printf("%s return %d\n", __FUNCTION__, verdict);
+
+       return verdict;
+}
+
+static int check_absolute_diff(SNDFILE * sndref, SF_INFO * infosref,
+                               SNDFILE * sndtst, SF_INFO * infostst,
+                               int accuracy)
+{
+       short refsample[MAXCHANNELS], tstsample[MAXCHANNELS];
+       short refmax[MAXCHANNELS], tstmax[MAXCHANNELS];
+       double refbits, tstbits;
+       double rms_absolute = 1.0 / (pow(2, accuracy - 2));
+       double calc_max[MAXCHANNELS];
+       int calc_count = 0;
+       short r1, r2;
+       double cur_diff;
+       int i, j, verdict;
+
+       memset(&refmax, 0, sizeof(refmax));
+       memset(&tstmax, 0, sizeof(tstmax));
+       memset(&calc_max, 0, sizeof(calc_max));
+       memset(&refsample, 0, sizeof(refsample));
+       memset(&tstsample, 0, sizeof(tstsample));
+
+       sf_seek(sndref, 0, SEEK_SET);
+       sf_seek(sndtst, 0, SEEK_SET);
+
+       verdict = 1;
+
+       printf("Absolute max: %f\n", rms_absolute);
+       for (i = 0; i < infostst->frames; i++) {
+               r1 = sf_read_short(sndref, refsample, infostst->channels);
+
+               if (r1 != infostst->channels) {
+                       printf("Failed to read reference data: %s "
+                                       "(r1=%d, channels=%d)",
+                                       sf_strerror(sndref), r1,
+                                       infostst->channels);
+                       return -1;
+               }
+
+               r2 = sf_read_short(sndtst, tstsample, infostst->channels);
+               if (r2 != infostst->channels) {
+                       printf("Failed to read test data: %s "
+                                       "(r2=%d, channels=%d)\n",
+                                       sf_strerror(sndtst), r2,
+                                       infostst->channels);
+                       return -1;
+               }
+
+               for (j = 0; j < infostst->channels; j++) {
+                       refbits = sampletobits(refsample[j], 0);
+                       tstbits = sampletobits(tstsample[j], 0);
+
+                       cur_diff = fabs(tstbits - refbits);
+
+                       if (cur_diff > rms_absolute) {
+                               calc_count++;
+                               /* printf("Channel %d exceeded : fabs(%f - %f) = %f > %f\n", j, tstbits, refbits, cur_diff, rms_absolute); */
+                               verdict = 0;
+                       }
+
+                       if (cur_diff > calc_max[j]) {
+                               calc_max[j] = cur_diff;
+                               refmax[j] = refsample[j];
+                               tstmax[j] = tstsample[j];
+                       }
+               }
+       }
+
+       for (j = 0; j < infostst->channels; j++) {
+               printf("Calculated max: %f (%hd-%hd=%hd)\n",
+                       calc_max[j], tstmax[j], refmax[j],
+                       tstmax[j] - refmax[j]);
+       }
+
+       printf("%s return %d\n", __FUNCTION__, verdict);
+
+       return verdict;
+}
+
+static void usage(void)
+{
+       printf("SBC conformance test ver %s\n", VERSION);
+       printf("Copyright (c) 2007-2010  Marcel Holtmann\n");
+       printf("Copyright (c) 2007-2008  Frederic Dalleau\n\n");
+
+       printf("Usage:\n"
+               "\tsbctester reference.wav checkfile.wav\n"
+               "\tsbctester integer\n"
+               "\n");
+
+       printf("To test the encoder:\n");
+       printf("\tUse a reference codec to encode original.wav to reference.sbc\n");
+       printf("\tUse sbcenc to encode original.wav to checkfile.sbc\n");
+       printf("\tDecode both file using the reference decoder\n");
+       printf("\tRun sbctester with these two wav files to get the result\n\n");
+
+       printf("\tA file called out.csv is generated to use the data in a\n");
+       printf("\tspreadsheet application or database.\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+       SNDFILE *sndref = NULL;
+       SNDFILE *sndtst = NULL;
+       SF_INFO infosref;
+       SF_INFO infostst;
+       char *ref;
+       char *tst;
+       int pass_rms, pass_absolute, pass, accuracy;
+
+       if (argc == 2) {
+               double db;
+
+               printf("Test sampletobits\n");
+               db = sampletobits((short) atoi(argv[1]), 1);
+               printf("db = %f\n", db);
+               exit(0);
+       }
+
+       if (argc < 3) {
+               usage();
+               exit(1);
+       }
+
+       ref = argv[1];
+       tst = argv[2];
+
+       printf("opening reference %s\n", ref);
+
+       sndref = sf_open(ref, SFM_READ, &infosref);
+       if (!sndref) {
+               printf("Failed to open reference file\n");
+               exit(1);
+       }
+
+       printf("opening testfile %s\n", tst);
+       sndtst = sf_open(tst, SFM_READ, &infostst);
+       if (!sndtst) {
+               printf("Failed to open test file\n");
+               sf_close(sndref);
+               exit(1);
+       }
+
+       printf("reference:\n\t%d frames,\n\t%d hz,\n\t%d channels\n",
+               (int) infosref.frames, (int) infosref.samplerate,
+               (int) infosref.channels);
+       printf("testfile:\n\t%d frames,\n\t%d hz,\n\t%d channels\n",
+               (int) infostst.frames, (int) infostst.samplerate,
+               (int) infostst.channels);
+
+       /* check number of channels */
+       if (infosref.channels > 2 || infostst.channels > 2) {
+               printf("Too many channels\n");
+               goto error;
+       }
+
+       /* compare number of samples */
+       if (infosref.samplerate != infostst.samplerate ||
+                               infosref.channels != infostst.channels) {
+               printf("Cannot compare files with different charasteristics\n");
+               goto error;
+       }
+
+       accuracy = DEFACCURACY;
+       printf("Accuracy: %d\n", accuracy);
+
+       /* Condition 1 rms level */
+       pass_rms = calculate_rms_level(sndref, &infosref, sndtst, &infostst,
+                                       accuracy, "out.csv");
+       if (pass_rms < 0)
+               goto error;
+
+       /* Condition 2 absolute difference */
+       pass_absolute = check_absolute_diff(sndref, &infosref, sndtst,
+                                               &infostst, accuracy);
+       if (pass_absolute < 0)
+               goto error;
+
+       /* Verdict */
+       pass = pass_rms && pass_absolute;
+       printf("Verdict: %s\n", pass ? "pass" : "fail");
+
+       return 0;
+
+error:
+       sf_close(sndref);
+       sf_close(sndtst);
+
+       exit(1);
+}
diff --git a/scripts/bluetooth-hid2hci.rules b/scripts/bluetooth-hid2hci.rules
new file mode 100644 (file)
index 0000000..0687c8a
--- /dev/null
@@ -0,0 +1,28 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="hid2hci_end"
+SUBSYSTEM!="usb", GOTO="hid2hci_end"
+
+# Variety of Dell Bluetooth devices - match on a mouse device that is
+# self powered and where a HID report needs to be sent to switch modes
+# Known supported devices: 413c:8154, 413c:8158, 413c:8162
+ATTR{bInterfaceClass}=="03", ATTR{bInterfaceSubClass}=="01", ATTR{bInterfaceProtocol}=="02", \
+  ATTRS{bDeviceClass}=="00", ATTRS{idVendor}=="413c", ATTRS{bmAttributes}=="e0", \
+  RUN+="hid2hci --method=dell --devpath=%p", ENV{HID2HCI_SWITCH}="1"
+
+# Logitech devices
+KERNEL=="hiddev*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70[345abce]|c71[34bc]", \
+  RUN+="hid2hci --method=logitech-hid --devpath=%p"
+
+ENV{DEVTYPE}!="usb_device", GOTO="hid2hci_end"
+
+# When a Dell device recovers from S3, the mouse child needs to be repoked
+# Unfortunately the only event seen is the BT device disappearing, so the mouse
+# device needs to be chased down on the USB bus.
+ATTR{bDeviceClass}=="e0", ATTR{bDeviceSubClass}=="01", ATTR{bDeviceProtocol}=="01", ATTR{idVendor}=="413c", \
+  ENV{REMOVE_CMD}="/sbin/udevadm trigger --action=change --subsystem-match=usb --property-match=HID2HCI_SWITCH=1"
+
+# CSR devices
+ATTR{idVendor}=="0a12|0458|05ac", ATTR{idProduct}=="1000", RUN+="hid2hci --method=csr --devpath=%p"
+
+LABEL="hid2hci_end"
diff --git a/scripts/bluetooth-serial.rules b/scripts/bluetooth-serial.rules
new file mode 100644 (file)
index 0000000..072335f
--- /dev/null
@@ -0,0 +1,35 @@
+# Brain Boxes BL-620 Bluetooth Adapter
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="Brain Boxes", ATTRS{prod_id2}=="Bluetooth PC Card", ENV{HCIOPTS}="bboxes", RUN+="bluetooth_serial"
+
+# Xircom CreditCard Bluetooth Adapter
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="Xircom", ATTRS{prod_id3}=="CBT", ENV{HCIOPTS}="xircom", RUN+="bluetooth_serial"
+
+# Xircom RealPort2 Bluetooth Adapter
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="Xircom", ATTRS{prod_id3}=="CBT", ENV{HCIOPTS}="xircom", RUN+="bluetooth_serial"
+
+# IBM Bluetooth PC Card II
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="IBM", ATTRS{prod_id2}=="Bluetooth PC Card II", ENV{HCIOPTS}="tdk", RUN+="bluetooth_serial"
+
+# TDK Bluetooth PC Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="TDK", ATTRS{prod_id2}=="Bluetooth PC Card II", ENV{HCIOPTS}="tdk", RUN+="bluetooth_serial"
+
+# AmbiCom BT2000C Bluetooth PC/CF Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="AmbiCom BT2000C", ATTRS{prod_id2}=="Bluetooth PC/CF Card", ENV{HCIOPTS}="bt2000c", RUN+="bluetooth_serial"
+
+# COM One Platinium Bluetooth PC Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="COM1 SA", ATTRS{prod_id2}=="MC310 CARD", ENV{HCIOPTS}="comone", RUN+="bluetooth_serial"
+
+# Sphinx PICO Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="SPHINX", ATTRS{prod_id2}=="BT-CARD", ENV{HCIOPTS}="picocard", RUN+="bluetooth_serial"
+
+# H-Soft blue+Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="H-Soft", ATTRS{prod_id2}=="Blue+CARD", ENV{HCIOPTS}="$sysfs{manf_id},$sysfs{card_id}", RUN+="bluetooth_serial"
+
+# Compaq iPAQ Bluetooth Sleeve, Belkin F8T020, any other muppet who used an OXCF950 and didn't bother to program it appropriately.
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="CF CARD", ATTRS{prod_id2}=="GENERIC", ENV{HCIOPTS}="$sysfs{manf_id},$sysfs{card_id}", RUN+="bluetooth_serial"
+
+# Zoom Bluetooth Card and Sitecom CN-504 Card
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="PCMCIA", ATTRS{prod_id2}=="Bluetooth Card", ENV{HCIOPTS}="zoom", RUN+="bluetooth_serial"
+
+# CC&C BT0100M
+SUBSYSTEM=="tty", SUBSYSTEMS=="pcmcia", ATTRS{prod_id1}=="Bluetooth BT0100M", ENV{HCIOPTS}="bcsp 115200", RUN+="bluetooth_serial"
diff --git a/scripts/bluetooth_serial b/scripts/bluetooth_serial
new file mode 100644 (file)
index 0000000..e5be6c2
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# bluetooth_serial
+#
+# Bluetooth serial PCMCIA card initialization
+#
+
+start_serial()
+{
+       if [ ! -x /bin/setserial -o ! -x /usr/sbin/hciattach ]; then
+               logger "$0: setserial or hciattach not executable, cannot start $DEVNAME"
+               return 1
+       fi
+
+       if [ "$BAUDBASE" != "" ]; then
+               /bin/setserial $DEVNAME baud_base $BAUDBASE
+       fi
+
+       /usr/sbin/hciattach $DEVNAME $HCIOPTS 2>&1 | logger -t hciattach
+}
+
+stop_serial()
+{
+       [ -x /bin/fuser ] || return 1 
+
+       /bin/fuser -k -HUP $DEVNAME > /dev/null
+}
+
+case "$ACTION" in
+   add)
+       start_serial
+       ;;
+   remove)
+       stop_serial
+       ;;
+   *)
+       logger "Unknown action received $0: $ACTION"
+       ;;
+esac
diff --git a/serial/main.c b/serial/main.c
new file mode 100644 (file)
index 0000000..38ded03
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "manager.h"
+
+static DBusConnection *connection;
+
+static int serial_init(void)
+{
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -EIO;
+
+       if (serial_manager_init(connection) < 0) {
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void serial_exit(void)
+{
+       serial_manager_exit();
+
+       dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(serial, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, serial_init, serial_exit)
diff --git a/serial/manager.c b/serial/manager.c
new file mode 100644 (file)
index 0000000..438ba6c
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+
+#include "log.h"
+
+#include "error.h"
+#include "port.h"
+#include "proxy.h"
+#include "storage.h"
+#include "manager.h"
+#include "sdpd.h"
+#include "glib-helper.h"
+
+static DBusConnection *connection = NULL;
+
+static int serial_probe(struct btd_device *device, const char *uuid)
+{
+       struct btd_adapter *adapter = device_get_adapter(device);
+       const gchar *path = device_get_path(device);
+       sdp_list_t *protos;
+       int ch;
+       bdaddr_t src, dst;
+       const sdp_record_t *rec;
+
+       DBG("path %s: %s", path, uuid);
+
+       rec = btd_device_get_record(device, uuid);
+       if (!rec)
+               return -EINVAL;
+
+       if (sdp_get_access_protos(rec, &protos) < 0)
+               return -EINVAL;
+
+       ch = sdp_get_proto_port(protos, RFCOMM_UUID);
+       sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+       sdp_list_free(protos, NULL);
+
+       if (ch < 1 || ch > 30) {
+               error("Channel out of range: %d", ch);
+               return -EINVAL;
+       }
+
+       adapter_get_address(adapter, &src);
+       device_get_address(device, &dst, NULL);
+
+       return port_register(connection, path, &src, &dst, uuid, ch);
+}
+
+static void serial_remove(struct btd_device *device)
+{
+       const gchar *path = device_get_path(device);
+
+       DBG("path %s", path);
+
+       port_unregister(path);
+}
+
+
+static int port_probe(struct btd_device *device, GSList *uuids)
+{
+       while (uuids) {
+               serial_probe(device, uuids->data);
+               uuids = uuids->next;
+       }
+
+       return 0;
+}
+
+static void port_remove(struct btd_device *device)
+{
+       return serial_remove(device);
+}
+
+static struct btd_device_driver serial_port_driver = {
+       .name   = "serial-port",
+       .uuids  = BTD_UUIDS(RFCOMM_UUID_STR),
+       .probe  = port_probe,
+       .remove = port_remove,
+};
+
+static int proxy_probe(struct btd_adapter *adapter)
+{
+       const char *path = adapter_get_path(adapter);
+
+       DBG("path %s", path);
+
+       return proxy_register(connection, adapter);
+}
+
+static void proxy_remove(struct btd_adapter *adapter)
+{
+       const char *path = adapter_get_path(adapter);
+
+       DBG("path %s", path);
+
+       proxy_unregister(adapter);
+}
+
+static struct btd_adapter_driver serial_proxy_driver = {
+       .name   = "serial-proxy",
+       .probe  = proxy_probe,
+       .remove = proxy_remove,
+};
+
+int serial_manager_init(DBusConnection *conn)
+{
+       connection = dbus_connection_ref(conn);
+
+       btd_register_adapter_driver(&serial_proxy_driver);
+       btd_register_device_driver(&serial_port_driver);
+
+       return 0;
+}
+
+void serial_manager_exit(void)
+{
+       btd_unregister_device_driver(&serial_port_driver);
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
diff --git a/serial/manager.h b/serial/manager.h
new file mode 100644 (file)
index 0000000..c8b96e8
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int serial_manager_init(DBusConnection *conn);
+void serial_manager_exit(void);
diff --git a/serial/port.c b/serial/port.c
new file mode 100644 (file)
index 0000000..f90bb6a
--- /dev/null
@@ -0,0 +1,646 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/dbus-common.h"
+
+#include "log.h"
+#include "glib-helper.h"
+#include "sdp-client.h"
+#include "btio.h"
+
+#include "error.h"
+#include "manager.h"
+#include "adapter.h"
+#include "device.h"
+#include "storage.h"
+#include "port.h"
+
+#define SERIAL_PORT_INTERFACE  "org.bluez.Serial"
+
+#define MAX_OPEN_TRIES         5
+#define OPEN_WAIT              300     /* ms. udev node creation retry wait */
+
+struct serial_device {
+       DBusConnection  *conn;          /* for name listener handling */
+       bdaddr_t        src;            /* Source (local) address */
+       bdaddr_t        dst;            /* Destination address */
+       char            *path;          /* Device path */
+       GSList          *ports;         /* Available ports */
+};
+
+struct serial_port {
+       DBusMessage     *msg;           /* for name listener handling */
+       int16_t         id;             /* RFCOMM device id */
+       uint8_t         channel;        /* RFCOMM channel */
+       char            *uuid;          /* service identification */
+       char            *dev;           /* RFCOMM device name */
+       int             fd;             /* Opened file descriptor */
+       GIOChannel      *io;            /* BtIO channel */
+       guint           listener_id;
+       struct serial_device *device;
+};
+
+static GSList *devices = NULL;
+
+static struct serial_device *find_device(GSList *devices, const char *path)
+{
+       GSList *l;
+
+       for (l = devices; l != NULL; l = l->next) {
+               struct serial_device *device = l->data;
+
+               if (!strcmp(device->path, path))
+                       return device;
+       }
+
+       return NULL;
+}
+
+static struct serial_port *find_port(GSList *ports, const char *pattern)
+{
+       GSList *l;
+       int channel;
+       char *endptr = NULL;
+
+       channel = strtol(pattern, &endptr, 10);
+
+       for (l = ports; l != NULL; l = l->next) {
+               struct serial_port *port = l->data;
+               char *uuid_str;
+               int ret;
+
+               if (port->uuid && !strcasecmp(port->uuid, pattern))
+                       return port;
+
+               if (endptr && *endptr == '\0' && port->channel == channel)
+                       return port;
+
+               if (port->dev && !strcmp(port->dev, pattern))
+                       return port;
+
+               if (!port->uuid)
+                       continue;
+
+               uuid_str = bt_name2string(pattern);
+               if (!uuid_str)
+                       continue;
+
+               ret = strcasecmp(port->uuid, uuid_str);
+               g_free(uuid_str);
+               if (ret == 0)
+                       return port;
+       }
+
+       return NULL;
+}
+
+static int port_release(struct serial_port *port)
+{
+       struct rfcomm_dev_req req;
+       int rfcomm_ctl;
+       int err = 0;
+
+       if (port->id < 0) {
+               if (port->io) {
+                       g_io_channel_shutdown(port->io, TRUE, NULL);
+                       g_io_channel_unref(port->io);
+                       port->io = NULL;
+               } else
+                       bt_cancel_discovery(&port->device->src,
+                                               &port->device->dst);
+
+               return 0;
+       }
+
+       DBG("Serial port %s released", port->dev);
+
+       rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+       if (rfcomm_ctl < 0)
+               return -errno;
+
+       if (port->fd >= 0) {
+               close(port->fd);
+               port->fd = -1;
+       }
+
+       memset(&req, 0, sizeof(req));
+       req.dev_id = port->id;
+
+       /*
+        * We are hitting a kernel bug inside RFCOMM code when
+        * RFCOMM_HANGUP_NOW bit is set on request's flags passed to
+        * ioctl(RFCOMMRELEASEDEV)!
+        */
+       req.flags = (1 << RFCOMM_HANGUP_NOW);
+
+       if (ioctl(rfcomm_ctl, RFCOMMRELEASEDEV, &req) < 0) {
+               err = -errno;
+               error("Can't release device %s: %s (%d)",
+                               port->dev, strerror(-err), -err);
+       }
+
+       g_free(port->dev);
+       port->dev = NULL;
+       port->id = -1;
+       close(rfcomm_ctl);
+       return err;
+}
+
+static void serial_port_free(void *data)
+{
+       struct serial_port *port = data;
+       struct serial_device *device = port->device;
+
+       if (device && port->listener_id > 0)
+               g_dbus_remove_watch(device->conn, port->listener_id);
+
+       port_release(port);
+
+       g_free(port->uuid);
+       g_free(port);
+}
+
+static void serial_device_free(void *data)
+{
+       struct serial_device *device = data;
+
+       g_free(device->path);
+       if (device->conn)
+               dbus_connection_unref(device->conn);
+       g_free(device);
+}
+
+static void port_owner_exited(DBusConnection *conn, void *user_data)
+{
+       struct serial_port *port = user_data;
+
+       port_release(port);
+
+       port->listener_id = 0;
+}
+
+static void path_unregister(void *data)
+{
+       struct serial_device *device = data;
+
+       DBG("Unregistered interface %s on path %s", SERIAL_PORT_INTERFACE,
+               device->path);
+
+       devices = g_slist_remove(devices, device);
+       serial_device_free(device);
+}
+
+void port_release_all(void)
+{
+       g_slist_free_full(devices, serial_device_free);
+}
+
+static void open_notify(int fd, int err, struct serial_port *port)
+{
+       struct serial_device *device = port->device;
+       DBusMessage *reply;
+
+       if (err < 0) {
+               /* Max tries exceeded */
+               port_release(port);
+               reply = btd_error_failed(port->msg, strerror(-err));
+       } else {
+               port->fd = fd;
+               reply = g_dbus_create_reply(port->msg,
+                               DBUS_TYPE_STRING, &port->dev,
+                               DBUS_TYPE_INVALID);
+       }
+
+       /* Reply to the requestor */
+       g_dbus_send_message(device->conn, reply);
+}
+
+static gboolean open_continue(gpointer user_data)
+{
+       struct serial_port *port = user_data;
+       int fd;
+       static int ntries = MAX_OPEN_TRIES;
+
+       if (!port->listener_id)
+               return FALSE; /* Owner exited */
+
+       fd = open(port->dev, O_RDONLY | O_NOCTTY);
+       if (fd < 0) {
+               int err = -errno;
+               error("Could not open %s: %s (%d)",
+                               port->dev, strerror(-err), -err);
+               if (!--ntries) {
+                       /* Reporting error */
+                       open_notify(fd, err, port);
+                       ntries = MAX_OPEN_TRIES;
+                       return FALSE;
+               }
+               return TRUE;
+       }
+
+       /* Connection succeeded */
+       open_notify(fd, 0, port);
+       return FALSE;
+}
+
+static int port_open(struct serial_port *port)
+{
+       int fd;
+
+       fd = open(port->dev, O_RDONLY | O_NOCTTY);
+       if (fd < 0) {
+               g_timeout_add(OPEN_WAIT, open_continue, port);
+               return -EINPROGRESS;
+       }
+
+       return fd;
+}
+
+static void rfcomm_connect_cb(GIOChannel *chan, GError *conn_err,
+                                                       gpointer user_data)
+{
+       struct serial_port *port = user_data;
+       struct serial_device *device = port->device;
+       struct rfcomm_dev_req req;
+       int sk, fd;
+       DBusMessage *reply;
+
+       /* Owner exited? */
+       if (!port->listener_id)
+               return;
+
+       if (conn_err) {
+               error("%s", conn_err->message);
+               reply = btd_error_failed(port->msg, conn_err->message);
+               goto fail;
+       }
+
+       sk = g_io_channel_unix_get_fd(chan);
+
+       if (dbus_message_has_member(port->msg, "ConnectFD")) {
+               reply = g_dbus_create_reply(port->msg, DBUS_TYPE_UNIX_FD, &sk,
+                                                       DBUS_TYPE_INVALID);
+               g_dbus_send_message(device->conn, reply);
+
+               close(sk);
+
+               g_dbus_remove_watch(device->conn, port->listener_id);
+               port->listener_id = 0;
+
+               return;
+       }
+
+       memset(&req, 0, sizeof(req));
+       req.dev_id = -1;
+       req.flags = (1 << RFCOMM_REUSE_DLC);
+       bacpy(&req.src, &device->src);
+       bacpy(&req.dst, &device->dst);
+       req.channel = port->channel;
+
+       g_io_channel_unref(port->io);
+       port->io = NULL;
+
+       port->id = ioctl(sk, RFCOMMCREATEDEV, &req);
+       if (port->id < 0) {
+               int err = -errno;
+               error("ioctl(RFCOMMCREATEDEV): %s (%d)", strerror(-err), -err);
+               reply = btd_error_failed(port->msg, strerror(-err));
+               g_io_channel_shutdown(chan, TRUE, NULL);
+               goto fail;
+       }
+
+       port->dev = g_strdup_printf("/dev/rfcomm%d", port->id);
+
+       DBG("Serial port %s created", port->dev);
+
+       g_io_channel_shutdown(chan, TRUE, NULL);
+
+       /* Addressing connect port */
+       fd = port_open(port);
+       if (fd < 0)
+               /* Open in progress: Wait the callback */
+               return;
+
+       open_notify(fd, 0, port);
+       return;
+
+fail:
+       g_dbus_send_message(device->conn, reply);
+       g_dbus_remove_watch(device->conn, port->listener_id);
+       port->listener_id = 0;
+}
+
+static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+       struct serial_port *port = user_data;
+       struct serial_device *device = port->device;
+       sdp_record_t *record = NULL;
+       sdp_list_t *protos;
+       DBusMessage *reply;
+       GError *gerr = NULL;
+
+       if (!port->listener_id)
+               return;
+
+       if (err < 0) {
+               error("Unable to get service record: %s (%d)", strerror(-err),
+                       -err);
+               reply = btd_error_failed(port->msg, strerror(-err));
+               goto failed;
+       }
+
+       if (!recs || !recs->data) {
+               error("No record found");
+               reply = btd_error_failed(port->msg, "No record found");
+               goto failed;
+       }
+
+       record = recs->data;
+
+       if (sdp_get_access_protos(record, &protos) < 0) {
+               error("Unable to get access protos from port record");
+               reply = btd_error_failed(port->msg, "Invalid channel");
+               goto failed;
+       }
+
+       port->channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+
+       sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+       sdp_list_free(protos, NULL);
+
+       port->io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, port,
+                               NULL, &gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &device->src,
+                               BT_IO_OPT_DEST_BDADDR, &device->dst,
+                               BT_IO_OPT_CHANNEL, port->channel,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_INVALID);
+       if (!port->io) {
+               error("%s", gerr->message);
+               reply = btd_error_failed(port->msg, gerr->message);
+               g_error_free(gerr);
+               goto failed;
+       }
+
+       return;
+
+failed:
+       g_dbus_remove_watch(device->conn, port->listener_id);
+       port->listener_id = 0;
+       g_dbus_send_message(device->conn, reply);
+}
+
+static int connect_port(struct serial_port *port)
+{
+       struct serial_device *device = port->device;
+       uuid_t uuid;
+       int err;
+
+       if (!port->uuid)
+               goto connect;
+
+       err = bt_string2uuid(&uuid, port->uuid);
+       if (err < 0)
+               return err;
+
+       sdp_uuid128_to_uuid(&uuid);
+
+       return bt_search_service(&device->src, &device->dst, &uuid,
+                               get_record_cb, port, NULL);
+
+connect:
+       port->io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, port,
+                               NULL, NULL,
+                               BT_IO_OPT_SOURCE_BDADDR, &device->src,
+                               BT_IO_OPT_DEST_BDADDR, &device->dst,
+                               BT_IO_OPT_CHANNEL, port->channel,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_INVALID);
+       if (port->io == NULL)
+               return -EIO;
+
+       return 0;
+}
+
+static struct serial_port *create_port(struct serial_device *device,
+                                       const char *uuid, uint8_t channel)
+{
+       struct serial_port *port;
+
+       port = g_new0(struct serial_port, 1);
+       port->uuid = g_strdup(uuid);
+       port->channel = channel;
+       port->device = device;
+       port->id = -1;
+       port->fd = -1;
+
+       device->ports = g_slist_append(device->ports, port);
+
+       return port;
+}
+
+static DBusMessage *port_connect(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct serial_device *device = user_data;
+       struct serial_port *port;
+       const char *pattern;
+       int err;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return NULL;
+
+       port = find_port(device->ports, pattern);
+       if (!port) {
+               char *endptr = NULL;
+               int channel;
+
+               channel = strtol(pattern, &endptr, 10);
+               if ((endptr && *endptr != '\0') || channel < 1 || channel > 30)
+                       return btd_error_does_not_exist(msg);
+
+               port = create_port(device, NULL, channel);
+       }
+
+       if (port->listener_id)
+               return btd_error_failed(msg, "Port already in use");
+
+       port->listener_id = g_dbus_add_disconnect_watch(conn,
+                                               dbus_message_get_sender(msg),
+                                               port_owner_exited, port,
+                                               NULL);
+
+       port->msg = dbus_message_ref(msg);
+
+       err = connect_port(port);
+       if (err < 0) {
+               error("%s", strerror(-err));
+               g_dbus_remove_watch(conn, port->listener_id);
+               port->listener_id = 0;
+
+               return btd_error_failed(msg, strerror(-err));
+       }
+
+       return NULL;
+}
+
+static DBusMessage *port_disconnect(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct serial_device *device = user_data;
+       struct serial_port *port;
+       const char *dev, *owner, *caller;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dev,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return NULL;
+
+       port = find_port(device->ports, dev);
+       if (!port)
+               return btd_error_does_not_exist(msg);
+
+       if (!port->listener_id)
+               return btd_error_not_connected(msg);
+
+       owner = dbus_message_get_sender(port->msg);
+       caller = dbus_message_get_sender(msg);
+       if (!g_str_equal(owner, caller))
+               return btd_error_not_authorized(msg);
+
+       port_release(port);
+
+       g_dbus_remove_watch(conn, port->listener_id);
+       port->listener_id = 0;
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable port_methods[] = {
+       { GDBUS_ASYNC_METHOD("Connect",
+               GDBUS_ARGS({ "pattern", "s" }), GDBUS_ARGS({ "tty", "s" }),
+               port_connect) },
+       { GDBUS_ASYNC_METHOD("ConnectFD",
+               GDBUS_ARGS({ "pattern", "s" }), GDBUS_ARGS({ "fd", "s" }),
+               port_connect) },
+       { GDBUS_METHOD("Disconnect",
+               GDBUS_ARGS({ "device", "s" }), NULL,
+               port_disconnect) },
+       { }
+};
+
+static struct serial_device *create_serial_device(DBusConnection *conn,
+                                       const char *path, bdaddr_t *src,
+                                       bdaddr_t *dst)
+{
+       struct serial_device *device;
+
+       device = g_new0(struct serial_device, 1);
+       device->conn = dbus_connection_ref(conn);
+       bacpy(&device->dst, dst);
+       bacpy(&device->src, src);
+       device->path = g_strdup(path);
+
+       if (!g_dbus_register_interface(conn, path,
+                               SERIAL_PORT_INTERFACE,
+                               port_methods, NULL, NULL,
+                               device, path_unregister)) {
+               error("D-Bus failed to register %s interface",
+                               SERIAL_PORT_INTERFACE);
+               serial_device_free(device);
+               return NULL;
+       }
+
+       DBG("Registered interface %s on path %s",
+               SERIAL_PORT_INTERFACE, path);
+
+       return device;
+}
+
+int port_register(DBusConnection *conn, const char *path, bdaddr_t *src,
+                       bdaddr_t *dst, const char *uuid, uint8_t channel)
+{
+       struct serial_device *device;
+       struct serial_port *port;
+
+       device = find_device(devices, path);
+       if (!device) {
+               device = create_serial_device(conn, path, src, dst);
+               if (!device)
+                       return -1;
+               devices = g_slist_append(devices, device);
+       }
+
+       if (find_port(device->ports, uuid))
+               return 0;
+
+       port = g_new0(struct serial_port, 1);
+       port->uuid = g_strdup(uuid);
+       port->channel = channel;
+       port->device = device;
+       port->id = -1;
+       port->fd = -1;
+
+       device->ports = g_slist_append(device->ports, port);
+
+       return 0;
+}
+
+int port_unregister(const char *path)
+{
+       struct serial_device *device;
+
+       device = find_device(devices, path);
+       if (!device)
+               return -ENOENT;
+
+       g_slist_free_full(device->ports, serial_port_free);
+
+       g_dbus_unregister_interface(device->conn, path, SERIAL_PORT_INTERFACE);
+
+       return 0;
+}
diff --git a/serial/port.h b/serial/port.h
new file mode 100644 (file)
index 0000000..74ac9f0
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void port_release_all(void);
+
+int port_register(DBusConnection *conn, const char *path, bdaddr_t *src,
+                 bdaddr_t *dst, const char *name, uint8_t channel);
+
+int port_unregister(const char *path);
diff --git a/serial/proxy.c b/serial/proxy.c
new file mode 100644 (file)
index 0000000..dd38317
--- /dev/null
@@ -0,0 +1,1269 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/rfcomm.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "../src/dbus-common.h"
+#include "../src/adapter.h"
+
+#include "log.h"
+
+#include "error.h"
+#include "sdpd.h"
+#include "glib-helper.h"
+#include "btio.h"
+#include "proxy.h"
+
+#define SERIAL_PROXY_INTERFACE "org.bluez.SerialProxy"
+#define SERIAL_MANAGER_INTERFACE "org.bluez.SerialProxyManager"
+#define BUF_SIZE               1024
+
+typedef enum {
+       TTY_PROXY,
+       UNIX_SOCKET_PROXY,
+       TCP_SOCKET_PROXY,
+       UNKNOWN_PROXY_TYPE = 0xFF
+} proxy_type_t;
+
+struct serial_adapter {
+       struct btd_adapter      *btd_adapter;   /* Adapter pointer */
+       DBusConnection          *conn;          /* Adapter connection */
+       GSList                  *proxies;       /* Proxies list */
+};
+
+struct serial_proxy {
+       bdaddr_t        src;            /* Local address */
+       bdaddr_t        dst;            /* Remote address */
+       char            *path;          /* Proxy path */
+       char            *uuid128;       /* UUID 128 */
+       char            *address;       /* TTY or Unix socket name */
+       char            *owner;         /* Application bus name */
+       guint           watch;          /* Application watch */
+       short int       port;           /* TCP port */
+       proxy_type_t    type;           /* TTY or Unix socket */
+       struct termios  sys_ti;         /* Default TTY setting */
+       struct termios  proxy_ti;       /* Proxy TTY settings */
+       uint8_t         channel;        /* RFCOMM channel */
+       uint32_t        record_id;      /* Service record id */
+       GIOChannel      *io;            /* Server listen */
+       GIOChannel      *rfcomm;        /* Remote RFCOMM channel*/
+       GIOChannel      *local;         /* Local channel: TTY or Unix socket */
+       struct serial_adapter *adapter; /* Adapter pointer */
+};
+
+static GSList *adapters = NULL;
+static int sk_counter = 0;
+
+static void disable_proxy(struct serial_proxy *prx)
+{
+       if (prx->rfcomm) {
+               g_io_channel_shutdown(prx->rfcomm, TRUE, NULL);
+               g_io_channel_unref(prx->rfcomm);
+               prx->rfcomm = NULL;
+       }
+
+       if (prx->local) {
+               g_io_channel_shutdown(prx->local, TRUE, NULL);
+               g_io_channel_unref(prx->local);
+               prx->local = NULL;
+       }
+
+       remove_record_from_server(prx->record_id);
+       prx->record_id = 0;
+
+       g_io_channel_unref(prx->io);
+       prx->io = NULL;
+}
+
+static void proxy_free(struct serial_proxy *prx)
+{
+       g_free(prx->owner);
+       g_free(prx->path);
+       g_free(prx->address);
+       g_free(prx->uuid128);
+       g_free(prx);
+}
+
+static sdp_record_t *proxy_record_new(const char *uuid128, uint8_t channel)
+{
+       sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
+       uuid_t uuid, root_uuid, l2cap, rfcomm;
+       sdp_profile_desc_t profile;
+       sdp_record_t *record;
+       sdp_data_t *ch;
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(record, root);
+       sdp_list_free(root, NULL);
+
+       bt_string2uuid(&uuid, uuid128);
+       sdp_uuid128_to_uuid(&uuid);
+       svclass_id = sdp_list_append(NULL, &uuid);
+       sdp_set_service_classes(record, svclass_id);
+       sdp_list_free(svclass_id, NULL);
+
+       sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
+       profile.version = 0x0100;
+       profiles = sdp_list_append(NULL, &profile);
+       sdp_set_profile_descs(record, profiles);
+       sdp_list_free(profiles, NULL);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(NULL, &l2cap);
+       apseq = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+       proto[1] = sdp_list_append(NULL, &rfcomm);
+       ch = sdp_data_alloc(SDP_UINT8, &channel);
+       proto[1] = sdp_list_append(proto[1], ch);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_add_lang_attr(record);
+
+       sdp_set_info_attr(record, "Serial Proxy", NULL, "Serial Proxy");
+
+       sdp_data_free(ch);
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(aproto, NULL);
+
+       return record;
+}
+
+static int channel_write(GIOChannel *chan, char *buf, size_t size)
+{
+       size_t wbytes;
+       ssize_t written;
+       int fd;
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       wbytes = 0;
+       while (wbytes < size) {
+               written = write(fd, buf + wbytes, size - wbytes);
+               if (written < 0)
+                       return -errno;
+
+               wbytes += written;
+       }
+
+       return 0;
+}
+
+static gboolean forward_data(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       char buf[BUF_SIZE];
+       struct serial_proxy *prx = data;
+       GIOChannel *dest;
+       ssize_t rbytes;
+       int fd, err;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       dest = (chan == prx->rfcomm) ? prx->local : prx->rfcomm;
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       if (cond & (G_IO_HUP | G_IO_ERR)) {
+               /* Try forward remaining data */
+               do {
+                       rbytes = read(fd, buf, sizeof(buf));
+                       if (rbytes <= 0)
+                               break;
+
+                       err = channel_write(dest, buf, rbytes);
+               } while (err == 0);
+
+               g_io_channel_shutdown(prx->local, TRUE, NULL);
+               g_io_channel_unref(prx->local);
+               prx->local = NULL;
+
+               g_io_channel_shutdown(prx->rfcomm, TRUE, NULL);
+               g_io_channel_unref(prx->rfcomm);
+               prx->rfcomm = NULL;
+
+               return FALSE;
+       }
+
+       rbytes = read(fd, buf, sizeof(buf));
+       if (rbytes < 0)
+               return FALSE;
+
+       err = channel_write(dest, buf, rbytes);
+       if (err != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static inline int unix_socket_connect(const char *address)
+{
+       struct sockaddr_un addr;
+       int err, sk;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = PF_UNIX;
+
+       if (strncmp("x00", address, 3) == 0) {
+               /*
+                * Abstract namespace: first byte NULL, x00
+                * must be removed from the original address.
+                */
+               strncpy(addr.sun_path + 1, address + 3,
+                                               sizeof(addr.sun_path) - 2);
+       } else {
+               /* Filesystem address */
+               strncpy(addr.sun_path, address, sizeof(addr.sun_path) - 1);
+       }
+
+       /* Unix socket */
+       sk = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (sk < 0) {
+               err = -errno;
+               error("Unix socket(%s) create failed: %s(%d)",
+                               address, strerror(-err), -err);
+               return err;
+       }
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               err = -errno;
+               error("Unix socket(%s) connect failed: %s(%d)",
+                               address, strerror(-err), -err);
+               close(sk);
+               return err;
+       }
+
+       return sk;
+}
+
+static int tcp_socket_connect(const char *address)
+{
+       struct sockaddr_in addr;
+       int err, sk, port;
+
+       memset(&addr, 0, sizeof(addr));
+
+       if (strncmp(address, "localhost", 9) != 0) {
+               error("Address should have the form localhost:port.");
+               return -1;
+       }
+       port = atoi(strchr(address, ':') + 1);
+       if (port <= 0) {
+               error("Invalid port '%d'.", port);
+               return -1;
+       }
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+       addr.sin_port = htons(port);
+
+       sk = socket(PF_INET, SOCK_STREAM, 0);
+       if (sk < 0) {
+               err = -errno;
+               error("TCP socket(%s) create failed %s(%d)", address,
+                                                       strerror(-err), -err);
+               return err;
+       }
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               err = -errno;
+               error("TCP socket(%s) connect failed: %s(%d)",
+                                               address, strerror(-err), -err);
+               close(sk);
+               return err;
+       }
+       return sk;
+}
+
+static inline int tty_open(const char *tty, struct termios *ti)
+{
+       int err, sk;
+
+       sk = open(tty, O_RDWR | O_NOCTTY);
+       if (sk < 0) {
+               err = -errno;
+               error("Can't open TTY %s: %s(%d)", tty, strerror(-err), -err);
+               return err;
+       }
+
+       if (ti && tcsetattr(sk, TCSANOW, ti) < 0) {
+               err = -errno;
+               error("Can't change serial settings: %s(%d)",
+                               strerror(-err), -err);
+               close(sk);
+               return err;
+       }
+
+       return sk;
+}
+
+static void connect_event_cb(GIOChannel *chan, GError *conn_err, gpointer data)
+{
+       struct serial_proxy *prx = data;
+       int sk;
+
+       if (conn_err) {
+               error("%s", conn_err->message);
+               goto drop;
+       }
+
+       /* Connect local */
+       switch (prx->type) {
+       case UNIX_SOCKET_PROXY:
+               sk = unix_socket_connect(prx->address);
+               break;
+       case TTY_PROXY:
+               sk = tty_open(prx->address, &prx->proxy_ti);
+               break;
+       case TCP_SOCKET_PROXY:
+               sk = tcp_socket_connect(prx->address);
+               break;
+       default:
+               sk = -1;
+       }
+
+       if (sk < 0)
+               goto drop;
+
+       prx->local = g_io_channel_unix_new(sk);
+
+       g_io_add_watch(prx->rfcomm,
+                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                       forward_data, prx);
+
+       g_io_add_watch(prx->local,
+                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                       forward_data, prx);
+
+       return;
+
+drop:
+       g_io_channel_shutdown(prx->rfcomm, TRUE, NULL);
+       g_io_channel_unref(prx->rfcomm);
+       prx->rfcomm = NULL;
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+       struct serial_proxy *prx = user_data;
+       GError *err = NULL;
+
+       if (derr) {
+               error("Access denied: %s", derr->message);
+               goto reject;
+       }
+
+       if (!bt_io_accept(prx->rfcomm, connect_event_cb, prx, NULL,
+                                                       &err)) {
+               error("bt_io_accept: %s", err->message);
+               g_error_free(err);
+               goto reject;
+       }
+
+       return;
+
+reject:
+       g_io_channel_shutdown(prx->rfcomm, TRUE, NULL);
+       g_io_channel_unref(prx->rfcomm);
+       prx->rfcomm = NULL;
+}
+
+static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
+{
+       struct serial_proxy *prx = user_data;
+       int perr;
+       char address[18];
+       GError *err = NULL;
+
+       bt_io_get(chan, BT_IO_RFCOMM, &err,
+                       BT_IO_OPT_DEST_BDADDR, &prx->dst,
+                       BT_IO_OPT_DEST, address,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               goto drop;
+       }
+
+       if (prx->rfcomm) {
+               error("Refusing connect from %s: Proxy already in use",
+                               address);
+               goto drop;
+       }
+
+       DBG("Serial Proxy: incoming connect from %s", address);
+
+       prx->rfcomm = g_io_channel_ref(chan);
+
+       perr = btd_request_authorization(&prx->src, &prx->dst,
+                                       prx->uuid128, auth_cb, prx);
+       if (perr < 0) {
+               error("Refusing connect from %s: %s (%d)", address,
+                               strerror(-perr), -perr);
+               g_io_channel_unref(prx->rfcomm);
+               prx->rfcomm = NULL;
+               goto drop;
+       }
+
+       return;
+
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static int enable_proxy(struct serial_proxy *prx)
+{
+       sdp_record_t *record;
+       GError *gerr = NULL;
+       int err;
+
+       if (prx->io)
+               return -EALREADY;
+
+       /* Listen */
+       prx->io = bt_io_listen(BT_IO_RFCOMM, NULL, confirm_event_cb, prx,
+                               NULL, &gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &prx->src,
+                               BT_IO_OPT_INVALID);
+       if (!prx->io)
+               goto failed;
+
+       bt_io_get(prx->io, BT_IO_RFCOMM, &gerr,
+                       BT_IO_OPT_CHANNEL, &prx->channel,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               g_io_channel_unref(prx->io);
+               prx->io = NULL;
+               goto failed;
+       }
+
+       DBG("Allocated channel %d", prx->channel);
+
+       g_io_channel_set_close_on_unref(prx->io, TRUE);
+
+       record = proxy_record_new(prx->uuid128, prx->channel);
+       if (!record) {
+               g_io_channel_unref(prx->io);
+               return -ENOMEM;
+       }
+
+       err = add_record_to_server(&prx->src, record);
+       if (err < 0) {
+               sdp_record_free(record);
+               g_io_channel_unref(prx->io);
+               return err;
+       }
+
+       prx->record_id = record->handle;
+
+       return 0;
+
+failed:
+       error("%s", gerr->message);
+       g_error_free(gerr);
+       return -EIO;
+
+}
+static DBusMessage *proxy_enable(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct serial_proxy *prx = data;
+       int err;
+
+       err = enable_proxy(prx);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *proxy_disable(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct serial_proxy *prx = data;
+
+       if (!prx->io)
+               return btd_error_failed(msg, "Not enabled");
+
+       /* Remove the watches and unregister the record */
+       disable_proxy(prx);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *proxy_get_info(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct serial_proxy *prx = data;
+       DBusMessage *reply;
+       DBusMessageIter iter, dict;
+       dbus_bool_t boolean;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       dict_append_entry(&dict, "uuid", DBUS_TYPE_STRING, &prx->uuid128);
+
+       dict_append_entry(&dict, "address", DBUS_TYPE_STRING, &prx->address);
+
+       if (prx->channel)
+               dict_append_entry(&dict, "channel",
+                                       DBUS_TYPE_BYTE, &prx->channel);
+
+       boolean = (prx->io ? TRUE : FALSE);
+       dict_append_entry(&dict, "enabled", DBUS_TYPE_BOOLEAN, &boolean);
+
+       boolean = (prx->rfcomm ? TRUE : FALSE);
+       dict_append_entry(&dict, "connected", DBUS_TYPE_BOOLEAN, &boolean);
+
+       /* If connected: append the remote address */
+       if (boolean) {
+               char bda[18];
+               const char *pstr = bda;
+
+               ba2str(&prx->dst, bda);
+               dict_append_entry(&dict, "address", DBUS_TYPE_STRING, &pstr);
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static struct {
+       const char      *str;
+       speed_t         speed;
+} supported_speed[]  = {
+       {"50",          B50     },
+       {"300",         B300    },
+       {"600",         B600    },
+       {"1200",        B1200   },
+       {"1800",        B1800   },
+       {"2400",        B2400   },
+       {"4800",        B4800   },
+       {"9600",        B9600   },
+       {"19200",       B19200  },
+       {"38400",       B38400  },
+       {"57600",       B57600  },
+       {"115200",      B115200 },
+       { NULL,         B0      }
+};
+
+static speed_t str2speed(const char *str, speed_t *speed)
+{
+       int i;
+
+       for (i = 0; supported_speed[i].str; i++) {
+               if (strcmp(supported_speed[i].str, str) != 0)
+                       continue;
+
+               if (speed)
+                       *speed = supported_speed[i].speed;
+
+               return supported_speed[i].speed;
+       }
+
+       return B0;
+}
+
+static int set_parity(const char *str, tcflag_t *ctrl)
+{
+       if (strcasecmp("even", str) == 0) {
+               *ctrl |= PARENB;
+               *ctrl &= ~PARODD;
+       } else if (strcasecmp("odd", str) == 0) {
+               *ctrl |= PARENB;
+               *ctrl |= PARODD;
+       } else if (strcasecmp("mark", str) == 0)
+               *ctrl |= PARENB;
+       else if ((strcasecmp("none", str) == 0) ||
+                       (strcasecmp("space", str) == 0))
+               *ctrl &= ~PARENB;
+       else
+               return -1;
+
+       return 0;
+}
+
+static int set_databits(uint8_t databits, tcflag_t *ctrl)
+{
+       if (databits < 5 || databits > 8)
+               return -EINVAL;
+
+       *ctrl &= ~CSIZE;
+       switch (databits) {
+       case 5:
+               *ctrl |= CS5;
+               break;
+       case 6:
+               *ctrl |= CS6;
+               break;
+       case 7:
+               *ctrl |= CS7;
+               break;
+       case 8:
+               *ctrl |= CS8;
+               break;
+       }
+
+       return 0;
+}
+
+static int set_stopbits(uint8_t stopbits, tcflag_t *ctrl)
+{
+       /* 1.5 will not be allowed */
+       switch (stopbits) {
+       case 1:
+               *ctrl &= ~CSTOPB;
+               return 0;
+       case 2:
+               *ctrl |= CSTOPB;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static DBusMessage *proxy_set_serial_params(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct serial_proxy *prx = data;
+       const char *ratestr, *paritystr;
+       uint8_t databits, stopbits;
+       tcflag_t ctrl;          /* Control mode flags */
+       speed_t speed = B0;     /* In/Out speed */
+
+       /* Don't allow change TTY settings if it is open */
+       if (prx->local)
+               return btd_error_not_authorized(msg);
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &ratestr,
+                               DBUS_TYPE_BYTE, &databits,
+                               DBUS_TYPE_BYTE, &stopbits,
+                               DBUS_TYPE_STRING, &paritystr,
+                               DBUS_TYPE_INVALID))
+               return NULL;
+
+       if (str2speed(ratestr, &speed)  == B0)
+               return btd_error_invalid_args(msg);
+
+       ctrl = prx->proxy_ti.c_cflag;
+       if (set_databits(databits, &ctrl) < 0)
+               return btd_error_invalid_args(msg);
+
+       if (set_stopbits(stopbits, &ctrl) < 0)
+               return btd_error_invalid_args(msg);
+
+       if (set_parity(paritystr, &ctrl) < 0)
+               return btd_error_invalid_args(msg);
+
+       prx->proxy_ti.c_cflag = ctrl;
+       prx->proxy_ti.c_cflag |= (CLOCAL | CREAD);
+       cfsetispeed(&prx->proxy_ti, speed);
+       cfsetospeed(&prx->proxy_ti, speed);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable proxy_methods[] = {
+       { GDBUS_METHOD("Enable", NULL, NULL, proxy_enable) },
+       { GDBUS_METHOD("Disable", NULL, NULL, proxy_disable) },
+       { GDBUS_METHOD("GetInfo",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       proxy_get_info) },
+       { GDBUS_METHOD("SetSerialParameters",
+                       GDBUS_ARGS({ "rate", "s" }, { "data", "y" },
+                                       { "stop", "y" }, { "parity", "s" }),
+                       NULL, proxy_set_serial_params) },
+       { },
+};
+
+static void proxy_path_unregister(gpointer data)
+{
+       struct serial_proxy *prx = data;
+       int sk;
+
+       DBG("Unregistered proxy: %s", prx->address);
+
+       if (prx->type != TTY_PROXY)
+               goto done;
+
+       /* Restore the initial TTY configuration */
+       sk = open(prx->address, O_RDWR | O_NOCTTY);
+       if (sk >= 0) {
+               tcsetattr(sk, TCSAFLUSH, &prx->sys_ti);
+               close(sk);
+       }
+done:
+
+       proxy_free(prx);
+}
+
+static int register_proxy_object(struct serial_proxy *prx)
+{
+       struct serial_adapter *adapter = prx->adapter;
+       char path[MAX_PATH_LENGTH + 1];
+
+       snprintf(path, MAX_PATH_LENGTH, "%s/proxy%d",
+                       adapter_get_path(adapter->btd_adapter), sk_counter++);
+
+       if (!g_dbus_register_interface(adapter->conn, path,
+                                       SERIAL_PROXY_INTERFACE,
+                                       proxy_methods, NULL, NULL,
+                                       prx, proxy_path_unregister)) {
+               error("D-Bus failed to register %s path", path);
+               return -1;
+       }
+
+       prx->path = g_strdup(path);
+       adapter->proxies = g_slist_append(adapter->proxies, prx);
+
+       DBG("Registered proxy: %s", path);
+
+       return 0;
+}
+
+static int proxy_tty_register(struct serial_adapter *adapter,
+                               const char *uuid128, const char *address,
+                               struct termios *ti,
+                               struct serial_proxy **proxy)
+{
+       struct termios sys_ti;
+       struct serial_proxy *prx;
+       int sk, ret;
+
+       sk = open(address, O_RDONLY | O_NOCTTY);
+       if (sk < 0) {
+               error("Can't open TTY: %s(%d)", strerror(errno), errno);
+               return -EINVAL;
+       }
+
+       prx = g_new0(struct serial_proxy, 1);
+       prx->address = g_strdup(address);
+       prx->uuid128 = g_strdup(uuid128);
+       prx->type = TTY_PROXY;
+       adapter_get_address(adapter->btd_adapter, &prx->src);
+       prx->adapter = adapter;
+
+       /* Current TTY settings */
+       memset(&sys_ti, 0, sizeof(sys_ti));
+       tcgetattr(sk, &sys_ti);
+       memcpy(&prx->sys_ti, &sys_ti, sizeof(sys_ti));
+       close(sk);
+
+       if (!ti) {
+               /* Use current settings */
+               memcpy(&prx->proxy_ti, &sys_ti, sizeof(sys_ti));
+       } else {
+               /* New TTY settings: user provided */
+               memcpy(&prx->proxy_ti, ti, sizeof(*ti));
+       }
+
+       ret = register_proxy_object(prx);
+       if (ret < 0) {
+               proxy_free(prx);
+               return ret;
+       }
+
+       *proxy = prx;
+
+       return ret;
+}
+
+static int proxy_socket_register(struct serial_adapter *adapter,
+                               const char *uuid128, const char *address,
+                               struct serial_proxy **proxy)
+{
+       struct serial_proxy *prx;
+       int ret;
+
+       prx = g_new0(struct serial_proxy, 1);
+       prx->address = g_strdup(address);
+       prx->uuid128 = g_strdup(uuid128);
+       prx->type = UNIX_SOCKET_PROXY;
+       adapter_get_address(adapter->btd_adapter, &prx->src);
+       prx->adapter = adapter;
+
+       ret = register_proxy_object(prx);
+       if (ret < 0) {
+               proxy_free(prx);
+               return ret;
+       }
+
+       *proxy = prx;
+
+       return ret;
+}
+
+static int proxy_tcp_register(struct serial_adapter *adapter,
+                               const char *uuid128, const char *address,
+                               struct serial_proxy **proxy)
+{
+       struct serial_proxy *prx;
+       int ret;
+
+       prx = g_new0(struct serial_proxy, 1);
+       prx->address = g_strdup(address);
+       prx->uuid128 = g_strdup(uuid128);
+       prx->type = TCP_SOCKET_PROXY;
+       adapter_get_address(adapter->btd_adapter, &prx->src);
+       prx->adapter = adapter;
+
+       ret = register_proxy_object(prx);
+       if (ret < 0) {
+               proxy_free(prx);
+               return ret;
+       }
+
+       *proxy = prx;
+
+       return ret;
+}
+
+static proxy_type_t addr2type(const char *address)
+{
+       struct stat st;
+
+       if (stat(address, &st) < 0) {
+               /*
+                * Unix socket: if the sun_path starts with null byte
+                * it refers to abstract namespace. 'x00' will be used
+                * to represent the null byte.
+                */
+               if (strncmp("localhost:", address, 10) == 0)
+                       return TCP_SOCKET_PROXY;
+               if (strncmp("x00", address, 3) != 0)
+                       return UNKNOWN_PROXY_TYPE;
+               else
+                       return UNIX_SOCKET_PROXY;
+       } else {
+               /* Filesystem: char device or unix socket */
+               if (S_ISCHR(st.st_mode) && strncmp("/dev/", address, 4) == 0)
+                       return TTY_PROXY;
+               else if (S_ISSOCK(st.st_mode))
+                       return UNIX_SOCKET_PROXY;
+               else
+                       return UNKNOWN_PROXY_TYPE;
+       }
+}
+
+static int proxy_addrcmp(gconstpointer proxy, gconstpointer addr)
+{
+       const struct serial_proxy *prx = proxy;
+       const char *address = addr;
+
+       return strcmp(prx->address, address);
+}
+
+static int proxy_pathcmp(gconstpointer proxy, gconstpointer p)
+{
+       const struct serial_proxy *prx = proxy;
+       const char *path = p;
+
+       return strcmp(prx->path, path);
+}
+
+static int register_proxy(struct serial_adapter *adapter,
+                               const char *uuid_str, const char *address,
+                               struct serial_proxy **proxy)
+{
+       proxy_type_t type;
+       int err;
+
+       type = addr2type(address);
+       if (type == UNKNOWN_PROXY_TYPE)
+               return -EINVAL;
+
+       /* Only one proxy per address(TTY or unix socket) is allowed */
+       if (g_slist_find_custom(adapter->proxies, address, proxy_addrcmp))
+               return -EALREADY;
+
+       switch (type) {
+       case UNIX_SOCKET_PROXY:
+               err = proxy_socket_register(adapter, uuid_str, address, proxy);
+               break;
+       case TTY_PROXY:
+               err = proxy_tty_register(adapter, uuid_str, address, NULL,
+                                       proxy);
+               break;
+       case TCP_SOCKET_PROXY:
+               err = proxy_tcp_register(adapter, uuid_str, address, proxy);
+               break;
+       default:
+               err = -EINVAL;
+       }
+
+       if (err < 0)
+               return err;
+
+       g_dbus_emit_signal(adapter->conn,
+                               adapter_get_path(adapter->btd_adapter),
+                               SERIAL_MANAGER_INTERFACE, "ProxyCreated",
+                               DBUS_TYPE_STRING, &(*proxy)->path,
+                               DBUS_TYPE_INVALID);
+
+       return 0;
+}
+
+static void unregister_proxy(struct serial_proxy *proxy)
+{
+       struct serial_adapter *adapter = proxy->adapter;
+       char *path = g_strdup(proxy->path);
+
+       if (proxy->watch > 0)
+               g_dbus_remove_watch(adapter->conn, proxy->watch);
+
+       g_dbus_emit_signal(adapter->conn,
+                       adapter_get_path(adapter->btd_adapter),
+                       SERIAL_MANAGER_INTERFACE, "ProxyRemoved",
+                       DBUS_TYPE_STRING, &path,
+                       DBUS_TYPE_INVALID);
+
+       adapter->proxies = g_slist_remove(adapter->proxies, proxy);
+
+       g_dbus_unregister_interface(adapter->conn, path,
+                                       SERIAL_PROXY_INTERFACE);
+
+       g_free(path);
+}
+
+static void watch_proxy(DBusConnection *connection, void *user_data)
+{
+       struct serial_proxy *proxy = user_data;
+
+       proxy->watch = 0;
+       unregister_proxy(proxy);
+}
+
+static DBusMessage *create_proxy(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct serial_adapter *adapter = data;
+       struct serial_proxy *proxy;
+       const char *pattern, *address;
+       char *uuid_str;
+       int err;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &pattern,
+                               DBUS_TYPE_STRING, &address,
+                               DBUS_TYPE_INVALID))
+               return NULL;
+
+       uuid_str = bt_name2string(pattern);
+       if (!uuid_str)
+               return btd_error_invalid_args(msg);
+
+       err = register_proxy(adapter, uuid_str, address, &proxy);
+       g_free(uuid_str);
+
+       if (err == -EINVAL)
+               return btd_error_invalid_args(msg);
+       else if (err == -EALREADY)
+               return btd_error_already_exists(msg);
+       else if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       proxy->owner = g_strdup(dbus_message_get_sender(msg));
+       proxy->watch = g_dbus_add_disconnect_watch(conn, proxy->owner,
+                                               watch_proxy,
+                                               proxy, NULL);
+
+       return g_dbus_create_reply(msg, DBUS_TYPE_STRING, &proxy->path,
+                                       DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *list_proxies(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct serial_adapter *adapter = data;
+       const GSList *l;
+       DBusMessage *reply;
+       DBusMessageIter iter, iter_array;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_TYPE_STRING_AS_STRING, &iter_array);
+
+       for (l = adapter->proxies; l; l = l->next) {
+               struct serial_proxy *prx = l->data;
+
+               dbus_message_iter_append_basic(&iter_array,
+                               DBUS_TYPE_STRING, &prx->path);
+       }
+
+       dbus_message_iter_close_container(&iter, &iter_array);
+
+       return reply;
+}
+
+static DBusMessage *remove_proxy(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       struct serial_adapter *adapter = data;
+       struct serial_proxy *prx;
+       const char *path, *sender;
+       GSList *l;
+
+       if (!dbus_message_get_args(msg, NULL,
+                               DBUS_TYPE_STRING, &path,
+                               DBUS_TYPE_INVALID))
+               return NULL;
+
+       l = g_slist_find_custom(adapter->proxies, path, proxy_pathcmp);
+       if (!l)
+               return btd_error_does_not_exist(msg);
+
+       prx = l->data;
+
+       sender = dbus_message_get_sender(msg);
+       if (g_strcmp0(prx->owner, sender) != 0)
+               return btd_error_not_authorized(msg);
+
+       unregister_proxy(prx);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static void manager_path_unregister(void *data)
+{
+       struct serial_adapter *adapter = data;
+       GSList *l;
+
+       /* Remove proxy objects */
+       for (l = adapter->proxies; l; l = l->next) {
+               struct serial_proxy *prx = l->data;
+               char *path = g_strdup(prx->path);
+
+               g_dbus_unregister_interface(adapter->conn, path,
+                                       SERIAL_PROXY_INTERFACE);
+               g_free(path);
+       }
+
+       if (adapter->conn)
+               dbus_connection_unref(adapter->conn);
+
+       adapters = g_slist_remove(adapters, adapter);
+       g_slist_free(adapter->proxies);
+       btd_adapter_unref(adapter->btd_adapter);
+       g_free(adapter);
+}
+
+static const GDBusMethodTable manager_methods[] = {
+       { GDBUS_METHOD("CreateProxy",
+                       GDBUS_ARGS({ "pattern", "s" },
+                                       { "address", "s" }),
+                       GDBUS_ARGS({ "path", "s" }),
+                       create_proxy) },
+       { GDBUS_METHOD("ListProxies",
+                       NULL, GDBUS_ARGS({ "paths", "as" }),
+                       list_proxies) },
+       { GDBUS_METHOD("RemoveProxy",
+                       GDBUS_ARGS({ "path", "s" }), NULL,
+                       remove_proxy) },
+       { },
+};
+
+static const GDBusSignalTable manager_signals[] = {
+       { GDBUS_SIGNAL("ProxyCreated", GDBUS_ARGS({ "path", "s" })) },
+       { GDBUS_SIGNAL("ProxyRemoved", GDBUS_ARGS({ "path", "s" })) },
+       { }
+};
+
+static struct serial_adapter *find_adapter(GSList *list,
+                                       struct btd_adapter *btd_adapter)
+{
+       for (; list; list = list->next) {
+               struct serial_adapter *adapter = list->data;
+
+               if (adapter->btd_adapter == btd_adapter)
+                       return adapter;
+       }
+
+       return NULL;
+}
+
+static void serial_proxy_init(struct serial_adapter *adapter)
+{
+       GKeyFile *config;
+       GError *gerr = NULL;
+       const char *file = CONFIGDIR "/serial.conf";
+       char **group_list;
+       int i;
+
+       config = g_key_file_new();
+
+       if (!g_key_file_load_from_file(config, file, 0, &gerr)) {
+               error("Parsing %s failed: %s", file, gerr->message);
+               g_error_free(gerr);
+               g_key_file_free(config);
+               return;
+       }
+
+       group_list = g_key_file_get_groups(config, NULL);
+
+       for (i = 0; group_list[i] != NULL; i++) {
+               char *group_str = group_list[i], *uuid_str, *address;
+               int err;
+               struct serial_proxy *prx;
+
+               /* string length of "Proxy" is 5 */
+               if (strlen(group_str) < 5 || strncmp(group_str, "Proxy", 5))
+                       continue;
+
+               uuid_str = g_key_file_get_string(config, group_str, "UUID",
+                                                                       &gerr);
+               if (gerr) {
+                       DBG("%s: %s", file, gerr->message);
+                       g_error_free(gerr);
+                       g_key_file_free(config);
+                       g_strfreev(group_list);
+                       return;
+               }
+
+               address = g_key_file_get_string(config, group_str, "Address",
+                                                                       &gerr);
+               if (gerr) {
+                       DBG("%s: %s", file, gerr->message);
+                       g_error_free(gerr);
+                       g_key_file_free(config);
+                       g_free(uuid_str);
+                       g_strfreev(group_list);
+                       return;
+               }
+
+               err = register_proxy(adapter, uuid_str, address, &prx);
+               if (err == -EINVAL)
+                       error("Invalid address.");
+               else if (err == -EALREADY)
+                       DBG("Proxy already exists.");
+               else if (err < 0)
+                       error("Proxy creation failed (%s)", strerror(-err));
+               else {
+                       err = enable_proxy(prx);
+                       if (err < 0)
+                               error("Proxy enable failed (%s)",
+                                               strerror(-err));
+               }
+
+               g_free(uuid_str);
+               g_free(address);
+       }
+
+       g_strfreev(group_list);
+       g_key_file_free(config);
+}
+
+int proxy_register(DBusConnection *conn, struct btd_adapter *btd_adapter)
+{
+       struct serial_adapter *adapter;
+       const char *path;
+
+       adapter = find_adapter(adapters, btd_adapter);
+       if (adapter)
+               return -EINVAL;
+
+       adapter = g_new0(struct serial_adapter, 1);
+       adapter->conn = dbus_connection_ref(conn);
+       adapter->btd_adapter = btd_adapter_ref(btd_adapter);
+
+       path = adapter_get_path(btd_adapter);
+
+       if (!g_dbus_register_interface(conn, path,
+                                       SERIAL_MANAGER_INTERFACE,
+                                       manager_methods, manager_signals, NULL,
+                                       adapter, manager_path_unregister)) {
+               error("Failed to register %s interface to %s",
+                               SERIAL_MANAGER_INTERFACE, path);
+               return -1;
+       }
+
+       adapters = g_slist_append(adapters, adapter);
+
+       DBG("Registered interface %s on path %s",
+               SERIAL_MANAGER_INTERFACE, path);
+
+       serial_proxy_init(adapter);
+
+       return 0;
+}
+
+void proxy_unregister(struct btd_adapter *btd_adapter)
+{
+       struct serial_adapter *adapter;
+
+       adapter = find_adapter(adapters, btd_adapter);
+       if (!adapter)
+               return;
+
+       g_dbus_unregister_interface(adapter->conn,
+                       adapter_get_path(btd_adapter),
+                       SERIAL_MANAGER_INTERFACE);
+}
diff --git a/serial/proxy.h b/serial/proxy.h
new file mode 100644 (file)
index 0000000..7871665
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int proxy_register(DBusConnection *conn, struct btd_adapter *btd_adapter);
+void proxy_unregister(struct btd_adapter *btd_adapter);
diff --git a/serial/serial.conf b/serial/serial.conf
new file mode 100644 (file)
index 0000000..43ee6af
--- /dev/null
@@ -0,0 +1,10 @@
+# Configuration file for serial
+
+# There could be multiple proxy sections, the format is [Proxy <user chosen name>]
+#[Proxy DUN]
+
+# UUID for DUN proxy service
+#UUID=00001103-0000-1000-8000-00805F9B34FB
+
+# Address for device node
+#Address=/dev/ttyx
diff --git a/src/adapter.c b/src/adapter.c
new file mode 100644 (file)
index 0000000..f922876
--- /dev/null
@@ -0,0 +1,3580 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "textfile.h"
+
+#include "hcid.h"
+#include "sdpd.h"
+#include "adapter.h"
+#include "manager.h"
+#include "device.h"
+#include "dbus-common.h"
+#include "error.h"
+#include "glib-helper.h"
+#include "agent.h"
+#include "storage.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "attrib-server.h"
+#include "eir.h"
+
+/* Flags Descriptions */
+#define EIR_LIM_DISC                0x01 /* LE Limited Discoverable Mode */
+#define EIR_GEN_DISC                0x02 /* LE General Discoverable Mode */
+#define EIR_BREDR_UNSUP             0x04 /* BR/EDR Not Supported */
+#define EIR_SIM_CONTROLLER          0x08 /* Simultaneous LE and BR/EDR to Same
+                                           Device Capable (Controller) */
+#define EIR_SIM_HOST                0x10 /* Simultaneous LE and BR/EDR to Same
+                                           Device Capable (Host) */
+
+#define IO_CAPABILITY_DISPLAYONLY      0x00
+#define IO_CAPABILITY_DISPLAYYESNO     0x01
+#define IO_CAPABILITY_KEYBOARDONLY     0x02
+#define IO_CAPABILITY_NOINPUTNOOUTPUT  0x03
+#define IO_CAPABILITY_KEYBOARDDISPLAY  0x04
+#define IO_CAPABILITY_INVALID          0xFF
+
+#define check_address(address) bachk(address)
+
+#define OFF_TIMER 3
+
+static DBusConnection *connection = NULL;
+static GSList *adapter_drivers = NULL;
+
+static GSList *ops_candidates = NULL;
+
+const struct btd_adapter_ops *adapter_ops = NULL;
+
+struct session_req {
+       struct btd_adapter      *adapter;
+       DBusConnection          *conn;          /* Connection reference */
+       DBusMessage             *msg;           /* Unreplied message ref */
+       char                    *owner;         /* Bus name of the owner */
+       guint                   id;             /* Listener id */
+       uint8_t                 mode;           /* Requested mode */
+       int                     refcount;       /* Session refcount */
+       gboolean                got_reply;      /* Agent reply received */
+};
+
+struct service_auth {
+       service_auth_cb cb;
+       void *user_data;
+       struct btd_device *device;
+       struct btd_adapter *adapter;
+};
+
+struct btd_adapter {
+       uint16_t dev_id;
+       gboolean up;
+       char *path;                     /* adapter object path */
+       bdaddr_t bdaddr;                /* adapter Bluetooth Address */
+       uint32_t dev_class;             /* Class of Device */
+       char *name;                     /* adapter name */
+       gboolean allow_name_changes;    /* whether the adapter name can be changed */
+       guint stop_discov_id;           /* stop inquiry/scanning id */
+       uint32_t discov_timeout;        /* discoverable time(sec) */
+       guint pairable_timeout_id;      /* pairable timeout id */
+       uint32_t pairable_timeout;      /* pairable time(sec) */
+       uint8_t scan_mode;              /* scan mode: SCAN_DISABLED, SCAN_PAGE,
+                                        * SCAN_INQUIRY */
+       uint8_t mode;                   /* off, connectable, discoverable,
+                                        * limited */
+       uint8_t global_mode;            /* last valid global mode */
+       struct session_req *pending_mode;
+       int state;                      /* standard inq, periodic inq, name
+                                        * resolving, suspended discovery */
+       GSList *found_devices;
+       GSList *oor_devices;            /* out of range device list */
+       struct agent *agent;            /* For the new API */
+       guint auth_idle_id;             /* Ongoing authorization */
+       GSList *connections;            /* Connected devices */
+       GSList *devices;                /* Devices structure pointers */
+       GSList *mode_sessions;          /* Request Mode sessions */
+       GSList *disc_sessions;          /* Discovery sessions */
+       guint discov_id;                /* Discovery timer */
+       gboolean discovering;           /* Discovery active */
+       gboolean discov_suspended;      /* Discovery suspended */
+       guint auto_timeout_id;          /* Automatic connections timeout */
+       sdp_list_t *services;           /* Services associated to adapter */
+
+       gboolean pairable;              /* pairable state */
+       gboolean initialized;
+
+       gboolean off_requested;         /* DEVDOWN ioctl was called */
+
+       gint ref;
+
+       guint off_timer;
+
+       GSList *powered_callbacks;
+       GSList *pin_callbacks;
+
+       GSList *loaded_drivers;
+};
+
+static void dev_info_free(void *data)
+{
+       struct remote_dev_info *dev = data;
+
+       g_free(dev->name);
+       g_free(dev->alias);
+       g_slist_free_full(dev->services, g_free);
+       g_strfreev(dev->uuids);
+       g_free(dev);
+}
+
+int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
+                                                       uint8_t minor)
+{
+       return adapter_ops->set_dev_class(adapter->dev_id, major, minor);
+}
+
+static const char *mode2str(uint8_t mode)
+{
+       switch(mode) {
+       case MODE_OFF:
+               return "off";
+       case MODE_CONNECTABLE:
+               return "connectable";
+       case MODE_DISCOVERABLE:
+               return "discoverable";
+       default:
+               return "unknown";
+       }
+}
+
+static uint8_t get_mode(const bdaddr_t *bdaddr, const char *mode)
+{
+       if (strcasecmp("off", mode) == 0)
+               return MODE_OFF;
+       else if (strcasecmp("connectable", mode) == 0)
+               return MODE_CONNECTABLE;
+       else if (strcasecmp("discoverable", mode) == 0)
+               return MODE_DISCOVERABLE;
+       else if (strcasecmp("on", mode) == 0) {
+               char onmode[14], srcaddr[18];
+
+               ba2str(bdaddr, srcaddr);
+               if (read_on_mode(srcaddr, onmode, sizeof(onmode)) < 0)
+                       return MODE_CONNECTABLE;
+
+               return get_mode(bdaddr, onmode);
+       } else
+               return MODE_UNKNOWN;
+}
+
+static struct session_req *session_ref(struct session_req *req)
+{
+       req->refcount++;
+
+       DBG("%p: ref=%d", req, req->refcount);
+
+       return req;
+}
+
+static struct session_req *create_session(struct btd_adapter *adapter,
+                                       DBusConnection *conn, DBusMessage *msg,
+                                       uint8_t mode, GDBusWatchFunction cb)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct session_req *req;
+
+       req = g_new0(struct session_req, 1);
+       req->adapter = adapter;
+       req->conn = dbus_connection_ref(conn);
+       req->msg = dbus_message_ref(msg);
+       req->mode = mode;
+
+       if (cb == NULL)
+               return session_ref(req);
+
+       req->owner = g_strdup(sender);
+       req->id = g_dbus_add_disconnect_watch(conn, sender, cb, req, NULL);
+
+       info("%s session %p with %s activated",
+               req->mode ? "Mode" : "Discovery", req, sender);
+
+       return session_ref(req);
+}
+
+static int adapter_set_mode(struct btd_adapter *adapter, uint8_t mode)
+{
+       int err;
+
+       if (mode == MODE_CONNECTABLE)
+               err = adapter_ops->set_discoverable(adapter->dev_id, FALSE, 0);
+       else
+               err = adapter_ops->set_discoverable(adapter->dev_id, TRUE,
+                                               adapter->discov_timeout);
+
+       return err;
+}
+
+static struct session_req *find_session_by_msg(GSList *list, const DBusMessage *msg)
+{
+       for (; list; list = list->next) {
+               struct session_req *req = list->data;
+
+               if (req->msg == msg)
+                       return req;
+       }
+
+       return NULL;
+}
+
+static int set_mode(struct btd_adapter *adapter, uint8_t new_mode,
+                       DBusMessage *msg)
+{
+       int err;
+       const char *modestr;
+
+       if (adapter->pending_mode != NULL)
+               return -EALREADY;
+
+       if (!adapter->up && new_mode != MODE_OFF) {
+               err = adapter_ops->set_powered(adapter->dev_id, TRUE);
+               if (err < 0)
+                       return err;
+
+               goto done;
+       }
+
+       if (adapter->up && new_mode == MODE_OFF) {
+               err = adapter_ops->set_powered(adapter->dev_id, FALSE);
+               if (err < 0)
+                       return err;
+
+               adapter->off_requested = TRUE;
+
+               goto done;
+       }
+
+       if (new_mode == adapter->mode)
+               return 0;
+
+       err = adapter_set_mode(adapter, new_mode);
+
+       if (err < 0)
+               return err;
+
+done:
+       modestr = mode2str(new_mode);
+       write_device_mode(&adapter->bdaddr, modestr);
+
+       DBG("%s", modestr);
+
+       if (msg != NULL) {
+               struct session_req *req;
+
+               req = find_session_by_msg(adapter->mode_sessions, msg);
+               if (req) {
+                       adapter->pending_mode = req;
+                       session_ref(req);
+               } else
+                       /* Wait for mode change to reply */
+                       adapter->pending_mode = create_session(adapter,
+                                       connection, msg, new_mode, NULL);
+       } else
+               /* Nothing to reply just write the new mode */
+               adapter->mode = new_mode;
+
+       return 0;
+}
+
+static DBusMessage *set_discoverable(DBusConnection *conn, DBusMessage *msg,
+                               gboolean discoverable, void *data)
+{
+       struct btd_adapter *adapter = data;
+       uint8_t mode;
+       int err;
+
+       mode = discoverable ? MODE_DISCOVERABLE : MODE_CONNECTABLE;
+
+       if (mode == adapter->mode) {
+               adapter->global_mode = mode;
+               return dbus_message_new_method_return(msg);
+       }
+
+       err = set_mode(adapter, mode, msg);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       return NULL;
+}
+
+static DBusMessage *set_powered(DBusConnection *conn, DBusMessage *msg,
+                               gboolean powered, void *data)
+{
+       struct btd_adapter *adapter = data;
+       uint8_t mode;
+       int err;
+
+       if (powered) {
+               mode = get_mode(&adapter->bdaddr, "on");
+               return set_discoverable(conn, msg, mode == MODE_DISCOVERABLE,
+                                                                       data);
+       }
+
+       mode = MODE_OFF;
+
+       if (mode == adapter->mode) {
+               adapter->global_mode = mode;
+               return dbus_message_new_method_return(msg);
+       }
+
+       err = set_mode(adapter, mode, msg);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       return NULL;
+}
+
+static DBusMessage *set_pairable(DBusConnection *conn, DBusMessage *msg,
+                               gboolean pairable, void *data)
+{
+       struct btd_adapter *adapter = data;
+       int err;
+
+       if (adapter->scan_mode == SCAN_DISABLED)
+               return btd_error_not_ready(msg);
+
+       if (pairable == adapter->pairable)
+               goto done;
+
+       if (!(adapter->scan_mode & SCAN_INQUIRY))
+               goto store;
+
+       err = set_mode(adapter, MODE_DISCOVERABLE, NULL);
+       if (err < 0 && msg)
+               return btd_error_failed(msg, strerror(-err));
+
+store:
+       adapter_ops->set_pairable(adapter->dev_id, pairable);
+
+done:
+       return msg ? dbus_message_new_method_return(msg) : NULL;
+}
+
+static gboolean pairable_timeout_handler(void *data)
+{
+       set_pairable(NULL, NULL, FALSE, data);
+
+       return FALSE;
+}
+
+static void adapter_set_pairable_timeout(struct btd_adapter *adapter,
+                                       guint interval)
+{
+       if (adapter->pairable_timeout_id) {
+               g_source_remove(adapter->pairable_timeout_id);
+               adapter->pairable_timeout_id = 0;
+       }
+
+       if (interval == 0)
+               return;
+
+       adapter->pairable_timeout_id = g_timeout_add_seconds(interval,
+                                               pairable_timeout_handler,
+                                               adapter);
+}
+
+void btd_adapter_pairable_changed(struct btd_adapter *adapter,
+                                                       gboolean pairable)
+{
+       adapter->pairable = pairable;
+
+       write_device_pairable(&adapter->bdaddr, pairable);
+
+       emit_property_changed(connection, adapter->path,
+                               ADAPTER_INTERFACE, "Pairable",
+                               DBUS_TYPE_BOOLEAN, &pairable);
+
+       if (pairable && adapter->pairable_timeout)
+               adapter_set_pairable_timeout(adapter,
+                                               adapter->pairable_timeout);
+}
+
+static struct session_req *find_session(GSList *list, const char *sender)
+{
+       for (; list; list = list->next) {
+               struct session_req *req = list->data;
+
+               if (g_str_equal(req->owner, sender))
+                       return req;
+       }
+
+       return NULL;
+}
+
+static uint8_t get_needed_mode(struct btd_adapter *adapter, uint8_t mode)
+{
+       GSList *l;
+
+       if (adapter->global_mode > mode)
+               mode = adapter->global_mode;
+
+       for (l = adapter->mode_sessions; l; l = l->next) {
+               struct session_req *req = l->data;
+
+               if (req->mode > mode)
+                       mode = req->mode;
+       }
+
+       return mode;
+}
+
+static GSList *remove_bredr(GSList *all)
+{
+       GSList *l, *le;
+
+       for (l = all, le = NULL; l; l = l->next) {
+               struct remote_dev_info *dev = l->data;
+               if (dev->bdaddr_type == BDADDR_BREDR) {
+                       dev_info_free(dev);
+                       continue;
+               }
+
+               le = g_slist_append(le, dev);
+       }
+
+       g_slist_free(all);
+
+       return le;
+}
+
+/* Called when a session gets removed or the adapter is stopped */
+static void stop_discovery(struct btd_adapter *adapter)
+{
+       adapter->found_devices = remove_bredr(adapter->found_devices);
+
+       if (adapter->oor_devices) {
+               g_slist_free(adapter->oor_devices);
+               adapter->oor_devices = NULL;
+       }
+
+       /* Reset if suspended, otherwise remove timer (software scheduler)
+        * or request inquiry to stop */
+       if (adapter->discov_suspended) {
+               adapter->discov_suspended = FALSE;
+               return;
+       }
+
+       if (adapter->discov_id > 0) {
+               g_source_remove(adapter->discov_id);
+               adapter->discov_id = 0;
+               return;
+       }
+
+       if (adapter->up)
+               adapter_ops->stop_discovery(adapter->dev_id);
+}
+
+static void session_remove(struct session_req *req)
+{
+       struct btd_adapter *adapter = req->adapter;
+
+       /* Ignore set_mode session */
+       if (req->owner == NULL)
+               return;
+
+       DBG("%s session %p with %s deactivated",
+               req->mode ? "Mode" : "Discovery", req, req->owner);
+
+       if (req->mode) {
+               uint8_t mode;
+
+               adapter->mode_sessions = g_slist_remove(adapter->mode_sessions,
+                                                       req);
+
+               mode = get_needed_mode(adapter, adapter->global_mode);
+
+               if (mode == adapter->mode)
+                       return;
+
+               DBG("Switching to '%s' mode", mode2str(mode));
+
+               set_mode(adapter, mode, NULL);
+       } else {
+               adapter->disc_sessions = g_slist_remove(adapter->disc_sessions,
+                                                       req);
+
+               if (adapter->disc_sessions)
+                       return;
+
+               DBG("Stopping discovery");
+
+               stop_discovery(adapter);
+       }
+}
+
+static void session_free(void *data)
+{
+       struct session_req *req = data;
+
+       if (req->id)
+               g_dbus_remove_watch(req->conn, req->id);
+
+       if (req->msg) {
+               dbus_message_unref(req->msg);
+               if (!req->got_reply && req->mode && req->adapter->agent)
+                       agent_cancel(req->adapter->agent);
+       }
+
+       if (req->conn)
+               dbus_connection_unref(req->conn);
+       g_free(req->owner);
+       g_free(req);
+}
+
+static void session_owner_exit(DBusConnection *conn, void *user_data)
+{
+       struct session_req *req = user_data;
+
+       req->id = 0;
+
+       session_remove(req);
+       session_free(req);
+}
+
+static void session_unref(struct session_req *req)
+{
+       req->refcount--;
+
+       DBG("%p: ref=%d", req, req->refcount);
+
+       if (req->refcount)
+               return;
+
+       session_remove(req);
+       session_free(req);
+}
+
+static void confirm_mode_cb(struct agent *agent, DBusError *derr, void *data)
+{
+       struct session_req *req = data;
+       int err;
+       DBusMessage *reply;
+
+       req->got_reply = TRUE;
+
+       if (derr && dbus_error_is_set(derr)) {
+               reply = dbus_message_new_error(req->msg, derr->name,
+                                               derr->message);
+               g_dbus_send_message(req->conn, reply);
+               session_unref(req);
+               return;
+       }
+
+       err = set_mode(req->adapter, req->mode, req->msg);
+       if (err < 0)
+               reply = btd_error_failed(req->msg, strerror(-err));
+       else if (!req->adapter->pending_mode)
+               reply = dbus_message_new_method_return(req->msg);
+       else
+               reply = NULL;
+
+       if (reply) {
+               /*
+                * Send reply immediately only if there was an error changing
+                * mode, or change is not needed. Otherwise, reply is sent in
+                * set_mode_complete.
+                */
+               g_dbus_send_message(req->conn, reply);
+
+               dbus_message_unref(req->msg);
+               req->msg = NULL;
+       }
+
+       if (!find_session(req->adapter->mode_sessions, req->owner))
+               session_unref(req);
+}
+
+static DBusMessage *set_discoverable_timeout(DBusConnection *conn,
+                                                       DBusMessage *msg,
+                                                       uint32_t timeout,
+                                                       void *data)
+{
+       struct btd_adapter *adapter = data;
+       const char *path;
+
+       if (adapter->discov_timeout == timeout && timeout == 0)
+               return dbus_message_new_method_return(msg);
+
+       if (adapter->scan_mode & SCAN_INQUIRY)
+               adapter_ops->set_discoverable(adapter->dev_id, TRUE, timeout);
+
+       adapter->discov_timeout = timeout;
+
+       write_discoverable_timeout(&adapter->bdaddr, timeout);
+
+       path = dbus_message_get_path(msg);
+
+       emit_property_changed(conn, path,
+                               ADAPTER_INTERFACE, "DiscoverableTimeout",
+                               DBUS_TYPE_UINT32, &timeout);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_pairable_timeout(DBusConnection *conn,
+                                               DBusMessage *msg,
+                                               uint32_t timeout,
+                                               void *data)
+{
+       struct btd_adapter *adapter = data;
+       const char *path;
+
+       if (adapter->pairable_timeout == timeout && timeout == 0)
+               return dbus_message_new_method_return(msg);
+
+       if (adapter->pairable)
+               adapter_set_pairable_timeout(adapter, timeout);
+
+       adapter->pairable_timeout = timeout;
+
+       write_pairable_timeout(&adapter->bdaddr, timeout);
+
+       path = dbus_message_get_path(msg);
+
+       emit_property_changed(conn, path,
+                               ADAPTER_INTERFACE, "PairableTimeout",
+                               DBUS_TYPE_UINT32, &timeout);
+
+       return dbus_message_new_method_return(msg);
+}
+
+void btd_adapter_class_changed(struct btd_adapter *adapter, uint32_t new_class)
+{
+       uint8_t class[3];
+
+       class[2] = (new_class >> 16) & 0xff;
+       class[1] = (new_class >> 8) & 0xff;
+       class[0] = new_class & 0xff;
+
+       write_local_class(&adapter->bdaddr, class);
+
+       adapter->dev_class = new_class;
+
+       if (main_opts.gatt_enabled) {
+               /* Removes service class */
+               class[1] = class[1] & 0x1f;
+               attrib_gap_set(adapter, GATT_CHARAC_APPEARANCE, class, 2);
+       }
+
+       emit_property_changed(connection, adapter->path,
+                               ADAPTER_INTERFACE, "Class",
+                               DBUS_TYPE_UINT32, &new_class);
+}
+
+void adapter_name_changed(struct btd_adapter *adapter, const char *name)
+{
+       if (g_strcmp0(adapter->name, name) == 0)
+               return;
+
+       g_free(adapter->name);
+       adapter->name = g_strdup(name);
+
+       if (connection)
+               emit_property_changed(connection, adapter->path,
+                                       ADAPTER_INTERFACE, "Name",
+                                       DBUS_TYPE_STRING, &name);
+
+       if (main_opts.gatt_enabled)
+               attrib_gap_set(adapter, GATT_CHARAC_DEVICE_NAME,
+                               (const uint8_t *) name, strlen(name));
+}
+
+int adapter_set_name(struct btd_adapter *adapter, const char *name)
+{
+       char maxname[MAX_NAME_LENGTH + 1];
+
+       if (g_strcmp0(adapter->name, name) == 0)
+               return 0;
+
+       memset(maxname, 0, sizeof(maxname));
+       strncpy(maxname, name, MAX_NAME_LENGTH);
+       if (!g_utf8_validate(maxname, -1, NULL)) {
+               error("Name change failed: supplied name isn't valid UTF-8");
+               return -EINVAL;
+       }
+
+       if (adapter->up) {
+               int err = adapter_ops->set_name(adapter->dev_id, maxname);
+               if (err < 0)
+                       return err;
+       } else {
+               g_free(adapter->name);
+               adapter->name = g_strdup(maxname);
+       }
+
+       write_local_name(&adapter->bdaddr, maxname);
+
+       return 0;
+}
+
+static DBusMessage *set_name(DBusConnection *conn, DBusMessage *msg,
+                                       const char *name, void *data)
+{
+       struct btd_adapter *adapter = data;
+       int ret;
+
+       if (adapter->allow_name_changes == FALSE)
+               return btd_error_failed(msg, strerror(EPERM));
+
+       ret = adapter_set_name(adapter, name);
+       if (ret == -EINVAL)
+               return btd_error_invalid_args(msg);
+       else if (ret < 0)
+               return btd_error_failed(msg, strerror(-ret));
+
+       return dbus_message_new_method_return(msg);
+}
+
+struct btd_device *adapter_find_device(struct btd_adapter *adapter,
+                                                       const char *dest)
+{
+       struct btd_device *device;
+       GSList *l;
+
+       if (!adapter)
+               return NULL;
+
+       l = g_slist_find_custom(adapter->devices, dest,
+                                       (GCompareFunc) device_address_cmp);
+       if (!l)
+               return NULL;
+
+       device = l->data;
+
+       return device;
+}
+
+static void adapter_update_devices(struct btd_adapter *adapter)
+{
+       char **devices;
+       int i;
+       GSList *l;
+
+       /* Devices */
+       devices = g_new0(char *, g_slist_length(adapter->devices) + 1);
+       for (i = 0, l = adapter->devices; l; l = l->next, i++) {
+               struct btd_device *dev = l->data;
+               devices[i] = (char *) device_get_path(dev);
+       }
+
+       emit_array_property_changed(connection, adapter->path,
+                                       ADAPTER_INTERFACE, "Devices",
+                                       DBUS_TYPE_OBJECT_PATH, &devices, i);
+       g_free(devices);
+}
+
+static void adapter_emit_uuids_updated(struct btd_adapter *adapter)
+{
+       char **uuids;
+       int i;
+       sdp_list_t *list;
+
+       if (!adapter->initialized)
+               return;
+
+       uuids = g_new0(char *, sdp_list_len(adapter->services) + 1);
+
+       for (i = 0, list = adapter->services; list; list = list->next) {
+               char *uuid;
+               sdp_record_t *rec = list->data;
+
+               uuid = bt_uuid2string(&rec->svclass);
+               if (uuid)
+                       uuids[i++] = uuid;
+       }
+
+       emit_array_property_changed(connection, adapter->path,
+                       ADAPTER_INTERFACE, "UUIDs", DBUS_TYPE_STRING, &uuids, i);
+
+       g_strfreev(uuids);
+}
+
+static uint8_t get_uuid_mask(uuid_t *uuid)
+{
+       if (uuid->type != SDP_UUID16)
+               return 0;
+
+       switch (uuid->value.uuid16) {
+       case DIALUP_NET_SVCLASS_ID:
+       case CIP_SVCLASS_ID:
+               return 0x42;    /* Telephony & Networking */
+       case IRMC_SYNC_SVCLASS_ID:
+       case OBEX_OBJPUSH_SVCLASS_ID:
+       case OBEX_FILETRANS_SVCLASS_ID:
+       case IRMC_SYNC_CMD_SVCLASS_ID:
+       case PBAP_PSE_SVCLASS_ID:
+               return 0x10;    /* Object Transfer */
+       case HEADSET_SVCLASS_ID:
+       case HANDSFREE_SVCLASS_ID:
+               return 0x20;    /* Audio */
+       case CORDLESS_TELEPHONY_SVCLASS_ID:
+       case INTERCOM_SVCLASS_ID:
+       case FAX_SVCLASS_ID:
+       case SAP_SVCLASS_ID:
+       /*
+        * Setting the telephony bit for the handsfree audio gateway
+        * role is not required by the HFP specification, but the
+        * Nokia 616 carkit is just plain broken! It will refuse
+        * pairing without this bit set.
+        */
+       case HANDSFREE_AGW_SVCLASS_ID:
+               return 0x40;    /* Telephony */
+       case AUDIO_SOURCE_SVCLASS_ID:
+       case VIDEO_SOURCE_SVCLASS_ID:
+               return 0x08;    /* Capturing */
+       case AUDIO_SINK_SVCLASS_ID:
+       case VIDEO_SINK_SVCLASS_ID:
+               return 0x04;    /* Rendering */
+       case PANU_SVCLASS_ID:
+       case NAP_SVCLASS_ID:
+       case GN_SVCLASS_ID:
+               return 0x02;    /* Networking */
+       default:
+               return 0;
+       }
+}
+
+static int uuid_cmp(const void *a, const void *b)
+{
+       const sdp_record_t *rec = a;
+       const uuid_t *uuid = b;
+
+       return sdp_uuid_cmp(&rec->svclass, uuid);
+}
+
+void adapter_service_insert(struct btd_adapter *adapter, void *r)
+{
+       sdp_record_t *rec = r;
+       gboolean new_uuid;
+
+       if (sdp_list_find(adapter->services, &rec->svclass, uuid_cmp) == NULL)
+               new_uuid = TRUE;
+       else
+               new_uuid = FALSE;
+
+       adapter->services = sdp_list_insert_sorted(adapter->services, rec,
+                                                               record_sort);
+
+       if (new_uuid) {
+               uint8_t svc_hint = get_uuid_mask(&rec->svclass);
+               adapter_ops->add_uuid(adapter->dev_id, &rec->svclass, svc_hint);
+       }
+
+       adapter_emit_uuids_updated(adapter);
+}
+
+void adapter_service_remove(struct btd_adapter *adapter, void *r)
+{
+       sdp_record_t *rec = r;
+
+       adapter->services = sdp_list_remove(adapter->services, rec);
+
+       if (sdp_list_find(adapter->services, &rec->svclass, uuid_cmp) == NULL)
+               adapter_ops->remove_uuid(adapter->dev_id, &rec->svclass);
+
+       adapter_emit_uuids_updated(adapter);
+}
+
+static struct btd_device *adapter_create_device(DBusConnection *conn,
+                                               struct btd_adapter *adapter,
+                                               const char *address,
+                                               uint8_t bdaddr_type)
+{
+       struct btd_device *device;
+       const char *path;
+
+       DBG("%s", address);
+
+       device = device_create(conn, adapter, address, bdaddr_type);
+       if (!device)
+               return NULL;
+
+       device_set_temporary(device, TRUE);
+
+       adapter->devices = g_slist_append(adapter->devices, device);
+
+       path = device_get_path(device);
+       g_dbus_emit_signal(conn, adapter->path,
+                       ADAPTER_INTERFACE, "DeviceCreated",
+                       DBUS_TYPE_OBJECT_PATH, &path,
+                       DBUS_TYPE_INVALID);
+
+       adapter_update_devices(adapter);
+
+       return device;
+}
+
+void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter,
+                                               struct btd_device *device,
+                                               gboolean remove_storage)
+{
+       const gchar *dev_path = device_get_path(device);
+       struct agent *agent;
+
+       adapter->devices = g_slist_remove(adapter->devices, device);
+       adapter->connections = g_slist_remove(adapter->connections, device);
+
+       adapter_update_devices(adapter);
+
+       g_dbus_emit_signal(conn, adapter->path,
+                       ADAPTER_INTERFACE, "DeviceRemoved",
+                       DBUS_TYPE_OBJECT_PATH, &dev_path,
+                       DBUS_TYPE_INVALID);
+
+       agent = device_get_agent(device);
+
+       if (agent && device_is_authorizing(device))
+               agent_cancel(agent);
+
+       device_remove(device, remove_storage);
+}
+
+struct btd_device *adapter_get_device(DBusConnection *conn,
+                                               struct btd_adapter *adapter,
+                                               const gchar *address)
+{
+       struct btd_device *device;
+
+       DBG("%s", address);
+
+       if (!adapter)
+               return NULL;
+
+       device = adapter_find_device(adapter, address);
+       if (device)
+               return device;
+
+       return adapter_create_device(conn, adapter, address,
+                                               BDADDR_BREDR);
+}
+
+static gboolean discovery_cb(gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       int err;
+
+       adapter->discov_id = 0;
+
+       err = adapter_ops->start_discovery(adapter->dev_id);
+       if (err < 0)
+               error("start_discovery: %s (%d)", strerror(-err), -err);
+
+       return FALSE;
+}
+
+static DBusMessage *adapter_start_discovery(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct session_req *req;
+       struct btd_adapter *adapter = data;
+       const char *sender = dbus_message_get_sender(msg);
+       int err;
+
+       if (!adapter->up)
+               return btd_error_not_ready(msg);
+
+       req = find_session(adapter->disc_sessions, sender);
+       if (req) {
+               session_ref(req);
+               return dbus_message_new_method_return(msg);
+       }
+
+       if (adapter->disc_sessions)
+               goto done;
+
+       g_slist_free_full(adapter->found_devices, dev_info_free);
+       adapter->found_devices = NULL;
+
+       g_slist_free(adapter->oor_devices);
+       adapter->oor_devices = NULL;
+
+       if (adapter->discov_suspended)
+               goto done;
+
+       err = adapter_ops->start_discovery(adapter->dev_id);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+done:
+       req = create_session(adapter, conn, msg, 0,
+                               session_owner_exit);
+
+       adapter->disc_sessions = g_slist_append(adapter->disc_sessions, req);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_stop_discovery(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct session_req *req;
+       const char *sender = dbus_message_get_sender(msg);
+
+       if (!adapter->up)
+               return btd_error_not_ready(msg);
+
+       req = find_session(adapter->disc_sessions, sender);
+       if (!req)
+               return btd_error_failed(msg, "Invalid discovery session");
+
+       session_unref(req);
+       info("Stopping discovery");
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       const char *property;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       char srcaddr[18];
+       gboolean value;
+       char **devices, **uuids;
+       int i;
+       GSList *l;
+       sdp_list_t *list;
+
+       ba2str(&adapter->bdaddr, srcaddr);
+
+       if (check_address(srcaddr) < 0)
+               return btd_error_invalid_args(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       /* Address */
+       property = srcaddr;
+       dict_append_entry(&dict, "Address", DBUS_TYPE_STRING, &property);
+
+       /* Name */
+       property = adapter->name ? : "";
+
+       dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &property);
+
+       /* Class */
+       dict_append_entry(&dict, "Class",
+                               DBUS_TYPE_UINT32, &adapter->dev_class);
+
+       /* Powered */
+       value = (adapter->up && !adapter->off_requested) ? TRUE : FALSE;
+       dict_append_entry(&dict, "Powered", DBUS_TYPE_BOOLEAN, &value);
+
+       /* Discoverable */
+       value = adapter->scan_mode & SCAN_INQUIRY ? TRUE : FALSE;
+       dict_append_entry(&dict, "Discoverable", DBUS_TYPE_BOOLEAN, &value);
+
+       /* Pairable */
+       dict_append_entry(&dict, "Pairable", DBUS_TYPE_BOOLEAN,
+                               &adapter->pairable);
+
+       /* DiscoverableTimeout */
+       dict_append_entry(&dict, "DiscoverableTimeout",
+                               DBUS_TYPE_UINT32, &adapter->discov_timeout);
+
+       /* PairableTimeout */
+       dict_append_entry(&dict, "PairableTimeout",
+                               DBUS_TYPE_UINT32, &adapter->pairable_timeout);
+
+
+       /* Discovering */
+       dict_append_entry(&dict, "Discovering", DBUS_TYPE_BOOLEAN,
+                                                       &adapter->discovering);
+
+       /* Devices */
+       devices = g_new0(char *, g_slist_length(adapter->devices) + 1);
+       for (i = 0, l = adapter->devices; l; l = l->next, i++) {
+               struct btd_device *dev = l->data;
+               devices[i] = (char *) device_get_path(dev);
+       }
+       dict_append_array(&dict, "Devices", DBUS_TYPE_OBJECT_PATH,
+                                                               &devices, i);
+       g_free(devices);
+
+       /* UUIDs */
+       uuids = g_new0(char *, sdp_list_len(adapter->services) + 1);
+
+       for (i = 0, list = adapter->services; list; list = list->next) {
+               sdp_record_t *rec = list->data;
+               char *uuid;
+
+               uuid = bt_uuid2string(&rec->svclass);
+               if (uuid)
+                       uuids[i++] = uuid;
+       }
+
+       dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING, &uuids, i);
+
+       g_strfreev(uuids);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter;
+       DBusMessageIter sub;
+       const char *property;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return btd_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return btd_error_invalid_args(msg);
+       dbus_message_iter_recurse(&iter, &sub);
+
+       if (g_str_equal("Name", property)) {
+               const char *name;
+
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+                       return btd_error_invalid_args(msg);
+               dbus_message_iter_get_basic(&sub, &name);
+
+               return set_name(conn, msg, name, data);
+       } else if (g_str_equal("Powered", property)) {
+               gboolean powered;
+
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+                       return btd_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&sub, &powered);
+
+               return set_powered(conn, msg, powered, data);
+       } else if (g_str_equal("Discoverable", property)) {
+               gboolean discoverable;
+
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+                       return btd_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&sub, &discoverable);
+
+               return set_discoverable(conn, msg, discoverable, data);
+       } else if (g_str_equal("DiscoverableTimeout", property)) {
+               uint32_t timeout;
+
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32)
+                       return btd_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&sub, &timeout);
+
+               return set_discoverable_timeout(conn, msg, timeout, data);
+       } else if (g_str_equal("Pairable", property)) {
+               gboolean pairable;
+
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+                       return btd_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&sub, &pairable);
+
+               return set_pairable(conn, msg, pairable, data);
+       } else if (g_str_equal("PairableTimeout", property)) {
+               uint32_t timeout;
+
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32)
+                       return btd_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&sub, &timeout);
+
+               return set_pairable_timeout(conn, msg, timeout, data);
+       }
+
+       return btd_error_invalid_args(msg);
+}
+
+static DBusMessage *request_session(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct session_req *req;
+       const char *sender = dbus_message_get_sender(msg);
+       uint8_t new_mode;
+       int err;
+
+       if (!adapter->agent)
+               return btd_error_agent_not_available(msg);
+
+       if (!adapter->mode_sessions)
+               adapter->global_mode = adapter->mode;
+
+       new_mode = get_mode(&adapter->bdaddr, "on");
+
+       req = find_session(adapter->mode_sessions, sender);
+       if (req) {
+               session_ref(req);
+               return dbus_message_new_method_return(msg);
+       } else {
+               req = create_session(adapter, conn, msg, new_mode,
+                                       session_owner_exit);
+               adapter->mode_sessions = g_slist_append(adapter->mode_sessions,
+                                                       req);
+       }
+
+       /* No need to change mode */
+       if (adapter->mode >= new_mode)
+               return dbus_message_new_method_return(msg);
+
+       err = agent_confirm_mode_change(adapter->agent, mode2str(new_mode),
+                                       confirm_mode_cb, req, NULL);
+       if (err < 0) {
+               session_unref(req);
+               return btd_error_failed(msg, strerror(-err));
+       }
+
+       return NULL;
+}
+
+static DBusMessage *release_session(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct session_req *req;
+       const char *sender = dbus_message_get_sender(msg);
+
+       req = find_session(adapter->mode_sessions, sender);
+       if (!req)
+               return btd_error_failed(msg, "Invalid Session");
+
+       session_unref(req);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *list_devices(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       DBusMessage *reply;
+       GSList *l;
+       DBusMessageIter iter;
+       DBusMessageIter array_iter;
+       const gchar *dev_path;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
+
+       for (l = adapter->devices; l; l = l->next) {
+               struct btd_device *device = l->data;
+
+               dev_path = device_get_path(device);
+
+               dbus_message_iter_append_basic(&array_iter,
+                               DBUS_TYPE_OBJECT_PATH, &dev_path);
+       }
+
+       dbus_message_iter_close_container(&iter, &array_iter);
+
+       return reply;
+}
+
+static DBusMessage *cancel_device_creation(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       const gchar *address, *sender = dbus_message_get_sender(msg);
+       struct btd_device *device;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       if (check_address(address) < 0)
+               return btd_error_invalid_args(msg);
+
+       device = adapter_find_device(adapter, address);
+       if (!device || !device_is_creating(device, NULL))
+               return btd_error_does_not_exist(msg);
+
+       if (!device_is_creating(device, sender))
+               return btd_error_not_authorized(msg);
+
+       device_set_temporary(device, TRUE);
+
+       if (device_is_connected(device)) {
+               device_request_disconnect(device, msg);
+               return NULL;
+       }
+
+       adapter_remove_device(conn, adapter, device, TRUE);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static struct btd_device *create_device_internal(DBusConnection *conn,
+                                               struct btd_adapter *adapter,
+                                               const char *address, int *err)
+{
+       struct remote_dev_info *dev;
+       struct btd_device *device;
+       bdaddr_t addr;
+       uint8_t bdaddr_type;
+
+       str2ba(address, &addr);
+
+       dev = adapter_search_found_devices(adapter, &addr);
+       if (dev)
+               bdaddr_type = dev->bdaddr_type;
+       else
+               bdaddr_type = BDADDR_BREDR;
+
+       device = adapter_create_device(conn, adapter, address, bdaddr_type);
+       if (!device && err)
+               *err = -ENOMEM;
+
+       return device;
+}
+
+static DBusMessage *create_device(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct btd_device *device;
+       const gchar *address;
+       DBusMessage *reply;
+       int err;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       if (check_address(address) < 0)
+               return btd_error_invalid_args(msg);
+
+       if (!adapter->up)
+               return btd_error_not_ready(msg);
+
+       if (adapter_find_device(adapter, address))
+               return btd_error_already_exists(msg);
+
+       DBG("%s", address);
+
+       device = create_device_internal(conn, adapter, address, &err);
+       if (!device)
+               goto failed;
+
+       if (device_is_bredr(device))
+               err = device_browse_sdp(device, conn, msg, NULL, FALSE);
+       else
+               err = device_browse_primary(device, conn, msg, FALSE);
+
+       if (err < 0) {
+               adapter_remove_device(conn, adapter, device, TRUE);
+               return btd_error_failed(msg, strerror(-err));
+       }
+
+       return NULL;
+
+failed:
+       if (err == -ENOTCONN) {
+               /* Device is not connectable */
+               const char *path = device_get_path(device);
+
+               reply = dbus_message_new_method_return(msg);
+
+               dbus_message_append_args(reply,
+                               DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+       } else
+               reply = btd_error_failed(msg, strerror(-err));
+
+       return reply;
+}
+
+static uint8_t parse_io_capability(const char *capability)
+{
+       if (g_str_equal(capability, ""))
+               return IO_CAPABILITY_DISPLAYYESNO;
+       if (g_str_equal(capability, "DisplayOnly"))
+               return IO_CAPABILITY_DISPLAYONLY;
+       if (g_str_equal(capability, "DisplayYesNo"))
+               return IO_CAPABILITY_DISPLAYYESNO;
+       if (g_str_equal(capability, "KeyboardOnly"))
+               return IO_CAPABILITY_KEYBOARDONLY;
+       if (g_str_equal(capability, "NoInputNoOutput"))
+               return IO_CAPABILITY_NOINPUTNOOUTPUT;
+       if (g_str_equal(capability, "KeyboardDisplay"))
+               return IO_CAPABILITY_KEYBOARDDISPLAY;
+       return IO_CAPABILITY_INVALID;
+}
+
+static DBusMessage *create_paired_device(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct btd_device *device;
+       const gchar *address, *agent_path, *capability, *sender;
+       uint8_t cap;
+       int err;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+                                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_STRING, &capability,
+                                       DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       if (check_address(address) < 0)
+               return btd_error_invalid_args(msg);
+
+       if (!adapter->up)
+               return btd_error_not_ready(msg);
+
+       sender = dbus_message_get_sender(msg);
+       if (adapter->agent &&
+                       agent_matches(adapter->agent, sender, agent_path)) {
+               error("Refusing adapter agent usage as device specific one");
+               return btd_error_invalid_args(msg);
+       }
+
+       cap = parse_io_capability(capability);
+       if (cap == IO_CAPABILITY_INVALID)
+               return btd_error_invalid_args(msg);
+
+       device = adapter_find_device(adapter, address);
+       if (!device) {
+               device = create_device_internal(conn, adapter, address, &err);
+               if (!device)
+                       return btd_error_failed(msg, strerror(-err));
+       }
+
+       return device_create_bonding(device, conn, msg, agent_path, cap);
+}
+
+static gint device_path_cmp(struct btd_device *device, const gchar *path)
+{
+       const gchar *dev_path = device_get_path(device);
+
+       return strcasecmp(dev_path, path);
+}
+
+static DBusMessage *remove_device(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct btd_device *device;
+       const char *path;
+       GSList *l;
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       l = g_slist_find_custom(adapter->devices,
+                       path, (GCompareFunc) device_path_cmp);
+       if (!l)
+               return btd_error_does_not_exist(msg);
+
+       device = l->data;
+
+       if (device_is_temporary(device) || device_is_busy(device))
+               return g_dbus_create_error(msg,
+                               ERROR_INTERFACE ".DoesNotExist",
+                               "Device creation in progress");
+
+       device_set_temporary(device, TRUE);
+
+       if (!device_is_connected(device)) {
+               adapter_remove_device(conn, adapter, device, TRUE);
+               return dbus_message_new_method_return(msg);
+       }
+
+       device_request_disconnect(device, msg);
+       return NULL;
+}
+
+static DBusMessage *find_device(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct btd_adapter *adapter = data;
+       struct btd_device *device;
+       DBusMessage *reply;
+       const gchar *address;
+       GSList *l;
+       const gchar *dev_path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+                                               DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       l = g_slist_find_custom(adapter->devices,
+                       address, (GCompareFunc) device_address_cmp);
+       if (!l)
+               return btd_error_does_not_exist(msg);
+
+       device = l->data;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dev_path = device_get_path(device);
+
+       dbus_message_append_args(reply,
+                               DBUS_TYPE_OBJECT_PATH, &dev_path,
+                               DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static void agent_removed(struct agent *agent, struct btd_adapter *adapter)
+{
+       adapter_ops->set_io_capability(adapter->dev_id,
+                                       IO_CAPABILITY_NOINPUTNOOUTPUT);
+
+       adapter->agent = NULL;
+}
+
+static DBusMessage *register_agent(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *path, *name, *capability;
+       struct btd_adapter *adapter = data;
+       uint8_t cap;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                       DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID))
+               return NULL;
+
+       if (adapter->agent)
+               return btd_error_already_exists(msg);
+
+       cap = parse_io_capability(capability);
+       if (cap == IO_CAPABILITY_INVALID)
+               return btd_error_invalid_args(msg);
+
+       name = dbus_message_get_sender(msg);
+
+       adapter->agent = agent_create(adapter, name, path, cap,
+                               (agent_remove_cb) agent_removed, adapter);
+
+       DBG("Agent registered for hci%d at %s:%s", adapter->dev_id, name,
+                       path);
+
+       adapter_ops->set_io_capability(adapter->dev_id, cap);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_agent(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *path, *name;
+       struct btd_adapter *adapter = data;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID))
+               return NULL;
+
+       name = dbus_message_get_sender(msg);
+
+       if (!adapter->agent || !agent_matches(adapter->agent, name, path))
+               return btd_error_does_not_exist(msg);
+
+       agent_free(adapter->agent);
+       adapter->agent = NULL;
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable adapter_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_ASYNC_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+                       set_property) },
+       { GDBUS_ASYNC_METHOD("RequestSession", NULL, NULL,
+                       request_session) },
+       { GDBUS_METHOD("ReleaseSession", NULL, NULL,
+                       release_session) },
+       { GDBUS_METHOD("StartDiscovery", NULL, NULL,
+                       adapter_start_discovery) },
+       { GDBUS_ASYNC_METHOD("StopDiscovery", NULL, NULL,
+                       adapter_stop_discovery) },
+       { GDBUS_DEPRECATED_METHOD("ListDevices",
+                       NULL, GDBUS_ARGS({ "devices", "ao" }),
+                       list_devices) },
+       { GDBUS_ASYNC_METHOD("CreateDevice",
+                       GDBUS_ARGS({ "address", "s" }),
+                       GDBUS_ARGS({ "device", "o" }),
+                       create_device) },
+       { GDBUS_ASYNC_METHOD("CreatePairedDevice",
+                       GDBUS_ARGS({ "address", "s" }, { "agent", "o" },
+                                                       { "capability", "s" }),
+                       GDBUS_ARGS({ "device", "o" }),
+                       create_paired_device) },
+       { GDBUS_ASYNC_METHOD("CancelDeviceCreation",
+                       GDBUS_ARGS({ "address", "s" }), NULL,
+                       cancel_device_creation) },
+       { GDBUS_ASYNC_METHOD("RemoveDevice",
+                       GDBUS_ARGS({ "device", "o" }), NULL,
+                       remove_device) },
+       { GDBUS_METHOD("FindDevice",
+                       GDBUS_ARGS({ "address", "s" }),
+                       GDBUS_ARGS({ "device", "o" }),
+                       find_device) },
+       { GDBUS_METHOD("RegisterAgent",
+                       GDBUS_ARGS({ "agent", "o" },
+                                       { "capability", "s" }), NULL,
+                       register_agent) },
+       { GDBUS_METHOD("UnregisterAgent",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       unregister_agent) },
+       { }
+};
+
+static const GDBusSignalTable adapter_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { GDBUS_SIGNAL("DeviceCreated",
+                       GDBUS_ARGS({ "device", "o" })) },
+       { GDBUS_SIGNAL("DeviceRemoved",
+                       GDBUS_ARGS({ "device", "o" })) },
+       { GDBUS_SIGNAL("DeviceFound",
+                       GDBUS_ARGS({ "address", "s" },
+                                               { "values", "a{sv}" })) },
+       { GDBUS_SIGNAL("DeviceDisappeared",
+                       GDBUS_ARGS({ "address", "s" })) },
+       { }
+};
+
+static void create_stored_device_from_profiles(char *key, char *value,
+                                               void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       GSList *list, *uuids = bt_string2list(value);
+       struct btd_device *device;
+
+       if (g_slist_find_custom(adapter->devices,
+                               key, (GCompareFunc) device_address_cmp))
+               return;
+
+       device = device_create(connection, adapter, key, BDADDR_BREDR);
+       if (!device)
+               return;
+
+       device_set_temporary(device, FALSE);
+       adapter->devices = g_slist_append(adapter->devices, device);
+
+       list = device_services_from_record(device, uuids);
+       if (list)
+               device_register_services(connection, device, list, ATT_PSM);
+
+       device_probe_drivers(device, uuids);
+
+       g_slist_free_full(uuids, g_free);
+}
+
+struct adapter_keys {
+       struct btd_adapter *adapter;
+       GSList *keys;
+};
+
+static int str2buf(const char *str, uint8_t *buf, size_t blen)
+{
+       int i, dlen;
+
+       if (str == NULL)
+               return -EINVAL;
+
+       memset(buf, 0, blen);
+
+       dlen = MIN((strlen(str) / 2), blen);
+
+       for (i = 0; i < dlen; i++)
+               sscanf(str + (i * 2), "%02hhX", &buf[i]);
+
+       return 0;
+}
+
+static struct link_key_info *get_key_info(const char *addr, const char *value)
+{
+       struct link_key_info *info;
+       char tmp[3];
+       long int l;
+
+       if (strlen(value) < 36) {
+               error("Unexpectedly short (%zu) link key line", strlen(value));
+               return NULL;
+       }
+
+       info = g_new0(struct link_key_info, 1);
+
+       str2ba(addr, &info->bdaddr);
+
+       str2buf(value, info->key, sizeof(info->key));
+
+       memcpy(tmp, value + 33, 2);
+       info->type = (uint8_t) strtol(tmp, NULL, 10);
+
+       memcpy(tmp, value + 35, 2);
+       l = strtol(tmp, NULL, 10);
+       if (l < 0)
+               l = 0;
+       info->pin_len = l;
+
+       return info;
+}
+
+static struct smp_ltk_info *get_ltk_info(const char *addr, uint8_t bdaddr_type,
+                                                       const char *value)
+{
+       struct smp_ltk_info *ltk;
+       char *ptr;
+       int i, ret;
+
+       if (strlen(value) < 60) {
+               error("Unexpectedly short (%zu) LTK", strlen(value));
+               return NULL;
+       }
+
+       ltk = g_new0(struct smp_ltk_info, 1);
+
+       str2ba(addr, &ltk->bdaddr);
+
+       ltk->bdaddr_type = bdaddr_type;
+
+       str2buf(value, ltk->val, sizeof(ltk->val));
+
+       ptr = (char *) value + 2 * sizeof(ltk->val) + 1;
+
+       ret = sscanf(ptr, " %hhd %hhd %hhd %hd %n",
+                    &ltk->authenticated, &ltk->master, &ltk->enc_size,
+                                                       &ltk->ediv, &i);
+       if (ret < 2) {
+               g_free(ltk);
+               return NULL;
+       }
+       ptr += i;
+
+       str2buf(ptr, ltk->rand, sizeof(ltk->rand));
+
+       return ltk;
+}
+
+static void create_stored_device_from_linkkeys(char *key, char *value,
+                                                       void *user_data)
+{
+       struct adapter_keys *keys = user_data;
+       struct btd_adapter *adapter = keys->adapter;
+       struct btd_device *device;
+       struct link_key_info *info;
+
+       info = get_key_info(key, value);
+       if (info)
+               keys->keys = g_slist_append(keys->keys, info);
+
+       if (g_slist_find_custom(adapter->devices, key,
+                                       (GCompareFunc) device_address_cmp))
+               return;
+
+       device = device_create(connection, adapter, key, BDADDR_BREDR);
+       if (device) {
+               device_set_temporary(device, FALSE);
+               adapter->devices = g_slist_append(adapter->devices, device);
+       }
+}
+
+static void create_stored_device_from_ltks(char *key, char *value,
+                                                       void *user_data)
+{
+       struct adapter_keys *keys = user_data;
+       struct btd_adapter *adapter = keys->adapter;
+       struct btd_device *device;
+       struct smp_ltk_info *info;
+       char address[18], srcaddr[18];
+       uint8_t bdaddr_type;
+       bdaddr_t src;
+
+       if (sscanf(key, "%17s#%hhu", address, &bdaddr_type) < 2)
+               return;
+
+       info = get_ltk_info(address, bdaddr_type, value);
+       if (info == NULL)
+               return;
+
+       keys->keys = g_slist_append(keys->keys, info);
+
+       if (g_slist_find_custom(adapter->devices, address,
+                                       (GCompareFunc) device_address_cmp))
+               return;
+
+       adapter_get_address(adapter, &src);
+       ba2str(&src, srcaddr);
+
+       if (g_strcmp0(srcaddr, address) == 0)
+               return;
+
+       device = device_create(connection, adapter, address, bdaddr_type);
+       if (device) {
+               device_set_temporary(device, FALSE);
+               adapter->devices = g_slist_append(adapter->devices, device);
+       }
+}
+
+static void create_stored_device_from_blocked(char *key, char *value,
+                                                       void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *device;
+
+       if (g_slist_find_custom(adapter->devices,
+                               key, (GCompareFunc) device_address_cmp))
+               return;
+
+       device = device_create(connection, adapter, key, BDADDR_BREDR);
+       if (device) {
+               device_set_temporary(device, FALSE);
+               adapter->devices = g_slist_append(adapter->devices, device);
+       }
+}
+
+static GSList *string_to_primary_list(char *str)
+{
+       GSList *l = NULL;
+       char **services;
+       int i;
+
+       if (str == NULL)
+               return NULL;
+
+       services = g_strsplit(str, " ", 0);
+       if (services == NULL)
+               return NULL;
+
+       for (i = 0; services[i]; i++) {
+               struct gatt_primary *prim;
+               int ret;
+
+               prim = g_new0(struct gatt_primary, 1);
+
+               ret = sscanf(services[i], "%04hX#%04hX#%s", &prim->range.start,
+                                                       &prim->range.end, prim->uuid);
+
+               if (ret < 3) {
+                       g_free(prim);
+                       continue;
+               }
+
+               l = g_slist_append(l, prim);
+       }
+
+       g_strfreev(services);
+
+       return l;
+}
+
+static void create_stored_device_from_primaries(char *key, char *value,
+                                                       void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *device;
+       GSList *services, *uuids, *l;
+       char address[18];
+       uint8_t bdaddr_type;
+
+       if (sscanf(key, "%17s#%hhu", address, &bdaddr_type) < 2)
+               return;
+
+       if (g_slist_find_custom(adapter->devices,
+                       address, (GCompareFunc) device_address_cmp))
+               return;
+
+       device = device_create(connection, adapter, address, bdaddr_type);
+       if (!device)
+               return;
+
+       device_set_temporary(device, FALSE);
+       adapter->devices = g_slist_append(adapter->devices, device);
+
+       services = string_to_primary_list(value);
+       if (services == NULL)
+               return;
+
+       for (l = services, uuids = NULL; l; l = l->next) {
+               struct gatt_primary *prim = l->data;
+               uuids = g_slist_append(uuids, prim->uuid);
+       }
+
+       device_register_services(connection, device, services, -1);
+
+       device_probe_drivers(device, uuids);
+
+       g_slist_free(uuids);
+}
+
+static void smp_key_free(void *data)
+{
+       struct smp_ltk_info *info = data;
+
+       g_free(info);
+}
+
+static void load_devices(struct btd_adapter *adapter)
+{
+       char filename[PATH_MAX + 1];
+       char srcaddr[18];
+       struct adapter_keys keys = { adapter, NULL };
+       int err;
+
+       ba2str(&adapter->bdaddr, srcaddr);
+
+       create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "profiles");
+       textfile_foreach(filename, create_stored_device_from_profiles,
+                                                               adapter);
+
+       create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "primaries");
+       textfile_foreach(filename, create_stored_device_from_primaries,
+                                                               adapter);
+
+       create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "linkkeys");
+       textfile_foreach(filename, create_stored_device_from_linkkeys, &keys);
+
+       err = adapter_ops->load_keys(adapter->dev_id, keys.keys,
+                                                       main_opts.debug_keys);
+       if (err < 0)
+               error("Unable to load keys to adapter_ops: %s (%d)",
+                                                       strerror(-err), -err);
+
+       g_slist_free_full(keys.keys, g_free);
+       keys.keys = NULL;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "longtermkeys");
+       textfile_foreach(filename, create_stored_device_from_ltks, &keys);
+
+       err = adapter_ops->load_ltks(adapter->dev_id, keys.keys);
+       if (err < 0)
+               error("Unable to load keys to adapter_ops: %s (%d)",
+                                                       strerror(-err), -err);
+       g_slist_free_full(keys.keys, smp_key_free);
+       keys.keys = NULL;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "blocked");
+       textfile_foreach(filename, create_stored_device_from_blocked, adapter);
+}
+
+int btd_adapter_block_address(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type)
+{
+       return adapter_ops->block_device(adapter->dev_id, bdaddr, bdaddr_type);
+}
+
+int btd_adapter_unblock_address(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type)
+{
+       return adapter_ops->unblock_device(adapter->dev_id, bdaddr,
+                                                               bdaddr_type);
+}
+
+static void clear_blocked(struct btd_adapter *adapter)
+{
+       int err;
+
+       err = adapter_ops->unblock_device(adapter->dev_id, BDADDR_ANY, 0);
+       if (err < 0)
+               error("Clearing blocked list failed: %s (%d)",
+                                               strerror(-err), -err);
+}
+
+static void probe_driver(struct btd_adapter *adapter, gpointer user_data)
+{
+       struct btd_adapter_driver *driver = user_data;
+       int err;
+
+       if (driver->probe == NULL)
+               return;
+
+       err = driver->probe(adapter);
+       if (err < 0) {
+               error("%s: %s (%d)", driver->name, strerror(-err), -err);
+               return;
+       }
+
+       adapter->loaded_drivers = g_slist_prepend(adapter->loaded_drivers,
+                                                                       driver);
+}
+
+static void load_drivers(struct btd_adapter *adapter)
+{
+       GSList *l;
+
+       for (l = adapter_drivers; l; l = l->next)
+               probe_driver(adapter, l->data);
+}
+
+static void load_connections(struct btd_adapter *adapter)
+{
+       GSList *l, *conns;
+       int err;
+
+       err = adapter_ops->get_conn_list(adapter->dev_id, &conns);
+       if (err < 0) {
+               error("Unable to fetch existing connections: %s (%d)",
+                                                       strerror(-err), -err);
+               return;
+       }
+
+       for (l = conns; l != NULL; l = g_slist_next(l)) {
+               bdaddr_t *bdaddr = l->data;
+               struct btd_device *device;
+               char address[18];
+
+               ba2str(bdaddr, address);
+               DBG("Adding existing connection to %s", address);
+
+               device = adapter_get_device(connection, adapter, address);
+               if (device)
+                       adapter_add_connection(adapter, device);
+       }
+
+       g_slist_free_full(conns, g_free);
+}
+
+static int get_discoverable_timeout(const char *src)
+{
+       int timeout;
+
+       if (read_discoverable_timeout(src, &timeout) == 0)
+               return timeout;
+
+       return main_opts.discovto;
+}
+
+static int get_pairable_timeout(const char *src)
+{
+       int timeout;
+
+       if (read_pairable_timeout(src, &timeout) == 0)
+               return timeout;
+
+       return main_opts.pairto;
+}
+
+static void call_adapter_powered_callbacks(struct btd_adapter *adapter,
+                                               gboolean powered)
+{
+       GSList *l;
+
+       for (l = adapter->powered_callbacks; l; l = l->next) {
+               btd_adapter_powered_cb cb = l->data;
+
+               cb(adapter, powered);
+       }
+}
+
+static void emit_device_disappeared(gpointer data, gpointer user_data)
+{
+       struct remote_dev_info *dev = data;
+       struct btd_adapter *adapter = user_data;
+       char address[18];
+       const char *paddr = address;
+
+       ba2str(&dev->bdaddr, address);
+
+       g_dbus_emit_signal(connection, adapter->path,
+                       ADAPTER_INTERFACE, "DeviceDisappeared",
+                       DBUS_TYPE_STRING, &paddr,
+                       DBUS_TYPE_INVALID);
+
+       adapter->found_devices = g_slist_remove(adapter->found_devices, dev);
+}
+
+void btd_adapter_get_mode(struct btd_adapter *adapter, uint8_t *mode,
+                                               uint8_t *on_mode,
+                                               uint16_t *discoverable_timeout,
+                                               gboolean *pairable)
+{
+       char str[14], address[18];
+
+       ba2str(&adapter->bdaddr, address);
+
+       if (mode) {
+               if (main_opts.remember_powered == FALSE)
+                       *mode = main_opts.mode;
+               else if (read_device_mode(address, str, sizeof(str)) == 0)
+                       *mode = get_mode(&adapter->bdaddr, str);
+               else
+                       *mode = main_opts.mode;
+       }
+
+       if (on_mode)
+               *on_mode = get_mode(&adapter->bdaddr, "on");
+
+       if (discoverable_timeout)
+               *discoverable_timeout = get_discoverable_timeout(address);
+
+       if (pairable)
+               *pairable = adapter->pairable;
+}
+
+void btd_adapter_get_class(struct btd_adapter *adapter, uint8_t *major,
+                                                               uint8_t *minor)
+{
+       uint8_t cls[3];
+
+       if (read_local_class(&adapter->bdaddr, cls) < 0) {
+               uint32_t class = htobl(main_opts.class);
+               memcpy(cls, &class, 3);
+       }
+
+       *major = cls[1];
+       *minor = cls[0];
+}
+
+const char *btd_adapter_get_name(struct btd_adapter *adapter)
+{
+       return adapter->name;
+}
+
+void btd_adapter_start(struct btd_adapter *adapter)
+{
+       char address[18];
+       gboolean powered;
+
+       ba2str(&adapter->bdaddr, address);
+
+       adapter->dev_class = 0;
+       adapter->off_requested = FALSE;
+       adapter->up = TRUE;
+       adapter->discov_timeout = get_discoverable_timeout(address);
+       adapter->pairable_timeout = get_pairable_timeout(address);
+       adapter->off_timer = 0;
+
+       if (adapter->scan_mode & SCAN_INQUIRY)
+               adapter->mode = MODE_DISCOVERABLE;
+       else
+               adapter->mode = MODE_CONNECTABLE;
+
+       powered = TRUE;
+       emit_property_changed(connection, adapter->path,
+                                       ADAPTER_INTERFACE, "Powered",
+                                       DBUS_TYPE_BOOLEAN, &powered);
+
+       call_adapter_powered_callbacks(adapter, TRUE);
+
+       adapter_ops->disable_cod_cache(adapter->dev_id);
+
+       info("Adapter %s has been enabled", adapter->path);
+}
+
+static void reply_pending_requests(struct btd_adapter *adapter)
+{
+       GSList *l;
+
+       if (!adapter)
+               return;
+
+       /* pending bonding */
+       for (l = adapter->devices; l; l = l->next) {
+               struct btd_device *device = l->data;
+
+               if (device_is_bonding(device, NULL))
+                       device_cancel_bonding(device,
+                                               HCI_OE_USER_ENDED_CONNECTION);
+       }
+}
+
+static void remove_driver(gpointer data, gpointer user_data)
+{
+       struct btd_adapter_driver *driver = data;
+       struct btd_adapter *adapter = user_data;
+
+       if (driver->remove)
+               driver->remove(adapter);
+}
+
+static void unload_drivers(struct btd_adapter *adapter)
+{
+       g_slist_foreach(adapter->loaded_drivers, remove_driver, adapter);
+       g_slist_free(adapter->loaded_drivers);
+       adapter->loaded_drivers = NULL;
+}
+
+static void set_mode_complete(struct btd_adapter *adapter)
+{
+       struct session_req *pending;
+       const char *modestr;
+       int err;
+
+       DBG("");
+
+       if (adapter->mode == MODE_OFF) {
+               g_slist_free_full(adapter->mode_sessions, session_free);
+               adapter->mode_sessions = NULL;
+       }
+
+       if (adapter->pending_mode == NULL)
+               return;
+
+       pending = adapter->pending_mode;
+       adapter->pending_mode = NULL;
+
+       err = (pending->mode != adapter->mode) ? -EINVAL : 0;
+
+       if (pending->msg != NULL) {
+               DBusMessage *msg = pending->msg;
+               DBusMessage *reply;
+
+               if (err < 0)
+                       reply = btd_error_failed(msg, strerror(-err));
+               else {
+                       if (strcmp(dbus_message_get_member(msg),
+                                               "SetProperty") == 0)
+                               adapter->global_mode = adapter->mode;
+                       reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+               }
+
+               g_dbus_send_message(connection, reply);
+       }
+
+       modestr = mode2str(adapter->mode);
+
+       DBG("%s", modestr);
+
+       /* restore if the mode doesn't matches the pending */
+       if (err != 0) {
+               write_device_mode(&adapter->bdaddr, modestr);
+               error("unable to set mode: %s", mode2str(pending->mode));
+       }
+
+       session_unref(pending);
+}
+
+int btd_adapter_stop(struct btd_adapter *adapter)
+{
+       gboolean prop_false = FALSE;
+
+       /* check pending requests */
+       reply_pending_requests(adapter);
+
+       adapter->up = FALSE;
+
+       stop_discovery(adapter);
+
+       if (adapter->disc_sessions) {
+               g_slist_free_full(adapter->disc_sessions, session_free);
+               adapter->disc_sessions = NULL;
+       }
+
+       while (adapter->connections) {
+               struct btd_device *device = adapter->connections->data;
+               adapter_remove_connection(adapter, device);
+       }
+
+       if (adapter->scan_mode == (SCAN_PAGE | SCAN_INQUIRY))
+               emit_property_changed(connection, adapter->path,
+                                       ADAPTER_INTERFACE, "Discoverable",
+                                       DBUS_TYPE_BOOLEAN, &prop_false);
+
+       if ((adapter->scan_mode & SCAN_PAGE) && adapter->pairable == TRUE)
+               emit_property_changed(connection, adapter->path,
+                                       ADAPTER_INTERFACE, "Pairable",
+                                       DBUS_TYPE_BOOLEAN, &prop_false);
+
+       if (adapter->discovering)
+               emit_property_changed(connection, adapter->path,
+                                       ADAPTER_INTERFACE, "Discovering",
+                                       DBUS_TYPE_BOOLEAN, &prop_false);
+
+       emit_property_changed(connection, adapter->path, ADAPTER_INTERFACE,
+                               "Powered", DBUS_TYPE_BOOLEAN, &prop_false);
+
+       adapter->discovering = FALSE;
+       adapter->scan_mode = SCAN_DISABLED;
+       adapter->mode = MODE_OFF;
+       adapter->off_requested = FALSE;
+
+       call_adapter_powered_callbacks(adapter, FALSE);
+
+       info("Adapter %s has been disabled", adapter->path);
+
+       set_mode_complete(adapter);
+
+       return 0;
+}
+
+static void off_timer_remove(struct btd_adapter *adapter)
+{
+       g_source_remove(adapter->off_timer);
+       adapter->off_timer = 0;
+}
+
+static void adapter_free(gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       agent_free(adapter->agent);
+       adapter->agent = NULL;
+
+       DBG("%p", adapter);
+
+       if (adapter->auth_idle_id)
+               g_source_remove(adapter->auth_idle_id);
+
+       if (adapter->off_timer)
+               off_timer_remove(adapter);
+
+       sdp_list_free(adapter->services, NULL);
+
+       g_slist_free_full(adapter->found_devices, dev_info_free);
+
+       g_slist_free(adapter->oor_devices);
+
+       g_free(adapter->path);
+       g_free(adapter->name);
+       g_free(adapter);
+}
+
+struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter)
+{
+       adapter->ref++;
+
+       DBG("%p: ref=%d", adapter, adapter->ref);
+
+       return adapter;
+}
+
+void btd_adapter_unref(struct btd_adapter *adapter)
+{
+       gchar *path;
+
+       adapter->ref--;
+
+       DBG("%p: ref=%d", adapter, adapter->ref);
+
+       if (adapter->ref > 0)
+               return;
+
+       path = g_strdup(adapter->path);
+
+       g_dbus_unregister_interface(connection, path, ADAPTER_INTERFACE);
+
+       g_free(path);
+}
+
+gboolean adapter_init(struct btd_adapter *adapter, gboolean up)
+{
+       adapter->up = up;
+
+       adapter->allow_name_changes = TRUE;
+
+       adapter_ops->read_bdaddr(adapter->dev_id, &adapter->bdaddr);
+
+       if (bacmp(&adapter->bdaddr, BDADDR_ANY) == 0) {
+               error("No address available for hci%d", adapter->dev_id);
+               return FALSE;
+       }
+
+       sdp_init_services_list(&adapter->bdaddr);
+
+       if (main_opts.gatt_enabled)
+               btd_adapter_gatt_server_start(adapter);
+
+       load_drivers(adapter);
+       clear_blocked(adapter);
+       load_devices(adapter);
+
+       /* Set pairable mode */
+       if (read_device_pairable(&adapter->bdaddr, &adapter->pairable) < 0)
+               adapter->pairable = TRUE;
+
+       /* retrieve the active connections: address the scenario where
+        * the are active connections before the daemon've started */
+       load_connections(adapter);
+
+       adapter->initialized = TRUE;
+
+       return TRUE;
+}
+
+struct btd_adapter *adapter_create(DBusConnection *conn, int id)
+{
+       char path[MAX_PATH_LENGTH];
+       struct btd_adapter *adapter;
+       const char *base_path = manager_get_base_path();
+
+       if (!connection)
+               connection = conn;
+
+       adapter = g_try_new0(struct btd_adapter, 1);
+       if (!adapter) {
+               error("adapter_create: failed to alloc memory for hci%d", id);
+               return NULL;
+       }
+
+       adapter->dev_id = id;
+
+       snprintf(path, sizeof(path), "%s/hci%d", base_path, id);
+       adapter->path = g_strdup(path);
+
+       if (!g_dbus_register_interface(conn, path, ADAPTER_INTERFACE,
+                                       adapter_methods, adapter_signals, NULL,
+                                       adapter, adapter_free)) {
+               error("Adapter interface init failed on path %s", path);
+               adapter_free(adapter);
+               return NULL;
+       }
+
+       return btd_adapter_ref(adapter);
+}
+
+void adapter_remove(struct btd_adapter *adapter)
+{
+       GSList *l;
+
+       DBG("Removing adapter %s", adapter->path);
+
+       for (l = adapter->devices; l; l = l->next)
+               device_remove(l->data, FALSE);
+       g_slist_free(adapter->devices);
+
+       unload_drivers(adapter);
+       if (main_opts.gatt_enabled)
+               btd_adapter_gatt_server_stop(adapter);
+
+       g_slist_free(adapter->pin_callbacks);
+
+       /* Return adapter to down state if it was not up on init */
+       adapter_ops->restore_powered(adapter->dev_id);
+}
+
+uint16_t adapter_get_dev_id(struct btd_adapter *adapter)
+{
+       return adapter->dev_id;
+}
+
+const gchar *adapter_get_path(struct btd_adapter *adapter)
+{
+       if (!adapter)
+               return NULL;
+
+       return adapter->path;
+}
+
+void adapter_get_address(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+{
+       bacpy(bdaddr, &adapter->bdaddr);
+}
+
+void adapter_set_allow_name_changes(struct btd_adapter *adapter,
+                                               gboolean allow_name_changes)
+{
+       adapter->allow_name_changes = allow_name_changes;
+}
+
+void adapter_set_discovering(struct btd_adapter *adapter,
+                                               gboolean discovering)
+{
+       const char *path = adapter->path;
+
+       adapter->discovering = discovering;
+
+       emit_property_changed(connection, path,
+                               ADAPTER_INTERFACE, "Discovering",
+                               DBUS_TYPE_BOOLEAN, &discovering);
+
+       if (discovering)
+               return;
+
+       g_slist_foreach(adapter->oor_devices, emit_device_disappeared, adapter);
+       g_slist_free_full(adapter->oor_devices, dev_info_free);
+       adapter->oor_devices = g_slist_copy(adapter->found_devices);
+
+       if (!adapter_has_discov_sessions(adapter) || adapter->discov_suspended)
+               return;
+
+       DBG("hci%u restarting discovery, disc_sessions %u", adapter->dev_id,
+                                       g_slist_length(adapter->disc_sessions));
+
+       adapter->discov_id = g_idle_add(discovery_cb, adapter);
+}
+
+static void suspend_discovery(struct btd_adapter *adapter)
+{
+       if (adapter->disc_sessions == NULL || adapter->discov_suspended)
+               return;
+
+       DBG("Suspending discovery");
+
+       if (adapter->oor_devices) {
+               g_slist_free(adapter->oor_devices);
+               adapter->oor_devices = NULL;
+       }
+
+       adapter->discov_suspended = TRUE;
+
+       if (adapter->discov_id > 0) {
+               g_source_remove(adapter->discov_id);
+               adapter->discov_id = 0;
+       } else
+               adapter_ops->stop_discovery(adapter->dev_id);
+}
+
+static int found_device_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct remote_dev_info *d = a;
+       const bdaddr_t *bdaddr = b;
+
+       if (bacmp(bdaddr, BDADDR_ANY) == 0)
+               return 0;
+
+       return bacmp(&d->bdaddr, bdaddr);
+}
+
+struct remote_dev_info *adapter_search_found_devices(struct btd_adapter *adapter,
+                                                       bdaddr_t *bdaddr)
+{
+       GSList *l;
+
+       l = g_slist_find_custom(adapter->found_devices, bdaddr,
+                                                       found_device_cmp);
+       if (l)
+               return l->data;
+
+       return NULL;
+}
+
+static int dev_rssi_cmp(struct remote_dev_info *d1, struct remote_dev_info *d2)
+{
+       int rssi1, rssi2;
+
+       rssi1 = d1->rssi < 0 ? -d1->rssi : d1->rssi;
+       rssi2 = d2->rssi < 0 ? -d2->rssi : d2->rssi;
+
+       return rssi1 - rssi2;
+}
+
+static void append_dict_valist(DBusMessageIter *iter,
+                                       const char *first_key,
+                                       va_list var_args)
+{
+       DBusMessageIter dict;
+       const char *key;
+       int type;
+       int n_elements;
+       void *val;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       key = first_key;
+       while (key) {
+               type = va_arg(var_args, int);
+               val = va_arg(var_args, void *);
+               if (type == DBUS_TYPE_ARRAY) {
+                       n_elements = va_arg(var_args, int);
+                       if (n_elements > 0)
+                               dict_append_array(&dict, key, DBUS_TYPE_STRING,
+                                               val, n_elements);
+               } else
+                       dict_append_entry(&dict, key, type, val);
+               key = va_arg(var_args, char *);
+       }
+
+       dbus_message_iter_close_container(iter, &dict);
+}
+
+static void emit_device_found(const char *path, const char *address,
+                               const char *first_key, ...)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       va_list var_args;
+
+       signal = dbus_message_new_signal(path, ADAPTER_INTERFACE,
+                                       "DeviceFound");
+       if (!signal) {
+               error("Unable to allocate new %s.DeviceFound signal",
+                               ADAPTER_INTERFACE);
+               return;
+       }
+       dbus_message_iter_init_append(signal, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &address);
+
+       va_start(var_args, first_key);
+       append_dict_valist(&iter, first_key, var_args);
+       va_end(var_args);
+
+       g_dbus_send_message(connection, signal);
+}
+
+static char **strlist2array(GSList *list)
+{
+       unsigned int i, n;
+       char **array;
+
+       if (list == NULL)
+               return NULL;
+
+       n = g_slist_length(list);
+       array = g_new0(char *, n + 1);
+
+       for (i = 0; list; list = list->next, i++)
+               array[i] = g_strdup((const gchar *) list->data);
+
+       return array;
+}
+
+void adapter_emit_device_found(struct btd_adapter *adapter,
+                                               struct remote_dev_info *dev)
+{
+       struct btd_device *device;
+       char peer_addr[18], local_addr[18];
+       const char *icon, *paddr = peer_addr;
+       dbus_bool_t paired = FALSE, trusted = FALSE;
+       dbus_int16_t rssi = dev->rssi;
+       char *alias;
+       size_t uuid_count;
+
+       ba2str(&dev->bdaddr, peer_addr);
+       ba2str(&adapter->bdaddr, local_addr);
+
+       device = adapter_find_device(adapter, paddr);
+       if (device) {
+               paired = device_is_paired(device);
+               trusted = device_is_trusted(device);
+       }
+
+       /* The uuids string array is updated only if necessary */
+       uuid_count = g_slist_length(dev->services);
+       if (dev->services && dev->uuid_count != uuid_count) {
+               g_strfreev(dev->uuids);
+               dev->uuids = strlist2array(dev->services);
+               dev->uuid_count = uuid_count;
+       }
+
+       if (!dev->alias) {
+               if (!dev->name) {
+                       alias = g_strdup(peer_addr);
+                       g_strdelimit(alias, ":", '-');
+               } else
+                       alias = g_strdup(dev->name);
+       } else
+               alias = g_strdup(dev->alias);
+
+       if (dev->bdaddr_type != BDADDR_BREDR) {
+               gboolean broadcaster;
+               uint16_t app;
+
+               if (dev->flags & (EIR_LIM_DISC | EIR_GEN_DISC))
+                       broadcaster = FALSE;
+               else
+                       broadcaster = TRUE;
+
+               dev->legacy = FALSE;
+
+               if (read_remote_appearance(&adapter->bdaddr, &dev->bdaddr,
+                                               dev->bdaddr_type, &app) == 0)
+                       icon = gap_appearance_to_icon(app);
+               else
+                       icon = NULL;
+
+               emit_device_found(adapter->path, paddr,
+                               "Address", DBUS_TYPE_STRING, &paddr,
+                               "Class", DBUS_TYPE_UINT32, &dev->class,
+                               "Icon", DBUS_TYPE_STRING, &icon,
+                               "RSSI", DBUS_TYPE_INT16, &rssi,
+                               "Name", DBUS_TYPE_STRING, &dev->name,
+                               "Alias", DBUS_TYPE_STRING, &alias,
+                               "LegacyPairing", DBUS_TYPE_BOOLEAN, &dev->legacy,
+                               "Paired", DBUS_TYPE_BOOLEAN, &paired,
+                               "Broadcaster", DBUS_TYPE_BOOLEAN, &broadcaster,
+                               "UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
+                               NULL);
+       } else {
+               icon = class_to_icon(dev->class);
+
+               emit_device_found(adapter->path, paddr,
+                               "Address", DBUS_TYPE_STRING, &paddr,
+                               "Class", DBUS_TYPE_UINT32, &dev->class,
+                               "Icon", DBUS_TYPE_STRING, &icon,
+                               "RSSI", DBUS_TYPE_INT16, &rssi,
+                               "Name", DBUS_TYPE_STRING, &dev->name,
+                               "Alias", DBUS_TYPE_STRING, &alias,
+                               "LegacyPairing", DBUS_TYPE_BOOLEAN, &dev->legacy,
+                               "Paired", DBUS_TYPE_BOOLEAN, &paired,
+                               "Trusted", DBUS_TYPE_BOOLEAN, &trusted,
+                               "UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
+                               NULL);
+       }
+
+       g_free(alias);
+}
+
+static struct remote_dev_info *found_device_new(const bdaddr_t *bdaddr,
+                                       uint8_t bdaddr_type, const char *name,
+                                       const char *alias, uint32_t class,
+                                       gboolean legacy, int flags)
+{
+       struct remote_dev_info *dev;
+
+       dev = g_new0(struct remote_dev_info, 1);
+       bacpy(&dev->bdaddr, bdaddr);
+       dev->bdaddr_type = bdaddr_type;
+       dev->name = g_strdup(name);
+       dev->alias = g_strdup(alias);
+       dev->class = class;
+       dev->legacy = legacy;
+       if (flags >= 0)
+               dev->flags = flags;
+
+       return dev;
+}
+
+static void remove_same_uuid(gpointer data, gpointer user_data)
+{
+       struct remote_dev_info *dev = user_data;
+       GSList *l;
+
+       for (l = dev->services; l; l = l->next) {
+               char *current_uuid = l->data;
+               char *new_uuid = data;
+
+               if (strcmp(current_uuid, new_uuid) == 0) {
+                       g_free(current_uuid);
+                       dev->services = g_slist_delete_link(dev->services, l);
+                       break;
+               }
+       }
+}
+
+static void dev_prepend_uuid(gpointer data, gpointer user_data)
+{
+       struct remote_dev_info *dev = user_data;
+       char *new_uuid = data;
+
+       dev->services = g_slist_prepend(dev->services, g_strdup(new_uuid));
+}
+
+static gboolean pairing_is_legacy(bdaddr_t *local, bdaddr_t *peer,
+                                       const uint8_t *eir, const char *name)
+{
+       unsigned char features[8];
+
+       if (eir)
+               return FALSE;
+
+       if (name == NULL)
+               return TRUE;
+
+       if (read_remote_features(local, peer, NULL, features) < 0)
+               return TRUE;
+
+       if (features[0] & 0x01)
+               return FALSE;
+       else
+               return TRUE;
+}
+
+static char *read_stored_data(bdaddr_t *local, bdaddr_t *peer, const char *file)
+{
+       char local_addr[18], peer_addr[18], filename[PATH_MAX + 1];
+
+       ba2str(local, local_addr);
+       ba2str(peer, peer_addr);
+
+       create_name(filename, PATH_MAX, STORAGEDIR, local_addr, file);
+
+       return textfile_get(filename, peer_addr);
+}
+
+void adapter_update_found_devices(struct btd_adapter *adapter,
+                                       bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                                       int8_t rssi, uint8_t confirm_name,
+                                       uint8_t *data, uint8_t data_len)
+{
+       struct remote_dev_info *dev;
+       struct eir_data eir_data;
+       char *alias, *name;
+       gboolean legacy, name_known;
+       uint32_t dev_class;
+       int err;
+
+       memset(&eir_data, 0, sizeof(eir_data));
+       err = eir_parse(&eir_data, data, data_len);
+       if (err < 0) {
+               error("Error parsing EIR data: %s (%d)", strerror(-err), -err);
+               return;
+       }
+
+       dev_class = eir_data.dev_class[0] | (eir_data.dev_class[1] << 8) |
+                                               (eir_data.dev_class[2] << 16);
+       if (dev_class != 0)
+               write_remote_class(&adapter->bdaddr, bdaddr, dev_class);
+
+       if (eir_data.appearance != 0)
+               write_remote_appearance(&adapter->bdaddr, bdaddr, bdaddr_type,
+                                                       eir_data.appearance);
+
+       if (eir_data.name != NULL && eir_data.name_complete)
+               write_device_name(&adapter->bdaddr, bdaddr, eir_data.name);
+
+       dev = adapter_search_found_devices(adapter, bdaddr);
+       if (dev) {
+               adapter->oor_devices = g_slist_remove(adapter->oor_devices,
+                                                       dev);
+
+               /* If an existing device had no name but the newly received EIR
+                * data has (complete or not), we want to present it to the
+                * user. */
+               if (dev->name == NULL && eir_data.name != NULL) {
+                       dev->name = g_strdup(eir_data.name);
+                       goto done;
+               }
+
+               if (dev->rssi != rssi)
+                       goto done;
+
+               eir_data_free(&eir_data);
+
+               return;
+       }
+
+       /* New device in the discovery session */
+
+       name = read_stored_data(&adapter->bdaddr, bdaddr, "names");
+
+       if (bdaddr_type == BDADDR_BREDR) {
+               legacy = pairing_is_legacy(&adapter->bdaddr, bdaddr, data,
+                                                                       name);
+
+               if (!name && main_opts.name_resolv &&
+                               adapter_has_discov_sessions(adapter))
+                       name_known = FALSE;
+               else
+                       name_known = TRUE;
+       } else {
+               legacy = FALSE;
+               name_known = TRUE;
+       }
+
+       if (confirm_name)
+               adapter_ops->confirm_name(adapter->dev_id, bdaddr, bdaddr_type,
+                                                               name_known);
+
+       alias = read_stored_data(&adapter->bdaddr, bdaddr, "aliases");
+
+       dev = found_device_new(bdaddr, bdaddr_type, name, alias, dev_class,
+                                               legacy, eir_data.flags);
+       free(name);
+       free(alias);
+
+       adapter->found_devices = g_slist_prepend(adapter->found_devices, dev);
+
+done:
+       dev->rssi = rssi;
+
+       adapter->found_devices = g_slist_sort(adapter->found_devices,
+                                               (GCompareFunc) dev_rssi_cmp);
+
+       g_slist_foreach(eir_data.services, remove_same_uuid, dev);
+       g_slist_foreach(eir_data.services, dev_prepend_uuid, dev);
+
+       adapter_emit_device_found(adapter, dev);
+
+       eir_data_free(&eir_data);
+}
+
+void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode)
+{
+       const gchar *path = adapter_get_path(adapter);
+       gboolean discoverable, pairable;
+
+       DBG("old 0x%02x new 0x%02x", adapter->scan_mode, scan_mode);
+
+       if (adapter->scan_mode == scan_mode)
+               return;
+
+       switch (scan_mode) {
+       case SCAN_DISABLED:
+               adapter->mode = MODE_OFF;
+               discoverable = FALSE;
+               pairable = FALSE;
+               break;
+       case SCAN_PAGE:
+               adapter->mode = MODE_CONNECTABLE;
+               discoverable = FALSE;
+               pairable = adapter->pairable;
+               break;
+       case (SCAN_PAGE | SCAN_INQUIRY):
+               adapter->mode = MODE_DISCOVERABLE;
+               discoverable = TRUE;
+               pairable = adapter->pairable;
+               break;
+       default:
+               /* ignore, reserved */
+               return;
+       }
+
+       /* If page scanning gets toggled emit the Pairable property */
+       if ((adapter->scan_mode & SCAN_PAGE) != (scan_mode & SCAN_PAGE))
+               emit_property_changed(connection, adapter->path,
+                                       ADAPTER_INTERFACE, "Pairable",
+                                       DBUS_TYPE_BOOLEAN, &pairable);
+
+       emit_property_changed(connection, path,
+                               ADAPTER_INTERFACE, "Discoverable",
+                               DBUS_TYPE_BOOLEAN, &discoverable);
+
+       adapter->scan_mode = scan_mode;
+
+       set_mode_complete(adapter);
+}
+
+struct agent *adapter_get_agent(struct btd_adapter *adapter)
+{
+       if (!adapter)
+               return NULL;
+
+       return adapter->agent;
+}
+
+void adapter_add_connection(struct btd_adapter *adapter,
+                                               struct btd_device *device)
+{
+       if (g_slist_find(adapter->connections, device)) {
+               error("Device is already marked as connected");
+               return;
+       }
+
+       device_add_connection(device, connection);
+
+       adapter->connections = g_slist_append(adapter->connections, device);
+}
+
+void adapter_remove_connection(struct btd_adapter *adapter,
+                                               struct btd_device *device)
+{
+       DBG("");
+
+       if (!g_slist_find(adapter->connections, device)) {
+               error("No matching connection for device");
+               return;
+       }
+
+       device_remove_connection(device, connection);
+
+       adapter->connections = g_slist_remove(adapter->connections, device);
+
+       if (device_is_authenticating(device))
+               device_cancel_authentication(device, TRUE);
+
+       if (device_is_temporary(device)) {
+               const char *path = device_get_path(device);
+
+               DBG("Removing temporary device %s", path);
+               adapter_remove_device(connection, adapter, device, TRUE);
+       }
+}
+
+gboolean adapter_has_discov_sessions(struct btd_adapter *adapter)
+{
+       if (!adapter || !adapter->disc_sessions)
+               return FALSE;
+
+       return TRUE;
+}
+
+int btd_register_adapter_driver(struct btd_adapter_driver *driver)
+{
+       adapter_drivers = g_slist_append(adapter_drivers, driver);
+
+       if (driver->probe == NULL)
+               return 0;
+
+       manager_foreach_adapter(probe_driver, driver);
+
+       return 0;
+}
+
+static void unload_driver(struct btd_adapter *adapter, gpointer data)
+{
+       adapter->loaded_drivers = g_slist_remove(adapter->loaded_drivers, data);
+}
+
+void btd_unregister_adapter_driver(struct btd_adapter_driver *driver)
+{
+       adapter_drivers = g_slist_remove(adapter_drivers, driver);
+
+       manager_foreach_adapter(unload_driver, driver);
+}
+
+static void agent_auth_cb(struct agent *agent, DBusError *derr,
+                                                       void *user_data)
+{
+       struct service_auth *auth = user_data;
+
+       device_set_authorizing(auth->device, FALSE);
+
+       auth->cb(derr, auth->user_data);
+}
+
+static gboolean auth_idle_cb(gpointer user_data)
+{
+       struct service_auth *auth = user_data;
+       struct btd_adapter *adapter = auth->adapter;
+
+       adapter->auth_idle_id = 0;
+
+       auth->cb(NULL, auth->user_data);
+
+       return FALSE;
+}
+
+static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
+                                       const char *uuid, service_auth_cb cb,
+                                       void *user_data)
+{
+       struct service_auth *auth;
+       struct btd_device *device;
+       struct agent *agent;
+       char address[18];
+       const gchar *dev_path;
+       int err;
+
+       ba2str(dst, address);
+       device = adapter_find_device(adapter, address);
+       if (!device)
+               return -EPERM;
+
+       /* Device connected? */
+       if (!g_slist_find(adapter->connections, device))
+               error("Authorization request for non-connected device!?");
+
+       if (adapter->auth_idle_id)
+               return -EBUSY;
+
+       auth = g_try_new0(struct service_auth, 1);
+       if (!auth)
+               return -ENOMEM;
+
+       auth->cb = cb;
+       auth->user_data = user_data;
+       auth->device = device;
+       auth->adapter = adapter;
+
+       if (device_is_trusted(device) == TRUE) {
+               adapter->auth_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
+                                                       auth_idle_cb, auth,
+                                                       g_free);
+               return 0;
+       }
+
+       agent = device_get_agent(device);
+       if (!agent) {
+               warn("Can't find device agent");
+               g_free(auth);
+               return -EPERM;
+       }
+
+       dev_path = device_get_path(device);
+
+       err = agent_authorize(agent, dev_path, uuid, agent_auth_cb, auth, g_free);
+       if (err < 0)
+               g_free(auth);
+       else
+               device_set_authorizing(device, TRUE);
+
+       return err;
+}
+
+int btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
+                                       const char *uuid, service_auth_cb cb,
+                                       void *user_data)
+{
+       struct btd_adapter *adapter;
+       GSList *l;
+
+       if (bacmp(src, BDADDR_ANY) != 0) {
+               adapter = manager_find_adapter(src);
+               if (!adapter)
+                       return -EPERM;
+
+               return adapter_authorize(adapter, dst, uuid, cb, user_data);
+       }
+
+       for (l = manager_get_adapters(); l != NULL; l = g_slist_next(l)) {
+               int err;
+
+               adapter = l->data;
+
+               err = adapter_authorize(adapter, dst, uuid, cb, user_data);
+               if (err == 0)
+                       return 0;
+       }
+
+       return -EPERM;
+}
+
+int btd_cancel_authorization(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       struct btd_adapter *adapter = manager_find_adapter(src);
+       struct btd_device *device;
+       struct agent *agent;
+       char address[18];
+       int err;
+
+       if (!adapter)
+               return -EPERM;
+
+       ba2str(dst, address);
+       device = adapter_find_device(adapter, address);
+       if (!device)
+               return -EPERM;
+
+       if (adapter->auth_idle_id) {
+               g_source_remove(adapter->auth_idle_id);
+               adapter->auth_idle_id = 0;
+               return 0;
+       }
+
+       /*
+        * FIXME: Cancel fails if authorization is requested to adapter's
+        * agent and in the meanwhile CreatePairedDevice is called.
+        */
+
+       agent = device_get_agent(device);
+       if (!agent)
+               return -EPERM;
+
+       err = agent_cancel(agent);
+
+       if (err == 0)
+               device_set_authorizing(device, FALSE);
+
+       return err;
+}
+
+static gchar *adapter_any_path = NULL;
+static int adapter_any_refcount = 0;
+
+const char *adapter_any_get_path(void)
+{
+       return adapter_any_path;
+}
+
+const char *btd_adapter_any_request_path(void)
+{
+       if (adapter_any_refcount++ > 0)
+               return adapter_any_path;
+
+       adapter_any_path = g_strdup_printf("%s/any", manager_get_base_path());
+
+       return adapter_any_path;
+}
+
+void btd_adapter_any_release_path(void)
+{
+       adapter_any_refcount--;
+
+       if (adapter_any_refcount > 0)
+               return;
+
+       g_free(adapter_any_path);
+       adapter_any_path = NULL;
+}
+
+gboolean adapter_powering_down(struct btd_adapter *adapter)
+{
+       return adapter->off_requested;
+}
+
+int btd_adapter_restore_powered(struct btd_adapter *adapter)
+{
+       char mode[14], address[18];
+
+       if (!adapter_ops)
+               return -EINVAL;
+
+       if (!main_opts.remember_powered)
+               return -EINVAL;
+
+       if (adapter->up)
+               return 0;
+
+       ba2str(&adapter->bdaddr, address);
+       if (read_device_mode(address, mode, sizeof(mode)) == 0 &&
+                                               g_str_equal(mode, "off"))
+               return 0;
+
+       return adapter_ops->set_powered(adapter->dev_id, TRUE);
+}
+
+static gboolean switch_off_timeout(gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       adapter_ops->set_powered(adapter->dev_id, FALSE);
+       adapter->off_timer = 0;
+
+       return FALSE;
+}
+
+int btd_adapter_switch_online(struct btd_adapter *adapter)
+{
+       if (!adapter_ops)
+               return -EINVAL;
+
+       if (adapter->up)
+               return -EALREADY;
+
+       if (adapter->off_timer)
+               off_timer_remove(adapter);
+
+       return adapter_ops->set_powered(adapter->dev_id, TRUE);
+}
+
+int btd_adapter_switch_offline(struct btd_adapter *adapter)
+{
+       if (!adapter_ops)
+               return -EINVAL;
+
+       if (!adapter->up)
+               return -EALREADY;
+
+       if (adapter->off_timer)
+               return 0;
+
+       adapter->global_mode = MODE_OFF;
+
+       if (adapter->connections == NULL)
+               return adapter_ops->set_powered(adapter->dev_id, FALSE);
+
+       g_slist_foreach(adapter->connections,
+                               (GFunc) device_request_disconnect, NULL);
+
+       adapter->off_timer = g_timeout_add_seconds(OFF_TIMER,
+                                               switch_off_timeout, adapter);
+
+       return 0;
+}
+
+static gboolean disable_auto(gpointer user_data)
+{
+       struct btd_adapter *adapter = user_data;
+       GSList *l;
+
+       for (l = adapter->devices; l; l = l->next) {
+               struct btd_device *device = l->data;
+
+               device_set_auto_connect(device, FALSE);
+       }
+
+       adapter->auto_timeout_id = 0;
+
+       return FALSE;
+}
+
+static void set_auto_connect(gpointer data, gpointer user_data)
+{
+       struct btd_device *device = data;
+
+       device_set_auto_connect(device, TRUE);
+}
+
+void btd_adapter_enable_auto_connect(struct btd_adapter *adapter)
+{
+       if (!adapter->up)
+               return;
+
+       DBG("Enabling automatic connections");
+
+       if (adapter->auto_timeout_id)
+               return;
+
+       g_slist_foreach(adapter->devices, set_auto_connect, NULL);
+
+       adapter->auto_timeout_id = g_timeout_add_seconds(main_opts.autoto,
+                                               disable_auto, adapter);
+}
+
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+                                                       btd_adapter_pin_cb_t cb)
+{
+       adapter->pin_callbacks = g_slist_prepend(adapter->pin_callbacks, cb);
+}
+
+void btd_adapter_unregister_pin_cb(struct btd_adapter *adapter,
+                                                       btd_adapter_pin_cb_t cb)
+{
+       adapter->pin_callbacks = g_slist_remove(adapter->pin_callbacks, cb);
+}
+
+ssize_t btd_adapter_get_pin(struct btd_adapter *adapter, struct btd_device *dev,
+                                       char *pin_buf, gboolean *display)
+{
+       GSList *l;
+       btd_adapter_pin_cb_t cb;
+       bdaddr_t sba, dba;
+       ssize_t ret;
+
+       for (l = adapter->pin_callbacks; l != NULL; l = g_slist_next(l)) {
+               cb = l->data;
+               ret = cb(adapter, dev, pin_buf, display);
+               if (ret > 0)
+                       return ret;
+       }
+
+       adapter_get_address(adapter, &sba);
+       device_get_address(dev, &dba, NULL);
+
+       return read_pin_code(&sba, &dba, pin_buf);
+}
+
+int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority)
+{
+       if (ops->setup == NULL)
+               return -EINVAL;
+
+       if (priority)
+               ops_candidates = g_slist_prepend(ops_candidates, ops);
+       else
+               ops_candidates = g_slist_append(ops_candidates, ops);
+
+       return 0;
+}
+
+void btd_adapter_cleanup_ops(struct btd_adapter_ops *ops)
+{
+       ops_candidates = g_slist_remove(ops_candidates, ops);
+       ops->cleanup();
+
+       if (adapter_ops == ops)
+               adapter_ops = NULL;
+}
+
+int adapter_ops_setup(void)
+{
+       GSList *l;
+       int ret;
+
+       if (!ops_candidates)
+               return -EINVAL;
+
+       for (l = ops_candidates; l != NULL; l = g_slist_next(l)) {
+               struct btd_adapter_ops *ops = l->data;
+
+               ret = ops->setup();
+               if (ret < 0)
+                       continue;
+
+               adapter_ops = ops;
+               break;
+       }
+
+       return ret;
+}
+
+void btd_adapter_register_powered_callback(struct btd_adapter *adapter,
+                                               btd_adapter_powered_cb cb)
+{
+       adapter->powered_callbacks =
+                       g_slist_append(adapter->powered_callbacks, cb);
+}
+
+void btd_adapter_unregister_powered_callback(struct btd_adapter *adapter,
+                                               btd_adapter_powered_cb cb)
+{
+       adapter->powered_callbacks =
+                       g_slist_remove(adapter->powered_callbacks, cb);
+}
+
+int btd_adapter_set_fast_connectable(struct btd_adapter *adapter,
+                                                       gboolean enable)
+{
+       if (!adapter_ops)
+               return -EINVAL;
+
+       if (!adapter->up)
+               return -EINVAL;
+
+       return adapter_ops->set_fast_connectable(adapter->dev_id, enable);
+}
+
+int btd_adapter_read_clock(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                               int which, int timeout, uint32_t *clock,
+                               uint16_t *accuracy)
+{
+       if (!adapter_ops)
+               return -EINVAL;
+
+       if (!adapter->up)
+               return -EINVAL;
+
+       return adapter_ops->read_clock(adapter->dev_id, bdaddr, which,
+                                               timeout, clock, accuracy);
+}
+
+int btd_adapter_disconnect_device(struct btd_adapter *adapter,
+                                       bdaddr_t *bdaddr, uint8_t bdaddr_type)
+
+{
+       return adapter_ops->disconnect(adapter->dev_id, bdaddr, bdaddr_type);
+}
+
+int btd_adapter_remove_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type)
+{
+       return adapter_ops->remove_bonding(adapter->dev_id, bdaddr,
+                                                               bdaddr_type);
+}
+
+int btd_adapter_pincode_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                       const char *pin, size_t pin_len)
+{
+       return adapter_ops->pincode_reply(adapter->dev_id, bdaddr, pin,
+                                                               pin_len);
+}
+
+int btd_adapter_confirm_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                       uint8_t bdaddr_type, gboolean success)
+{
+       return adapter_ops->confirm_reply(adapter->dev_id, bdaddr, bdaddr_type,
+                                                               success);
+}
+
+int btd_adapter_passkey_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                       uint8_t bdaddr_type, uint32_t passkey)
+{
+       return adapter_ops->passkey_reply(adapter->dev_id, bdaddr, bdaddr_type,
+                                                               passkey);
+}
+
+int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                       bt_hci_result_t cb, gpointer user_data)
+{
+       return adapter_ops->encrypt_link(adapter->dev_id, bdaddr, cb, user_data);
+}
+
+int btd_adapter_set_did(struct btd_adapter *adapter, uint16_t vendor,
+                                       uint16_t product, uint16_t version,
+                                       uint16_t source)
+{
+       return adapter_ops->set_did(adapter->dev_id, vendor, product, version,
+                                                               source);
+}
+
+int adapter_create_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                       uint8_t addr_type, uint8_t io_cap)
+{
+       suspend_discovery(adapter);
+       return adapter_ops->create_bonding(adapter->dev_id, bdaddr,
+                                               addr_type, io_cap);
+}
+
+int adapter_cancel_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+{
+       return adapter_ops->cancel_bonding(adapter->dev_id, bdaddr);
+}
+
+void adapter_bonding_complete(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                                               uint8_t status)
+{
+       struct btd_device *device;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       if (status == 0)
+               device = adapter_get_device(connection, adapter, addr);
+       else
+               device = adapter_find_device(adapter, addr);
+
+       if (device != NULL)
+               device_bonding_complete(device, status);
+
+       if (adapter->discov_suspended) {
+               adapter->discov_suspended = FALSE;
+               adapter_ops->start_discovery(adapter->dev_id);
+       }
+}
+
+int btd_adapter_read_local_oob_data(struct btd_adapter *adapter)
+{
+       return adapter_ops->read_local_oob_data(adapter->dev_id);
+}
+
+int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
+                       bdaddr_t *bdaddr, uint8_t *hash, uint8_t *randomizer)
+{
+       return adapter_ops->add_remote_oob_data(adapter->dev_id, bdaddr, hash,
+                                                               randomizer);
+}
+
+int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
+                                                       bdaddr_t *bdaddr)
+{
+       return adapter_ops->remove_remote_oob_data(adapter->dev_id, bdaddr);
+}
diff --git a/src/adapter.h b/src/adapter.h
new file mode 100644 (file)
index 0000000..b7ea62b
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#define ADAPTER_INTERFACE      "org.bluez.Adapter"
+
+#define MODE_OFF               0x00
+#define MODE_CONNECTABLE       0x01
+#define MODE_DISCOVERABLE      0x02
+#define MODE_UNKNOWN           0xff
+
+#define MAX_NAME_LENGTH                248
+
+/* Invalid SSP passkey value used to indicate negative replies */
+#define INVALID_PASSKEY                0xffffffff
+
+struct btd_adapter;
+
+struct link_key_info {
+       bdaddr_t bdaddr;
+       unsigned char key[16];
+       uint8_t type;
+       uint8_t pin_len;
+};
+
+struct smp_ltk_info {
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       uint8_t authenticated;
+       uint8_t master;
+       uint8_t enc_size;
+       uint16_t ediv;
+       uint8_t rand[8];
+       uint8_t val[16];
+};
+
+struct remote_dev_info {
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       int8_t rssi;
+       uint32_t class;
+       char *name;
+       char *alias;
+       dbus_bool_t legacy;
+       char **uuids;
+       size_t uuid_count;
+       GSList *services;
+       uint8_t flags;
+};
+
+void btd_adapter_start(struct btd_adapter *adapter);
+
+int btd_adapter_stop(struct btd_adapter *adapter);
+
+void btd_adapter_get_mode(struct btd_adapter *adapter, uint8_t *mode,
+                                               uint8_t *on_mode,
+                                               uint16_t *discoverable_timeout,
+                                               gboolean *pairable);
+
+void btd_adapter_get_class(struct btd_adapter *adapter, uint8_t *major,
+                                                       uint8_t *minor);
+const char *btd_adapter_get_name(struct btd_adapter *adapter);
+struct btd_device *adapter_get_device(DBusConnection *conn,
+                               struct btd_adapter *adapter, const char *address);
+
+struct btd_device *adapter_find_device(struct btd_adapter *adapter, const char *dest);
+
+void adapter_remove_device(DBusConnection *conn, struct btd_adapter *adapter,
+                                               struct btd_device *device,
+                                               gboolean remove_storage);
+
+struct btd_adapter *adapter_create(DBusConnection *conn, int id);
+gboolean adapter_init(struct btd_adapter *adapter, gboolean up);
+void adapter_remove(struct btd_adapter *adapter);
+void adapter_set_allow_name_changes(struct btd_adapter *adapter,
+                                               gboolean allow_name_changes);
+void adapter_set_discovering(struct btd_adapter *adapter,
+                                               gboolean discovering);
+uint16_t adapter_get_dev_id(struct btd_adapter *adapter);
+const gchar *adapter_get_path(struct btd_adapter *adapter);
+void adapter_get_address(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+struct remote_dev_info *adapter_search_found_devices(struct btd_adapter *adapter,
+                                                       bdaddr_t *bdaddr);
+void adapter_update_found_devices(struct btd_adapter *adapter,
+                                       bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                                       int8_t rssi, uint8_t confirm_name,
+                                       uint8_t *data, uint8_t data_len);
+void adapter_emit_device_found(struct btd_adapter *adapter,
+                                               struct remote_dev_info *dev);
+void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode);
+int adapter_set_name(struct btd_adapter *adapter, const char *name);
+void adapter_name_changed(struct btd_adapter *adapter, const char *name);
+void adapter_service_insert(struct btd_adapter *adapter, void *rec);
+void adapter_service_remove(struct btd_adapter *adapter, void *rec);
+void btd_adapter_class_changed(struct btd_adapter *adapter,
+                                                       uint32_t new_class);
+void btd_adapter_pairable_changed(struct btd_adapter *adapter,
+                                                       gboolean pairable);
+
+struct agent *adapter_get_agent(struct btd_adapter *adapter);
+void adapter_add_connection(struct btd_adapter *adapter,
+                                               struct btd_device *device);
+void adapter_remove_connection(struct btd_adapter *adapter,
+                                               struct btd_device *device);
+gboolean adapter_has_discov_sessions(struct btd_adapter *adapter);
+
+struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter);
+void btd_adapter_unref(struct btd_adapter *adapter);
+
+int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
+                                                       uint8_t minor);
+
+struct btd_adapter_driver {
+       const char *name;
+       int (*probe) (struct btd_adapter *adapter);
+       void (*remove) (struct btd_adapter *adapter);
+};
+
+typedef void (*service_auth_cb) (DBusError *derr, void *user_data);
+
+int btd_register_adapter_driver(struct btd_adapter_driver *driver);
+void btd_unregister_adapter_driver(struct btd_adapter_driver *driver);
+int btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
+               const char *uuid, service_auth_cb cb, void *user_data);
+int btd_cancel_authorization(const bdaddr_t *src, const bdaddr_t *dst);
+
+const char *adapter_any_get_path(void);
+
+const char *btd_adapter_any_request_path(void);
+void btd_adapter_any_release_path(void);
+gboolean adapter_powering_down(struct btd_adapter *adapter);
+
+int btd_adapter_restore_powered(struct btd_adapter *adapter);
+int btd_adapter_switch_online(struct btd_adapter *adapter);
+int btd_adapter_switch_offline(struct btd_adapter *adapter);
+void btd_adapter_enable_auto_connect(struct btd_adapter *adapter);
+
+typedef ssize_t (*btd_adapter_pin_cb_t) (struct btd_adapter *adapter,
+                       struct btd_device *dev, char *out, gboolean *display);
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+                                               btd_adapter_pin_cb_t cb);
+void btd_adapter_unregister_pin_cb(struct btd_adapter *adapter,
+                                               btd_adapter_pin_cb_t cb);
+ssize_t btd_adapter_get_pin(struct btd_adapter *adapter, struct btd_device *dev,
+                                       char *pin_buf, gboolean *display);
+
+typedef void (*bt_hci_result_t) (uint8_t status, gpointer user_data);
+
+struct btd_adapter_ops {
+       int (*setup) (void);
+       void (*cleanup) (void);
+       int (*set_powered) (int index, gboolean powered);
+       int (*set_discoverable) (int index, gboolean discoverable,
+                                                       uint16_t timeout);
+       int (*set_pairable) (int index, gboolean pairable);
+       int (*start_discovery) (int index);
+       int (*stop_discovery) (int index);
+
+       int (*set_name) (int index, const char *name);
+       int (*set_dev_class) (int index, uint8_t major, uint8_t minor);
+       int (*set_fast_connectable) (int index, gboolean enable);
+       int (*read_clock) (int index, bdaddr_t *bdaddr, int which, int timeout,
+                                       uint32_t *clock, uint16_t *accuracy);
+       int (*read_bdaddr) (int index, bdaddr_t *bdaddr);
+       int (*block_device) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type);
+       int (*unblock_device) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type);
+       int (*get_conn_list) (int index, GSList **conns);
+       int (*disconnect) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type);
+       int (*remove_bonding) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type);
+       int (*pincode_reply) (int index, bdaddr_t *bdaddr, const char *pin,
+                                                       size_t pin_len);
+       int (*confirm_reply) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                                                       gboolean success);
+       int (*passkey_reply) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                                                       uint32_t passkey);
+       int (*encrypt_link) (int index, bdaddr_t *bdaddr, bt_hci_result_t cb,
+                                                       gpointer user_data);
+       int (*set_did) (int index, uint16_t vendor, uint16_t product,
+                                       uint16_t version, uint16_t source);
+       int (*add_uuid) (int index, uuid_t *uuid, uint8_t svc_hint);
+       int (*remove_uuid) (int index, uuid_t *uuid);
+       int (*disable_cod_cache) (int index);
+       int (*restore_powered) (int index);
+       int (*load_keys) (int index, GSList *keys, gboolean debug_keys);
+       int (*set_io_capability) (int index, uint8_t io_capability);
+       int (*create_bonding) (int index, bdaddr_t *bdaddr,
+                                       uint8_t bdaddr_type, uint8_t io_cap);
+       int (*cancel_bonding) (int index, bdaddr_t *bdaddr);
+       int (*read_local_oob_data) (int index);
+       int (*add_remote_oob_data) (int index, bdaddr_t *bdaddr, uint8_t *hash,
+                                                       uint8_t *randomizer);
+       int (*remove_remote_oob_data) (int index, bdaddr_t *bdaddr);
+       int (*confirm_name) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                                                       gboolean name_known);
+       int (*load_ltks) (int index, GSList *keys);
+};
+
+int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority);
+void btd_adapter_cleanup_ops(struct btd_adapter_ops *btd_adapter_ops);
+int adapter_ops_setup(void);
+
+typedef void (*btd_adapter_powered_cb) (struct btd_adapter *adapter,
+                                               gboolean powered);
+void btd_adapter_register_powered_callback(struct btd_adapter *adapter,
+                                               btd_adapter_powered_cb cb);
+void btd_adapter_unregister_powered_callback(struct btd_adapter *adapter,
+                                               btd_adapter_powered_cb cb);
+
+/* If TRUE, enables fast connectabe, i.e. reduces page scan interval and changes
+ * type. If FALSE, disables fast connectable, i.e. sets page scan interval and
+ * type to default values. Valid for both connectable and discoverable modes. */
+int btd_adapter_set_fast_connectable(struct btd_adapter *adapter,
+                                                       gboolean enable);
+
+int btd_adapter_read_clock(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                               int which, int timeout, uint32_t *clock,
+                               uint16_t *accuracy);
+
+int btd_adapter_block_address(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type);
+int btd_adapter_unblock_address(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type);
+
+int btd_adapter_disconnect_device(struct btd_adapter *adapter,
+                                       bdaddr_t *bdaddr, uint8_t bdaddr_type);
+
+int btd_adapter_remove_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type);
+
+int btd_adapter_pincode_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                       const char *pin, size_t pin_len);
+int btd_adapter_confirm_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                       uint8_t bdaddr_type, gboolean success);
+int btd_adapter_passkey_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                       uint8_t bdaddr_type, uint32_t passkey);
+
+int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                               bt_hci_result_t cb, gpointer user_data);
+
+int btd_adapter_set_did(struct btd_adapter *adapter, uint16_t vendor,
+                                       uint16_t product, uint16_t version,
+                                       uint16_t source);
+
+int adapter_create_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                               uint8_t bdaddr_type, uint8_t io_cap);
+
+int adapter_cancel_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+
+void adapter_bonding_complete(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+                                                       uint8_t status);
+
+int btd_adapter_read_local_oob_data(struct btd_adapter *adapter);
+
+int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
+                       bdaddr_t *bdaddr, uint8_t *hash, uint8_t *randomizer);
+
+int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
+                                                       bdaddr_t *bdaddr);
+
+int btd_adapter_gatt_server_start(struct btd_adapter *adapter);
+void btd_adapter_gatt_server_stop(struct btd_adapter *adapter);
diff --git a/src/agent.c b/src/agent.c
new file mode 100644 (file)
index 0000000..e542425
--- /dev/null
@@ -0,0 +1,844 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "adapter.h"
+#include "device.h"
+#include "agent.h"
+
+#define REQUEST_TIMEOUT (60 * 1000)            /* 60 seconds */
+
+typedef enum {
+       AGENT_REQUEST_PASSKEY,
+       AGENT_REQUEST_CONFIRMATION,
+       AGENT_REQUEST_PINCODE,
+       AGENT_REQUEST_AUTHORIZE,
+       AGENT_REQUEST_CONFIRM_MODE,
+       AGENT_REQUEST_DISPLAY_PINCODE,
+} agent_request_type_t;
+
+struct agent {
+       struct btd_adapter *adapter;
+       char *name;
+       char *path;
+       uint8_t capability;
+       struct agent_request *request;
+       int exited;
+       agent_remove_cb remove_cb;
+       void *remove_cb_data;
+       guint listener_id;
+};
+
+struct agent_request {
+       agent_request_type_t type;
+       struct agent *agent;
+       DBusMessage *msg;
+       DBusPendingCall *call;
+       void *cb;
+       void *user_data;
+       GDestroyNotify destroy;
+};
+
+static DBusConnection *connection = NULL;
+
+static void agent_release(struct agent *agent)
+{
+       DBusMessage *message;
+
+       DBG("Releasing agent %s, %s", agent->name, agent->path);
+
+       if (agent->request)
+               agent_cancel(agent);
+
+       message = dbus_message_new_method_call(agent->name, agent->path,
+                       "org.bluez.Agent", "Release");
+       if (message == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return;
+       }
+
+       g_dbus_send_message(connection, message);
+}
+
+static int send_cancel_request(struct agent_request *req)
+{
+       DBusMessage *message;
+
+       DBG("Sending Cancel request to %s, %s", req->agent->name,
+                                                       req->agent->path);
+
+       message = dbus_message_new_method_call(req->agent->name, req->agent->path,
+                                               "org.bluez.Agent", "Cancel");
+       if (message == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return -ENOMEM;
+       }
+
+       g_dbus_send_message(connection, message);
+
+       return 0;
+}
+
+static void agent_request_free(struct agent_request *req, gboolean destroy)
+{
+       if (req->msg)
+               dbus_message_unref(req->msg);
+       if (req->call)
+               dbus_pending_call_unref(req->call);
+       if (req->agent && req->agent->request)
+               req->agent->request = NULL;
+       if (destroy && req->destroy)
+               req->destroy(req->user_data);
+       g_free(req);
+}
+
+static void agent_exited(DBusConnection *conn, void *user_data)
+{
+       struct agent *agent = user_data;
+
+       DBG("Agent exited without calling Unregister");
+
+       agent->exited = TRUE;
+
+       agent_free(agent);
+}
+
+void agent_free(struct agent *agent)
+{
+       if (!agent)
+               return;
+
+       if (agent->remove_cb)
+               agent->remove_cb(agent, agent->remove_cb_data);
+
+       if (agent->request) {
+               DBusError err;
+               agent_pincode_cb pincode_cb;
+               agent_passkey_cb passkey_cb;
+               agent_cb cb;
+
+               dbus_error_init(&err);
+               dbus_set_error_const(&err, "org.bluez.Error.Failed", "Canceled");
+
+               switch (agent->request->type) {
+               case AGENT_REQUEST_PINCODE:
+                       pincode_cb = agent->request->cb;
+                       pincode_cb(agent, &err, NULL, agent->request->user_data);
+                       break;
+               case AGENT_REQUEST_PASSKEY:
+                       passkey_cb = agent->request->cb;
+                       passkey_cb(agent, &err, 0, agent->request->user_data);
+                       break;
+               default:
+                       cb = agent->request->cb;
+                       cb(agent, &err, agent->request->user_data);
+               }
+
+               dbus_error_free(&err);
+
+               agent_cancel(agent);
+       }
+
+       if (!agent->exited) {
+               g_dbus_remove_watch(connection, agent->listener_id);
+               agent_release(agent);
+       }
+
+       g_free(agent->name);
+       g_free(agent->path);
+
+       g_free(agent);
+}
+
+struct agent *agent_create(struct btd_adapter *adapter, const char *name,
+                               const char *path, uint8_t capability,
+                               agent_remove_cb cb, void *remove_cb_data)
+{
+       struct agent *agent;
+
+       agent = g_new0(struct agent, 1);
+
+       agent->adapter = adapter;
+       agent->name = g_strdup(name);
+       agent->path = g_strdup(path);
+       agent->capability = capability;
+       agent->remove_cb = cb;
+       agent->remove_cb_data = remove_cb_data;
+
+       agent->listener_id = g_dbus_add_disconnect_watch(connection, name,
+                                                       agent_exited, agent,
+                                                       NULL);
+
+       return agent;
+}
+
+static struct agent_request *agent_request_new(struct agent *agent,
+                                               agent_request_type_t type,
+                                               void *cb,
+                                               void *user_data,
+                                               GDestroyNotify destroy)
+{
+       struct agent_request *req;
+
+       req = g_new0(struct agent_request, 1);
+
+       req->agent = agent;
+       req->type = type;
+       req->cb = cb;
+       req->user_data = user_data;
+       req->destroy = destroy;
+
+       return req;
+}
+
+int agent_cancel(struct agent *agent)
+{
+       if (!agent->request)
+               return -EINVAL;
+
+       if (agent->request->call)
+               dbus_pending_call_cancel(agent->request->call);
+
+       if (!agent->exited)
+               send_cancel_request(agent->request);
+
+       agent_request_free(agent->request, TRUE);
+       agent->request = NULL;
+
+       return 0;
+}
+
+static void simple_agent_reply(DBusPendingCall *call, void *user_data)
+{
+       struct agent_request *req = user_data;
+       struct agent *agent = req->agent;
+       DBusMessage *message;
+       DBusError err;
+       agent_cb cb = req->cb;
+
+       /* steal_reply will always return non-NULL since the callback
+        * is only called after a reply has been received */
+       message = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, message)) {
+               error("Agent replied with an error: %s, %s",
+                               err.name, err.message);
+
+               cb(agent, &err, req->user_data);
+
+               if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+                       agent_cancel(agent);
+                       dbus_message_unref(message);
+                       dbus_error_free(&err);
+                       return;
+               }
+
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
+               error("Wrong reply signature: %s", err.message);
+               cb(agent, &err, req->user_data);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       cb(agent, NULL, req->user_data);
+done:
+       dbus_message_unref(message);
+
+       agent->request = NULL;
+       agent_request_free(req, TRUE);
+}
+
+static int agent_call_authorize(struct agent_request *req,
+                               const char *device_path,
+                               const char *uuid)
+{
+       struct agent *agent = req->agent;
+
+       req->msg = dbus_message_new_method_call(agent->name, agent->path,
+                               "org.bluez.Agent", "Authorize");
+       if (!req->msg) {
+               error("Couldn't allocate D-Bus message");
+               return -ENOMEM;
+       }
+
+       dbus_message_append_args(req->msg,
+                               DBUS_TYPE_OBJECT_PATH, &device_path,
+                               DBUS_TYPE_STRING, &uuid,
+                               DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, req->msg,
+                                       &req->call, REQUEST_TIMEOUT) == FALSE) {
+               error("D-Bus send failed");
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+       return 0;
+}
+
+int agent_authorize(struct agent *agent,
+                       const char *path,
+                       const char *uuid,
+                       agent_cb cb,
+                       void *user_data,
+                       GDestroyNotify destroy)
+{
+       struct agent_request *req;
+       int err;
+
+       if (agent->request)
+               return -EBUSY;
+
+       req = agent_request_new(agent, AGENT_REQUEST_AUTHORIZE, cb,
+                                                       user_data, destroy);
+
+       err = agent_call_authorize(req, path, uuid);
+       if (err < 0) {
+               agent_request_free(req, FALSE);
+               return -ENOMEM;
+       }
+
+       agent->request = req;
+
+       DBG("authorize request was sent for %s", path);
+
+       return 0;
+}
+
+static void pincode_reply(DBusPendingCall *call, void *user_data)
+{
+       struct agent_request *req = user_data;
+       struct agent *agent = req->agent;
+       struct btd_adapter *adapter = agent->adapter;
+       agent_pincode_cb cb = req->cb;
+       DBusMessage *message;
+       DBusError err;
+       bdaddr_t sba;
+       size_t len;
+       char *pin;
+
+       adapter_get_address(adapter, &sba);
+
+       /* steal_reply will always return non-NULL since the callback
+        * is only called after a reply has been received */
+       message = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, message)) {
+               error("Agent %s replied with an error: %s, %s",
+                               agent->path, err.name, err.message);
+
+               cb(agent, &err, NULL, req->user_data);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (!dbus_message_get_args(message, &err,
+                               DBUS_TYPE_STRING, &pin,
+                               DBUS_TYPE_INVALID)) {
+               error("Wrong passkey reply signature: %s", err.message);
+               cb(agent, &err, NULL, req->user_data);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       len = strlen(pin);
+
+       if (len > 16 || len < 1) {
+               error("Invalid PIN length (%zu) from agent", len);
+               dbus_set_error_const(&err, "org.bluez.Error.InvalidArgs",
+                                       "Invalid passkey length");
+               cb(agent, &err, NULL, req->user_data);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       cb(agent, NULL, pin, req->user_data);
+
+done:
+       if (message)
+               dbus_message_unref(message);
+
+       dbus_pending_call_cancel(req->call);
+       agent->request = NULL;
+       agent_request_free(req, TRUE);
+}
+
+static int pincode_request_new(struct agent_request *req, const char *device_path,
+                               dbus_bool_t secure)
+{
+       struct agent *agent = req->agent;
+
+       /* TODO: Add a new method or a new param to Agent interface to request
+               secure pin. */
+
+       req->msg = dbus_message_new_method_call(agent->name, agent->path,
+                                       "org.bluez.Agent", "RequestPinCode");
+       if (req->msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return -ENOMEM;
+       }
+
+       dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, req->msg,
+                                       &req->call, REQUEST_TIMEOUT) == FALSE) {
+               error("D-Bus send failed");
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(req->call, pincode_reply, req, NULL);
+       return 0;
+}
+
+int agent_request_pincode(struct agent *agent, struct btd_device *device,
+                               agent_pincode_cb cb, gboolean secure,
+                               void *user_data, GDestroyNotify destroy)
+{
+       struct agent_request *req;
+       const gchar *dev_path = device_get_path(device);
+       int err;
+
+       if (agent->request)
+               return -EBUSY;
+
+       req = agent_request_new(agent, AGENT_REQUEST_PINCODE, cb,
+                                                       user_data, destroy);
+
+       err = pincode_request_new(req, dev_path, secure);
+       if (err < 0)
+               goto failed;
+
+       agent->request = req;
+
+       return 0;
+
+failed:
+       agent_request_free(req, FALSE);
+       return err;
+}
+
+static int confirm_mode_change_request_new(struct agent_request *req,
+                                               const char *mode)
+{
+       struct agent *agent = req->agent;
+
+       req->msg = dbus_message_new_method_call(agent->name, agent->path,
+                               "org.bluez.Agent", "ConfirmModeChange");
+       if (req->msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return -ENOMEM;
+       }
+
+       dbus_message_append_args(req->msg,
+                               DBUS_TYPE_STRING, &mode,
+                               DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, req->msg,
+                                       &req->call, REQUEST_TIMEOUT) == FALSE) {
+               error("D-Bus send failed");
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+       return 0;
+}
+
+int agent_confirm_mode_change(struct agent *agent, const char *new_mode,
+                               agent_cb cb, void *user_data,
+                               GDestroyNotify destroy)
+{
+       struct agent_request *req;
+       int err;
+
+       if (agent->request)
+               return -EBUSY;
+
+       DBG("Calling Agent.ConfirmModeChange: name=%s, path=%s, mode=%s",
+                       agent->name, agent->path, new_mode);
+
+       req = agent_request_new(agent, AGENT_REQUEST_CONFIRM_MODE,
+                               cb, user_data, destroy);
+
+       err = confirm_mode_change_request_new(req, new_mode);
+       if (err < 0)
+               goto failed;
+
+       agent->request = req;
+
+       return 0;
+
+failed:
+       agent_request_free(req, FALSE);
+       return err;
+}
+
+static void passkey_reply(DBusPendingCall *call, void *user_data)
+{
+       struct agent_request *req = user_data;
+       struct agent *agent = req->agent;
+       agent_passkey_cb cb = req->cb;
+       DBusMessage *message;
+       DBusError err;
+       uint32_t passkey;
+
+       /* steal_reply will always return non-NULL since the callback
+        * is only called after a reply has been received */
+       message = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, message)) {
+               error("Agent replied with an error: %s, %s",
+                                               err.name, err.message);
+               cb(agent, &err, 0, req->user_data);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (!dbus_message_get_args(message, &err,
+                               DBUS_TYPE_UINT32, &passkey,
+                               DBUS_TYPE_INVALID)) {
+               error("Wrong passkey reply signature: %s", err.message);
+               cb(agent, &err, 0, req->user_data);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       cb(agent, NULL, passkey, req->user_data);
+
+done:
+       if (message)
+               dbus_message_unref(message);
+
+       dbus_pending_call_cancel(req->call);
+       agent->request = NULL;
+       agent_request_free(req, TRUE);
+}
+
+static int passkey_request_new(struct agent_request *req,
+                               const char *device_path)
+{
+       struct agent *agent = req->agent;
+
+       req->msg = dbus_message_new_method_call(agent->name, agent->path,
+                                       "org.bluez.Agent", "RequestPasskey");
+       if (req->msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return -ENOMEM;
+       }
+
+       dbus_message_append_args(req->msg, DBUS_TYPE_OBJECT_PATH, &device_path,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, req->msg,
+                                       &req->call, REQUEST_TIMEOUT) == FALSE) {
+               error("D-Bus send failed");
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(req->call, passkey_reply, req, NULL);
+       return 0;
+}
+
+int agent_request_passkey(struct agent *agent, struct btd_device *device,
+                               agent_passkey_cb cb, void *user_data,
+                               GDestroyNotify destroy)
+{
+       struct agent_request *req;
+       const gchar *dev_path = device_get_path(device);
+       int err;
+
+       if (agent->request)
+               return -EBUSY;
+
+       DBG("Calling Agent.RequestPasskey: name=%s, path=%s",
+                       agent->name, agent->path);
+
+       req = agent_request_new(agent, AGENT_REQUEST_PASSKEY, cb,
+                                                       user_data, destroy);
+
+       err = passkey_request_new(req, dev_path);
+       if (err < 0)
+               goto failed;
+
+       agent->request = req;
+
+       return 0;
+
+failed:
+       agent_request_free(req, FALSE);
+       return err;
+}
+
+static int confirmation_request_new(struct agent_request *req,
+                                       const char *device_path,
+                                       uint32_t passkey)
+{
+       struct agent *agent = req->agent;
+
+       req->msg = dbus_message_new_method_call(agent->name, agent->path,
+                               "org.bluez.Agent", "RequestConfirmation");
+       if (req->msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return -ENOMEM;
+       }
+
+       dbus_message_append_args(req->msg,
+                               DBUS_TYPE_OBJECT_PATH, &device_path,
+                               DBUS_TYPE_UINT32, &passkey,
+                               DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, req->msg,
+                               &req->call, REQUEST_TIMEOUT) == FALSE) {
+               error("D-Bus send failed");
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
+
+       return 0;
+}
+
+int agent_request_confirmation(struct agent *agent, struct btd_device *device,
+                               uint32_t passkey, agent_cb cb,
+                               void *user_data, GDestroyNotify destroy)
+{
+       struct agent_request *req;
+       const gchar *dev_path = device_get_path(device);
+       int err;
+
+       if (agent->request)
+               return -EBUSY;
+
+       DBG("Calling Agent.RequestConfirmation: name=%s, path=%s, passkey=%06u",
+                       agent->name, agent->path, passkey);
+
+       req = agent_request_new(agent, AGENT_REQUEST_CONFIRMATION, cb,
+                               user_data, destroy);
+
+       err = confirmation_request_new(req, dev_path, passkey);
+       if (err < 0)
+               goto failed;
+
+       agent->request = req;
+
+       return 0;
+
+failed:
+       agent_request_free(req, FALSE);
+       return err;
+}
+
+int agent_display_passkey(struct agent *agent, struct btd_device *device,
+                               uint32_t passkey)
+{
+       DBusMessage *message;
+       const gchar *dev_path = device_get_path(device);
+
+       message = dbus_message_new_method_call(agent->name, agent->path,
+                               "org.bluez.Agent", "DisplayPasskey");
+       if (!message) {
+               error("Couldn't allocate D-Bus message");
+               return -1;
+       }
+
+       dbus_message_append_args(message,
+                               DBUS_TYPE_OBJECT_PATH, &dev_path,
+                               DBUS_TYPE_UINT32, &passkey,
+                               DBUS_TYPE_INVALID);
+
+       if (!g_dbus_send_message(connection, message)) {
+               error("D-Bus send failed");
+               return -1;
+       }
+
+       return 0;
+}
+
+static void display_pincode_reply(DBusPendingCall *call, void *user_data)
+{
+       struct agent_request *req = user_data;
+       struct agent *agent = req->agent;
+       DBusMessage *message;
+       DBusError err;
+       agent_cb cb = req->cb;
+
+       /* clear agent->request early; our callback will likely try
+        * another request */
+       agent->request = NULL;
+
+       /* steal_reply will always return non-NULL since the callback
+        * is only called after a reply has been received */
+       message = dbus_pending_call_steal_reply(call);
+
+       dbus_error_init(&err);
+       if (dbus_set_error_from_message(&err, message)) {
+               error("Agent replied with an error: %s, %s",
+                                               err.name, err.message);
+
+               cb(agent, &err, req->user_data);
+
+               if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+                       agent_cancel(agent);
+                       dbus_message_unref(message);
+                       dbus_error_free(&err);
+                       return;
+               }
+
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
+               error("Wrong reply signature: %s", err.message);
+               cb(agent, &err, req->user_data);
+               dbus_error_free(&err);
+               goto done;
+       }
+
+       cb(agent, NULL, req->user_data);
+done:
+       dbus_message_unref(message);
+
+       agent_request_free(req, TRUE);
+}
+
+static int display_pincode_request_new(struct agent_request *req,
+                                       const char *device_path,
+                                       const char *pincode)
+{
+       struct agent *agent = req->agent;
+
+       req->msg = dbus_message_new_method_call(agent->name, agent->path,
+                                       "org.bluez.Agent", "DisplayPinCode");
+       if (req->msg == NULL) {
+               error("Couldn't allocate D-Bus message");
+               return -ENOMEM;
+       }
+
+       dbus_message_append_args(req->msg,
+                                       DBUS_TYPE_OBJECT_PATH, &device_path,
+                                       DBUS_TYPE_STRING, &pincode,
+                                       DBUS_TYPE_INVALID);
+
+       if (dbus_connection_send_with_reply(connection, req->msg,
+                               &req->call, REQUEST_TIMEOUT) == FALSE) {
+               error("D-Bus send failed");
+               return -EIO;
+       }
+
+       dbus_pending_call_set_notify(req->call, display_pincode_reply,
+                                                               req, NULL);
+
+       return 0;
+}
+
+int agent_display_pincode(struct agent *agent, struct btd_device *device,
+                               const char *pincode, agent_cb cb,
+                               void *user_data, GDestroyNotify destroy)
+{
+       struct agent_request *req;
+       const gchar *dev_path = device_get_path(device);
+       int err;
+
+       if (agent->request)
+               return -EBUSY;
+
+       DBG("Calling Agent.DisplayPinCode: name=%s, path=%s, pincode=%s",
+                                       agent->name, agent->path, pincode);
+
+       req = agent_request_new(agent, AGENT_REQUEST_DISPLAY_PINCODE, cb,
+                                                       user_data, destroy);
+
+       err = display_pincode_request_new(req, dev_path, pincode);
+       if (err < 0)
+               goto failed;
+
+       agent->request = req;
+
+       return 0;
+
+failed:
+       agent_request_free(req, FALSE);
+       return err;
+}
+
+uint8_t agent_get_io_capability(struct agent *agent)
+{
+       return agent->capability;
+}
+
+gboolean agent_matches(struct agent *agent, const char *name, const char *path)
+{
+       if (g_str_equal(agent->name, name) && g_str_equal(agent->path, path))
+               return TRUE;
+
+       return FALSE;
+}
+
+gboolean agent_is_busy(struct agent *agent, void *user_data)
+{
+       if (!agent->request)
+               return FALSE;
+
+       if (user_data && user_data != agent->request->user_data)
+               return FALSE;
+
+       return TRUE;
+}
+
+void agent_exit(void)
+{
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
+
+void agent_init(void)
+{
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+}
diff --git a/src/agent.h b/src/agent.h
new file mode 100644 (file)
index 0000000..320b92d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct agent;
+
+typedef void (*agent_cb) (struct agent *agent, DBusError *err,
+                               void *user_data);
+
+typedef void (*agent_pincode_cb) (struct agent *agent, DBusError *err,
+                                       const char *pincode, void *user_data);
+
+typedef void (*agent_passkey_cb) (struct agent *agent, DBusError *err,
+                                       uint32_t passkey, void *user_data);
+
+typedef void (*agent_remove_cb) (struct agent *agent, void *user_data);
+
+struct agent *agent_create(struct btd_adapter *adapter, const char *name,
+                               const char *path, uint8_t capability,
+                               agent_remove_cb cb, void *remove_cb_data);
+
+void agent_free(struct agent *agent);
+
+int agent_authorize(struct agent *agent, const char *path,
+                       const char *uuid, agent_cb cb, void *user_data,
+                       GDestroyNotify destroy);
+
+int agent_request_pincode(struct agent *agent, struct btd_device *device,
+                               agent_pincode_cb cb, gboolean secure,
+                               void *user_data, GDestroyNotify destroy);
+
+int agent_confirm_mode_change(struct agent *agent, const char *new_mode,
+                               agent_cb cb, void *user_data,
+                               GDestroyNotify destroy);
+
+int agent_request_passkey(struct agent *agent, struct btd_device *device,
+                               agent_passkey_cb cb, void *user_data,
+                               GDestroyNotify destroy);
+
+int agent_request_confirmation(struct agent *agent, struct btd_device *device,
+                               uint32_t passkey, agent_cb cb,
+                               void *user_data, GDestroyNotify destroy);
+
+int agent_display_passkey(struct agent *agent, struct btd_device *device,
+                               uint32_t passkey);
+
+int agent_display_pincode(struct agent *agent, struct btd_device *device,
+                               const char *pincode, agent_cb cb,
+                               void *user_data, GDestroyNotify destroy);
+
+int agent_cancel(struct agent *agent);
+
+gboolean agent_is_busy(struct agent *agent, void *user_data);
+
+uint8_t agent_get_io_capability(struct agent *agent);
+
+gboolean agent_matches(struct agent *agent, const char *name, const char *path);
+
+void agent_init(void);
+void agent_exit(void);
diff --git a/src/attio.h b/src/attio.h
new file mode 100644 (file)
index 0000000..16e2873
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef void (*attio_connect_cb) (GAttrib *attrib, gpointer user_data);
+typedef void (*attio_disconnect_cb) (gpointer user_data);
+
+guint btd_device_add_attio_callback(struct btd_device *device,
+                                               attio_connect_cb cfunc,
+                                               attio_disconnect_cb dcfunc,
+                                               gpointer user_data);
+
+gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id);
diff --git a/src/attrib-server.c b/src/attrib-server.c
new file mode 100644 (file)
index 0000000..5adbf92
--- /dev/null
@@ -0,0 +1,1542 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "log.h"
+#include "gdbus.h"
+#include "btio.h"
+#include "sdpd.h"
+#include "hcid.h"
+#include "adapter.h"
+#include "device.h"
+#include "manager.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
+#include "storage.h"
+
+#include "attrib-server.h"
+
+static GSList *servers = NULL;
+
+struct gatt_server {
+       struct btd_adapter *adapter;
+       GIOChannel *l2cap_io;
+       GIOChannel *le_io;
+       uint32_t gatt_sdp_handle;
+       uint32_t gap_sdp_handle;
+       GList *database;
+       GSList *clients;
+       uint16_t name_handle;
+       uint16_t appearance_handle;
+};
+
+struct gatt_channel {
+       bdaddr_t src;
+       bdaddr_t dst;
+       GAttrib *attrib;
+       guint mtu;
+       gboolean le;
+       guint id;
+       gboolean encrypted;
+       struct gatt_server *server;
+       guint cleanup_id;
+       struct btd_device *device;
+};
+
+struct group_elem {
+       uint16_t handle;
+       uint16_t end;
+       uint8_t *data;
+       uint16_t len;
+};
+
+static bt_uuid_t prim_uuid = {
+                       .type = BT_UUID16,
+                       .value.u16 = GATT_PRIM_SVC_UUID
+};
+static bt_uuid_t snd_uuid = {
+                       .type = BT_UUID16,
+                       .value.u16 = GATT_SND_SVC_UUID
+};
+static bt_uuid_t ccc_uuid = {
+                       .type = BT_UUID16,
+                       .value.u16 = GATT_CLIENT_CHARAC_CFG_UUID
+};
+
+static void attrib_free(void *data)
+{
+       struct attribute *a = data;
+
+       g_free(a->data);
+       g_free(a);
+}
+
+static void channel_free(struct gatt_channel *channel)
+{
+
+       if (channel->cleanup_id)
+               g_source_remove(channel->cleanup_id);
+
+       if (channel->device)
+               btd_device_unref(channel->device);
+
+       g_attrib_unref(channel->attrib);
+       g_free(channel);
+}
+
+static void gatt_server_free(struct gatt_server *server)
+{
+       g_list_free_full(server->database, attrib_free);
+
+       if (server->l2cap_io != NULL) {
+               g_io_channel_unref(server->l2cap_io);
+               g_io_channel_shutdown(server->l2cap_io, FALSE, NULL);
+       }
+
+       if (server->le_io != NULL) {
+               g_io_channel_unref(server->le_io);
+               g_io_channel_shutdown(server->le_io, FALSE, NULL);
+       }
+
+       g_slist_free_full(server->clients, (GDestroyNotify) channel_free);
+
+       if (server->gatt_sdp_handle > 0)
+               remove_record_from_server(server->gatt_sdp_handle);
+
+       if (server->gap_sdp_handle > 0)
+               remove_record_from_server(server->gap_sdp_handle);
+
+       if (server->adapter != NULL)
+               btd_adapter_unref(server->adapter);
+
+       g_free(server);
+}
+
+static gint adapter_cmp_addr(gconstpointer a, gconstpointer b)
+{
+       const struct gatt_server *server = a;
+       const bdaddr_t *bdaddr = b;
+       bdaddr_t src;
+
+       adapter_get_address(server->adapter, &src);
+
+       return bacmp(&src, bdaddr);
+}
+
+static gint adapter_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct gatt_server *server = a;
+       const struct btd_adapter *adapter = b;
+
+       if (server->adapter == adapter)
+               return 0;
+
+       return -1;
+}
+
+static struct gatt_server *find_gatt_server(const bdaddr_t *bdaddr)
+{
+       GSList *l;
+
+       l = g_slist_find_custom(servers, bdaddr, adapter_cmp_addr);
+       if (l == NULL) {
+               char addr[18];
+
+               ba2str(bdaddr, addr);
+               error("No GATT server found in %s", addr);
+               return NULL;
+       }
+
+       return l->data;
+}
+
+static sdp_record_t *server_record_new(uuid_t *uuid, uint16_t start, uint16_t end)
+{
+       sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto;
+       uuid_t root_uuid, proto_uuid, l2cap;
+       sdp_record_t *record;
+       sdp_data_t *psm, *sh, *eh;
+       uint16_t lp = ATT_PSM;
+
+       if (uuid == NULL)
+               return NULL;
+
+       if (start > end)
+               return NULL;
+
+       record = sdp_record_alloc();
+       if (record == NULL)
+               return NULL;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(record, root);
+       sdp_list_free(root, NULL);
+
+       svclass_id = sdp_list_append(NULL, uuid);
+       sdp_set_service_classes(record, svclass_id);
+       sdp_list_free(svclass_id, NULL);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(NULL, &l2cap);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&proto_uuid, ATT_UUID);
+       proto[1] = sdp_list_append(NULL, &proto_uuid);
+       sh = sdp_data_alloc(SDP_UINT16, &start);
+       proto[1] = sdp_list_append(proto[1], sh);
+       eh = sdp_data_alloc(SDP_UINT16, &end);
+       proto[1] = sdp_list_append(proto[1], eh);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_data_free(psm);
+       sdp_data_free(sh);
+       sdp_data_free(eh);
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(aproto, NULL);
+
+       return record;
+}
+
+static int handle_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct attribute *attrib = a;
+       uint16_t handle = GPOINTER_TO_UINT(b);
+
+       return attrib->handle - handle;
+}
+
+static int attribute_cmp(gconstpointer a1, gconstpointer a2)
+{
+       const struct attribute *attrib1 = a1;
+       const struct attribute *attrib2 = a2;
+
+       return attrib1->handle - attrib2->handle;
+}
+
+static struct attribute *find_svc_range(struct gatt_server *server,
+                                       uint16_t start, uint16_t *end)
+{
+       struct attribute *attrib;
+       guint h = start;
+       GList *l;
+
+       if (end == NULL)
+               return NULL;
+
+       l = g_list_find_custom(server->database, GUINT_TO_POINTER(h),
+                                                               handle_cmp);
+       if (!l)
+               return NULL;
+
+       attrib = l->data;
+
+       if (bt_uuid_cmp(&attrib->uuid, &prim_uuid) != 0 &&
+                       bt_uuid_cmp(&attrib->uuid, &snd_uuid) != 0)
+               return NULL;
+
+       *end = start;
+
+       for (l = l->next; l; l = l->next) {
+               struct attribute *a = l->data;
+
+               if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+                               bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)
+                       break;
+
+               *end = a->handle;
+       }
+
+       return attrib;
+}
+
+static uint32_t attrib_create_sdp_new(struct gatt_server *server,
+                                       uint16_t handle, const char *name)
+{
+       sdp_record_t *record;
+       struct attribute *a;
+       uint16_t end = 0;
+       uuid_t svc, gap_uuid;
+       bdaddr_t addr;
+
+       a = find_svc_range(server, handle, &end);
+
+       if (a == NULL)
+               return 0;
+
+       if (a->len == 2)
+               sdp_uuid16_create(&svc, att_get_u16(a->data));
+       else if (a->len == 16)
+               sdp_uuid128_create(&svc, a->data);
+       else
+               return 0;
+
+       record = server_record_new(&svc, handle, end);
+       if (record == NULL)
+               return 0;
+
+       if (name != NULL)
+               sdp_set_info_attr(record, name, "BlueZ", NULL);
+
+       sdp_uuid16_create(&gap_uuid, GENERIC_ACCESS_PROFILE_ID);
+       if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
+               sdp_set_url_attr(record, "http://www.bluez.org/",
+                               "http://www.bluez.org/",
+                               "http://www.bluez.org/");
+       }
+
+       adapter_get_address(server->adapter, &addr);
+       if (add_record_to_server(&addr, record) == 0)
+               return record->handle;
+
+       sdp_record_free(record);
+       return 0;
+}
+
+static struct attribute *attrib_db_add_new(struct gatt_server *server,
+                               uint16_t handle, bt_uuid_t *uuid, int read_reqs,
+                               int write_reqs, const uint8_t *value, int len)
+{
+       struct attribute *a;
+       guint h = handle;
+
+       DBG("handle=0x%04x", handle);
+
+       if (g_list_find_custom(server->database, GUINT_TO_POINTER(h),
+                                                               handle_cmp))
+               return NULL;
+
+       a = g_new0(struct attribute, 1);
+       a->len = len;
+       a->data = g_memdup(value, len);
+       a->handle = handle;
+       a->uuid = *uuid;
+       a->read_reqs = read_reqs;
+       a->write_reqs = write_reqs;
+
+       server->database = g_list_insert_sorted(server->database, a,
+                                                               attribute_cmp);
+
+       return a;
+}
+
+static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode,
+                                                               int reqs)
+{
+       /* FIXME: currently, it is assumed an encrypted link is enough for
+        * authentication. This will allow to enable the SMP negotiation once
+        * it is on upstream kernel. High security level should be mapped
+        * to authentication and medium to encryption permission. */
+       if (!channel->encrypted)
+               channel->encrypted = g_attrib_is_encrypted(channel->attrib);
+       if (reqs == ATT_AUTHENTICATION && !channel->encrypted)
+               return ATT_ECODE_AUTHENTICATION;
+       else if (reqs == ATT_AUTHORIZATION)
+               return ATT_ECODE_AUTHORIZATION;
+
+       switch (opcode) {
+       case ATT_OP_READ_BY_GROUP_REQ:
+       case ATT_OP_READ_BY_TYPE_REQ:
+       case ATT_OP_READ_REQ:
+       case ATT_OP_READ_BLOB_REQ:
+       case ATT_OP_READ_MULTI_REQ:
+               if (reqs == ATT_NOT_PERMITTED)
+                       return ATT_ECODE_READ_NOT_PERM;
+               break;
+       case ATT_OP_PREP_WRITE_REQ:
+       case ATT_OP_WRITE_REQ:
+       case ATT_OP_WRITE_CMD:
+               if (reqs == ATT_NOT_PERMITTED)
+                       return ATT_ECODE_WRITE_NOT_PERM;
+               break;
+       }
+
+       return 0;
+}
+
+static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start,
+                                               uint16_t end, bt_uuid_t *uuid,
+                                               uint8_t *pdu, int len)
+{
+       struct att_data_list *adl;
+       struct attribute *a;
+       struct group_elem *cur, *old = NULL;
+       GSList *l, *groups;
+       GList *dl, *database;
+       uint16_t length, last_handle, last_size = 0;
+       uint8_t status;
+       int i;
+
+       if (start > end || start == 0x0000)
+               return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start,
+                                       ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+       /*
+        * Only <<Primary Service>> and <<Secondary Service>> grouping
+        * types may be used in the Read By Group Type Request.
+        */
+
+       if (bt_uuid_cmp(uuid, &prim_uuid) != 0 &&
+               bt_uuid_cmp(uuid, &snd_uuid) != 0)
+               return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, 0x0000,
+                                       ATT_ECODE_UNSUPP_GRP_TYPE, pdu, len);
+
+       last_handle = end;
+       database = channel->server->database;
+       for (dl = database, groups = NULL, cur = NULL; dl; dl = dl->next) {
+
+               a = dl->data;
+
+               if (a->handle < start)
+                       continue;
+
+               if (a->handle >= end)
+                       break;
+
+               /* The old group ends when a new one starts */
+               if (old && (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+                               bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)) {
+                       old->end = last_handle;
+                       old = NULL;
+               }
+
+               if (bt_uuid_cmp(&a->uuid, uuid) != 0) {
+                       /* Still inside a service, update its last handle */
+                       if (old)
+                               last_handle = a->handle;
+                       continue;
+               }
+
+               if (last_size && (last_size != a->len))
+                       break;
+
+               status = att_check_reqs(channel, ATT_OP_READ_BY_GROUP_REQ,
+                                                               a->read_reqs);
+
+               if (status == 0x00 && a->read_cb)
+                       status = a->read_cb(a, channel->device,
+                                                       a->cb_user_data);
+
+               if (status) {
+                       g_slist_free_full(groups, g_free);
+                       return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ,
+                                               a->handle, status, pdu, len);
+               }
+
+               cur = g_new0(struct group_elem, 1);
+               cur->handle = a->handle;
+               cur->data = a->data;
+               cur->len = a->len;
+
+               /* Attribute Grouping Type found */
+               groups = g_slist_append(groups, cur);
+
+               last_size = a->len;
+               old = cur;
+               last_handle = cur->handle;
+       }
+
+       if (groups == NULL)
+               return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start,
+                                       ATT_ECODE_ATTR_NOT_FOUND, pdu, len);
+
+       if (dl == NULL)
+               cur->end = a->handle;
+       else
+               cur->end = last_handle;
+
+       length = g_slist_length(groups);
+
+       adl = att_data_list_alloc(length, last_size + 4);
+
+       for (i = 0, l = groups; l; l = l->next, i++) {
+               uint8_t *value;
+
+               cur = l->data;
+
+               value = (void *) adl->data[i];
+
+               att_put_u16(cur->handle, value);
+               att_put_u16(cur->end, &value[2]);
+               /* Attribute Value */
+               memcpy(&value[4], cur->data, cur->len);
+       }
+
+       length = enc_read_by_grp_resp(adl, pdu, len);
+
+       att_data_list_free(adl);
+       g_slist_free_full(groups, g_free);
+
+       return length;
+}
+
+static uint16_t read_by_type(struct gatt_channel *channel, uint16_t start,
+                                               uint16_t end, bt_uuid_t *uuid,
+                                               uint8_t *pdu, int len)
+{
+       struct att_data_list *adl;
+       GSList *l, *types;
+       GList *dl, *database;
+       struct attribute *a;
+       uint16_t num, length;
+       uint8_t status;
+       int i;
+
+       if (start > end || start == 0x0000)
+               return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start,
+                                       ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+       database = channel->server->database;
+       for (dl = database, length = 0, types = NULL; dl; dl = dl->next) {
+
+               a = dl->data;
+
+               if (a->handle < start)
+                       continue;
+
+               if (a->handle > end)
+                       break;
+
+               if (bt_uuid_cmp(&a->uuid, uuid)  != 0)
+                       continue;
+
+               status = att_check_reqs(channel, ATT_OP_READ_BY_TYPE_REQ,
+                                                               a->read_reqs);
+
+               if (status == 0x00 && a->read_cb)
+                       status = a->read_cb(a, channel->device,
+                                                       a->cb_user_data);
+
+               if (status) {
+                       g_slist_free(types);
+                       return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ,
+                                               a->handle, status, pdu, len);
+               }
+
+               /* All elements must have the same length */
+               if (length == 0)
+                       length = a->len;
+               else if (a->len != length)
+                       break;
+
+               types = g_slist_append(types, a);
+       }
+
+       if (types == NULL)
+               return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start,
+                                       ATT_ECODE_ATTR_NOT_FOUND, pdu, len);
+
+       num = g_slist_length(types);
+
+       /* Handle length plus attribute value length */
+       length += 2;
+
+       adl = att_data_list_alloc(num, length);
+
+       for (i = 0, l = types; l; i++, l = l->next) {
+               uint8_t *value;
+
+               a = l->data;
+
+               value = (void *) adl->data[i];
+
+               att_put_u16(a->handle, value);
+
+               /* Attribute Value */
+               memcpy(&value[2], a->data, a->len);
+       }
+
+       length = enc_read_by_type_resp(adl, pdu, len);
+
+       att_data_list_free(adl);
+       g_slist_free(types);
+
+       return length;
+}
+
+static int find_info(struct gatt_channel *channel, uint16_t start, uint16_t end,
+                                                       uint8_t *pdu, int len)
+{
+       struct attribute *a;
+       struct att_data_list *adl;
+       GSList *l, *info;
+       GList *dl, *database;
+       uint8_t format, last_type = BT_UUID_UNSPEC;
+       uint16_t length, num;
+       int i;
+
+       if (start > end || start == 0x0000)
+               return enc_error_resp(ATT_OP_FIND_INFO_REQ, start,
+                                       ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+       database = channel->server->database;
+       for (dl = database, info = NULL, num = 0; dl; dl = dl->next) {
+               a = dl->data;
+
+               if (a->handle < start)
+                       continue;
+
+               if (a->handle > end)
+                       break;
+
+               if (last_type == BT_UUID_UNSPEC)
+                       last_type = a->uuid.type;
+
+               if (a->uuid.type != last_type)
+                       break;
+
+               info = g_slist_append(info, a);
+               num++;
+
+               last_type = a->uuid.type;
+       }
+
+       if (info == NULL)
+               return enc_error_resp(ATT_OP_FIND_INFO_REQ, start,
+                                       ATT_ECODE_ATTR_NOT_FOUND, pdu, len);
+
+       if (last_type == BT_UUID16) {
+               length = 2;
+               format = 0x01;
+       } else if (last_type == BT_UUID128) {
+               length = 16;
+               format = 0x02;
+       } else {
+               g_slist_free(info);
+               return 0;
+       }
+
+       adl = att_data_list_alloc(num, length + 2);
+
+       for (i = 0, l = info; l; i++, l = l->next) {
+               uint8_t *value;
+
+               a = l->data;
+
+               value = (void *) adl->data[i];
+
+               att_put_u16(a->handle, value);
+
+               /* Attribute Value */
+               att_put_uuid(a->uuid, &value[2]);
+       }
+
+       length = enc_find_info_resp(format, adl, pdu, len);
+
+       att_data_list_free(adl);
+       g_slist_free(info);
+
+       return length;
+}
+
+static int find_by_type(struct gatt_channel *channel, uint16_t start,
+                       uint16_t end, bt_uuid_t *uuid, const uint8_t *value,
+                                       int vlen, uint8_t *opdu, int mtu)
+{
+       struct attribute *a;
+       struct att_range *range;
+       GSList *matches;
+       GList *dl, *database;
+       int len;
+
+       if (start > end || start == 0x0000)
+               return enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start,
+                                       ATT_ECODE_INVALID_HANDLE, opdu, mtu);
+
+       /* Searching first requested handle number */
+       database = channel->server->database;
+       for (dl = database, matches = NULL, range = NULL; dl; dl = dl->next) {
+               a = dl->data;
+
+               if (a->handle < start)
+                       continue;
+
+               if (a->handle > end)
+                       break;
+
+               /* Primary service? Attribute value matches? */
+               if ((bt_uuid_cmp(&a->uuid, uuid) == 0) && (a->len == vlen) &&
+                                       (memcmp(a->data, value, vlen) == 0)) {
+
+                       range = g_new0(struct att_range, 1);
+                       range->start = a->handle;
+                       /* It is allowed to have end group handle the same as
+                        * start handle, for groups with only one attribute. */
+                       range->end = a->handle;
+
+                       matches = g_slist_append(matches, range);
+               } else if (range) {
+                       /* Update the last found handle or reset the pointer
+                        * to track that a new group started: Primary or
+                        * Secondary service. */
+                       if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+                                       bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)
+                               range = NULL;
+                       else
+                               range->end = a->handle;
+               }
+       }
+
+       if (matches == NULL)
+               return enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start,
+                               ATT_ECODE_ATTR_NOT_FOUND, opdu, mtu);
+
+       len = enc_find_by_type_resp(matches, opdu, mtu);
+
+       g_slist_free_full(matches, g_free);
+
+       return len;
+}
+
+static uint16_t read_value(struct gatt_channel *channel, uint16_t handle,
+                                                       uint8_t *pdu, int len)
+{
+       struct attribute *a;
+       uint8_t status;
+       GList *l;
+       uint16_t cccval;
+       uint8_t bdaddr_type;
+       guint h = handle;
+
+       l = g_list_find_custom(channel->server->database,
+                                       GUINT_TO_POINTER(h), handle_cmp);
+       if (!l)
+               return enc_error_resp(ATT_OP_READ_REQ, handle,
+                                       ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+       a = l->data;
+
+       bdaddr_type = device_get_addr_type(channel->device);
+
+       if (bt_uuid_cmp(&ccc_uuid, &a->uuid) == 0 &&
+               read_device_ccc(&channel->src, &channel->dst, bdaddr_type,
+                                                       handle, &cccval) == 0) {
+               uint8_t config[2];
+
+               att_put_u16(cccval, config);
+               return enc_read_resp(config, sizeof(config), pdu, len);
+       }
+
+       status = att_check_reqs(channel, ATT_OP_READ_REQ, a->read_reqs);
+
+       if (status == 0x00 && a->read_cb)
+               status = a->read_cb(a, channel->device, a->cb_user_data);
+
+       if (status)
+               return enc_error_resp(ATT_OP_READ_REQ, handle, status, pdu,
+                                                                       len);
+
+       return enc_read_resp(a->data, a->len, pdu, len);
+}
+
+static uint16_t read_blob(struct gatt_channel *channel, uint16_t handle,
+                                       uint16_t offset, uint8_t *pdu, int len)
+{
+       struct attribute *a;
+       uint8_t status;
+       GList *l;
+       uint16_t cccval;
+       uint8_t bdaddr_type;
+       guint h = handle;
+
+       l = g_list_find_custom(channel->server->database,
+                                       GUINT_TO_POINTER(h), handle_cmp);
+       if (!l)
+               return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
+                                       ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+       a = l->data;
+
+       if (a->len <= offset)
+               return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
+                                       ATT_ECODE_INVALID_OFFSET, pdu, len);
+
+       bdaddr_type = device_get_addr_type(channel->device);
+
+       if (bt_uuid_cmp(&ccc_uuid, &a->uuid) == 0 &&
+               read_device_ccc(&channel->src, &channel->dst, bdaddr_type,
+                                                       handle, &cccval) == 0) {
+               uint8_t config[2];
+
+               att_put_u16(cccval, config);
+               return enc_read_blob_resp(config, sizeof(config), offset,
+                                                               pdu, len);
+       }
+
+       status = att_check_reqs(channel, ATT_OP_READ_BLOB_REQ, a->read_reqs);
+
+       if (status == 0x00 && a->read_cb)
+               status = a->read_cb(a, channel->device, a->cb_user_data);
+
+       if (status)
+               return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle, status,
+                                                               pdu, len);
+
+       return enc_read_blob_resp(a->data, a->len, offset, pdu, len);
+}
+
+static uint16_t write_value(struct gatt_channel *channel, uint16_t handle,
+                                               const uint8_t *value, int vlen,
+                                               uint8_t *pdu, int len)
+{
+       struct attribute *a;
+       uint8_t status;
+       GList *l;
+       guint h = handle;
+
+       l = g_list_find_custom(channel->server->database,
+                                       GUINT_TO_POINTER(h), handle_cmp);
+       if (!l)
+               return enc_error_resp(ATT_OP_WRITE_REQ, handle,
+                               ATT_ECODE_INVALID_HANDLE, pdu, len);
+
+       a = l->data;
+
+       status = att_check_reqs(channel, ATT_OP_WRITE_REQ, a->write_reqs);
+       if (status)
+               return enc_error_resp(ATT_OP_WRITE_REQ, handle, status, pdu,
+                                                                       len);
+
+       if (bt_uuid_cmp(&ccc_uuid, &a->uuid) != 0) {
+
+               attrib_db_update(channel->server->adapter, handle, NULL,
+                                                       value, vlen, NULL);
+
+               if (a->write_cb) {
+                       status = a->write_cb(a, channel->device,
+                                                       a->cb_user_data);
+                       if (status)
+                               return enc_error_resp(ATT_OP_WRITE_REQ, handle,
+                                                       status, pdu, len);
+               }
+       } else {
+               uint16_t cccval = att_get_u16(value);
+               uint8_t bdaddr_type = device_get_addr_type(channel->device);
+
+               write_device_ccc(&channel->src, &channel->dst, bdaddr_type,
+                                                               handle, cccval);
+       }
+
+       return enc_write_resp(pdu, len);
+}
+
+static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu,
+               uint8_t *pdu, int len)
+{
+       GError *gerr = NULL;
+       GIOChannel *io;
+       uint16_t imtu;
+
+       if (mtu < ATT_DEFAULT_LE_MTU)
+               return enc_error_resp(ATT_OP_MTU_REQ, 0,
+                                       ATT_ECODE_REQ_NOT_SUPP, pdu, len);
+
+       io = g_attrib_get_channel(channel->attrib);
+
+       bt_io_get(io, BT_IO_L2CAP, &gerr,
+                       BT_IO_OPT_IMTU, &imtu,
+                       BT_IO_OPT_INVALID);
+
+       if (gerr)
+               return enc_error_resp(ATT_OP_MTU_REQ, 0,
+                                       ATT_ECODE_UNLIKELY, pdu, len);
+
+       channel->mtu = MIN(mtu, imtu);
+       g_attrib_set_mtu(channel->attrib, channel->mtu);
+
+       return enc_mtu_resp(imtu, pdu, len);
+}
+
+static void channel_remove(struct gatt_channel *channel)
+{
+       channel->server->clients = g_slist_remove(channel->server->clients,
+                                                               channel);
+       channel_free(channel);
+}
+
+static gboolean channel_watch_cb(GIOChannel *io, GIOCondition cond,
+                                               gpointer user_data)
+{
+       channel_remove(user_data);
+
+       return FALSE;
+}
+
+static void channel_handler(const uint8_t *ipdu, uint16_t len,
+                                                       gpointer user_data)
+{
+       struct gatt_channel *channel = user_data;
+       uint8_t opdu[ATT_MAX_MTU], value[ATT_MAX_MTU];
+       uint16_t length, start, end, mtu, offset;
+       bt_uuid_t uuid;
+       uint8_t status = 0;
+       int vlen;
+
+       DBG("op 0x%02x", ipdu[0]);
+
+       switch (ipdu[0]) {
+       case ATT_OP_READ_BY_GROUP_REQ:
+               length = dec_read_by_grp_req(ipdu, len, &start, &end, &uuid);
+               if (length == 0) {
+                       status = ATT_ECODE_INVALID_PDU;
+                       goto done;
+               }
+
+               length = read_by_group(channel, start, end, &uuid, opdu,
+                                                               channel->mtu);
+               break;
+       case ATT_OP_READ_BY_TYPE_REQ:
+               length = dec_read_by_type_req(ipdu, len, &start, &end, &uuid);
+               if (length == 0) {
+                       status = ATT_ECODE_INVALID_PDU;
+                       goto done;
+               }
+
+               length = read_by_type(channel, start, end, &uuid, opdu,
+                                                               channel->mtu);
+               break;
+       case ATT_OP_READ_REQ:
+               length = dec_read_req(ipdu, len, &start);
+               if (length == 0) {
+                       status = ATT_ECODE_INVALID_PDU;
+                       goto done;
+               }
+
+               length = read_value(channel, start, opdu, channel->mtu);
+               break;
+       case ATT_OP_READ_BLOB_REQ:
+               length = dec_read_blob_req(ipdu, len, &start, &offset);
+               if (length == 0) {
+                       status = ATT_ECODE_INVALID_PDU;
+                       goto done;
+               }
+
+               length = read_blob(channel, start, offset, opdu, channel->mtu);
+               break;
+       case ATT_OP_MTU_REQ:
+               if (!channel->le) {
+                       status = ATT_ECODE_REQ_NOT_SUPP;
+                       goto done;
+               }
+
+               length = dec_mtu_req(ipdu, len, &mtu);
+               if (length == 0) {
+                       status = ATT_ECODE_INVALID_PDU;
+                       goto done;
+               }
+
+               length = mtu_exchange(channel, mtu, opdu, channel->mtu);
+               break;
+       case ATT_OP_FIND_INFO_REQ:
+               length = dec_find_info_req(ipdu, len, &start, &end);
+               if (length == 0) {
+                       status = ATT_ECODE_INVALID_PDU;
+                       goto done;
+               }
+
+               length = find_info(channel, start, end, opdu, channel->mtu);
+               break;
+       case ATT_OP_WRITE_REQ:
+               length = dec_write_req(ipdu, len, &start, value, &vlen);
+               if (length == 0) {
+                       status = ATT_ECODE_INVALID_PDU;
+                       goto done;
+               }
+
+               length = write_value(channel, start, value, vlen, opdu,
+                                                               channel->mtu);
+               break;
+       case ATT_OP_WRITE_CMD:
+               length = dec_write_cmd(ipdu, len, &start, value, &vlen);
+               if (length > 0)
+                       write_value(channel, start, value, vlen, opdu,
+                                                               channel->mtu);
+               return;
+       case ATT_OP_FIND_BY_TYPE_REQ:
+               length = dec_find_by_type_req(ipdu, len, &start, &end,
+                                                       &uuid, value, &vlen);
+               if (length == 0) {
+                       status = ATT_ECODE_INVALID_PDU;
+                       goto done;
+               }
+
+               length = find_by_type(channel, start, end, &uuid, value, vlen,
+                                                       opdu, channel->mtu);
+               break;
+       case ATT_OP_HANDLE_CNF:
+               return;
+       case ATT_OP_HANDLE_IND:
+       case ATT_OP_HANDLE_NOTIFY:
+               /* The attribute client is already handling these */
+               return;
+       case ATT_OP_READ_MULTI_REQ:
+       case ATT_OP_PREP_WRITE_REQ:
+       case ATT_OP_EXEC_WRITE_REQ:
+       default:
+               DBG("Unsupported request 0x%02x", ipdu[0]);
+               status = ATT_ECODE_REQ_NOT_SUPP;
+               goto done;
+       }
+
+       if (length == 0)
+               status = ATT_ECODE_IO;
+
+done:
+       if (status)
+               length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
+                                                               channel->mtu);
+
+       g_attrib_send(channel->attrib, 0, opdu[0], opdu, length,
+                                                       NULL, NULL, NULL);
+}
+
+guint attrib_channel_attach(GAttrib *attrib)
+{
+       struct gatt_server *server;
+       struct btd_device *device;
+       struct gatt_channel *channel;
+       GIOChannel *io;
+       GError *gerr = NULL;
+       char addr[18];
+       uint16_t cid;
+       guint mtu = 0;
+
+       io = g_attrib_get_channel(attrib);
+
+       channel = g_new0(struct gatt_channel, 1);
+
+       bt_io_get(io, BT_IO_L2CAP, &gerr,
+                       BT_IO_OPT_SOURCE_BDADDR, &channel->src,
+                       BT_IO_OPT_DEST_BDADDR, &channel->dst,
+                       BT_IO_OPT_CID, &cid,
+                       BT_IO_OPT_IMTU, &mtu,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("bt_io_get: %s", gerr->message);
+               g_error_free(gerr);
+               g_free(channel);
+               return 0;
+       }
+
+       server = find_gatt_server(&channel->src);
+       if (server == NULL) {
+               char src[18];
+
+               ba2str(&channel->src, src);
+               error("No GATT server found in %s", src);
+               g_free(channel);
+               return 0;
+       }
+
+       channel->server = server;
+
+       ba2str(&channel->dst, addr);
+
+       device = adapter_find_device(server->adapter, addr);
+       if (device == NULL || device_is_bonded(device) == FALSE)
+               delete_device_ccc(&channel->src, &channel->dst);
+
+       if (cid != ATT_CID) {
+               channel->le = FALSE;
+               channel->mtu = mtu;
+       } else {
+               channel->le = TRUE;
+               channel->mtu = ATT_DEFAULT_LE_MTU;
+       }
+
+       channel->attrib = g_attrib_ref(attrib);
+       channel->id = g_attrib_register(channel->attrib, GATTRIB_ALL_REQS,
+                                       channel_handler, channel, NULL);
+
+       channel->cleanup_id = g_io_add_watch(io, G_IO_HUP, channel_watch_cb,
+                                                               channel);
+
+       channel->device = btd_device_ref(device);
+
+       server->clients = g_slist_append(server->clients, channel);
+
+       return channel->id;
+}
+
+static gint channel_id_cmp(gconstpointer data, gconstpointer user_data)
+{
+       const struct gatt_channel *channel = data;
+       guint id = GPOINTER_TO_UINT(user_data);
+
+       return channel->id - id;
+}
+
+gboolean attrib_channel_detach(GAttrib *attrib, guint id)
+{
+       struct gatt_server *server;
+       struct gatt_channel *channel;
+       GError *gerr = NULL;
+       GIOChannel *io;
+       bdaddr_t src;
+       GSList *l;
+
+       io = g_attrib_get_channel(attrib);
+
+       bt_io_get(io, BT_IO_L2CAP, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src,
+                                                       BT_IO_OPT_INVALID);
+
+       if (gerr != NULL) {
+               error("bt_io_get: %s", gerr->message);
+               g_error_free(gerr);
+               return FALSE;
+       }
+
+       server = find_gatt_server(&src);
+       if (server == NULL)
+               return FALSE;
+
+       l = g_slist_find_custom(server->clients, GUINT_TO_POINTER(id),
+                                                               channel_id_cmp);
+       if (!l)
+               return FALSE;
+
+       channel = l->data;
+
+       g_attrib_unregister(channel->attrib, channel->id);
+       channel_remove(channel);
+
+       return TRUE;
+}
+
+static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
+{
+       GAttrib *attrib;
+
+       if (gerr) {
+               error("%s", gerr->message);
+               return;
+       }
+
+       attrib = g_attrib_new(io);
+       attrib_channel_attach(attrib);
+       g_attrib_unref(attrib);
+}
+
+static void confirm_event(GIOChannel *io, void *user_data)
+{
+       GError *gerr = NULL;
+
+       if (bt_io_accept(io, connect_event, user_data, NULL, &gerr) == FALSE) {
+               error("bt_io_accept: %s", gerr->message);
+               g_error_free(gerr);
+               g_io_channel_unref(io);
+       }
+
+       return;
+}
+
+static gboolean register_core_services(struct gatt_server *server)
+{
+       uint8_t atval[256];
+       bt_uuid_t uuid;
+       uint16_t appearance = 0x0000;
+
+       /* GAP service: primary service definition */
+       bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+       att_put_u16(GENERIC_ACCESS_PROFILE_ID, &atval[0]);
+       attrib_db_add_new(server, 0x0001, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 2);
+
+       /* GAP service: device name characteristic */
+       server->name_handle = 0x0006;
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ;
+       att_put_u16(server->name_handle, &atval[1]);
+       att_put_u16(GATT_CHARAC_DEVICE_NAME, &atval[3]);
+       attrib_db_add_new(server, 0x0004, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 5);
+
+       /* GAP service: device name attribute */
+       bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+       attrib_db_add_new(server, server->name_handle, &uuid, ATT_NONE,
+                                               ATT_NOT_PERMITTED, NULL, 0);
+
+       /* GAP service: device appearance characteristic */
+       server->appearance_handle = 0x0008;
+       bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+       atval[0] = ATT_CHAR_PROPER_READ;
+       att_put_u16(server->appearance_handle, &atval[1]);
+       att_put_u16(GATT_CHARAC_APPEARANCE, &atval[3]);
+       attrib_db_add_new(server, 0x0007, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 5);
+
+       /* GAP service: device appearance attribute */
+       bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+       att_put_u16(appearance, &atval[0]);
+       attrib_db_add_new(server, server->appearance_handle, &uuid, ATT_NONE,
+                                               ATT_NOT_PERMITTED, atval, 2);
+       server->gap_sdp_handle = attrib_create_sdp_new(server, 0x0001,
+                                               "Generic Access Profile");
+       if (server->gap_sdp_handle == 0) {
+               error("Failed to register GAP service record");
+               return FALSE;
+       }
+
+       /* GATT service: primary service definition */
+       bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+       att_put_u16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]);
+       attrib_db_add_new(server, 0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+                                                               atval, 2);
+
+       server->gatt_sdp_handle = attrib_create_sdp_new(server, 0x0010,
+                                               "Generic Attribute Profile");
+       if (server->gatt_sdp_handle == 0) {
+               error("Failed to register GATT service record");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+int btd_adapter_gatt_server_start(struct btd_adapter *adapter)
+{
+       struct gatt_server *server;
+       GError *gerr = NULL;
+       bdaddr_t addr;
+
+       DBG("Start GATT server in hci%d", adapter_get_dev_id(adapter));
+
+       server = g_new0(struct gatt_server, 1);
+       server->adapter = btd_adapter_ref(adapter);
+
+       adapter_get_address(server->adapter, &addr);
+
+       /* BR/EDR socket */
+       server->l2cap_io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event,
+                                       NULL, NULL, &gerr,
+                                       BT_IO_OPT_SOURCE_BDADDR, &addr,
+                                       BT_IO_OPT_PSM, ATT_PSM,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+                                       BT_IO_OPT_INVALID);
+
+       if (server->l2cap_io == NULL) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               gatt_server_free(server);
+               return -1;
+       }
+
+       if (!register_core_services(server)) {
+               gatt_server_free(server);
+               return -1;
+       }
+
+       /* LE socket */
+       server->le_io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event,
+                                       &server->le_io, NULL, &gerr,
+                                       BT_IO_OPT_SOURCE_BDADDR, &addr,
+                                       BT_IO_OPT_CID, ATT_CID,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+                                       BT_IO_OPT_INVALID);
+
+       if (server->le_io == NULL) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               /* Doesn't have LE support, continue */
+       }
+
+       servers = g_slist_prepend(servers, server);
+       return 0;
+}
+
+void btd_adapter_gatt_server_stop(struct btd_adapter *adapter)
+{
+       struct gatt_server *server;
+       GSList *l;
+
+       l = g_slist_find_custom(servers, adapter, adapter_cmp);
+       if (l == NULL)
+               return;
+
+       DBG("Stop GATT server in hci%d", adapter_get_dev_id(adapter));
+
+       server = l->data;
+       servers = g_slist_remove(servers, server);
+       gatt_server_free(server);
+}
+
+uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle,
+                                                       const char *name)
+{
+       GSList *l;
+
+       l = g_slist_find_custom(servers, adapter, adapter_cmp);
+       if (l == NULL)
+               return 0;
+
+       return attrib_create_sdp_new(l->data, handle, name);
+}
+
+void attrib_free_sdp(uint32_t sdp_handle)
+{
+       remove_record_from_server(sdp_handle);
+}
+
+static uint16_t find_uuid16_avail(struct btd_adapter *adapter, uint16_t nitems)
+{
+       struct gatt_server *server;
+       uint16_t handle;
+       GSList *l;
+       GList *dl;
+
+       l = g_slist_find_custom(servers, adapter, adapter_cmp);
+       if (l == NULL)
+               return 0;
+
+       server = l->data;
+       if (server->database == NULL)
+               return 0x0001;
+
+       for (dl = server->database, handle = 0x0001; dl; dl = dl->next) {
+               struct attribute *a = dl->data;
+
+               if ((bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+                               bt_uuid_cmp(&a->uuid, &snd_uuid) == 0) &&
+                               a->handle - handle >= nitems)
+                       /* Note: the range above excludes the current handle */
+                       return handle;
+
+               if (a->len == 16 && (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+                               bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)) {
+                       /* 128 bit UUID service definition */
+                       return 0;
+               }
+
+               if (a->handle == 0xffff)
+                       return 0;
+
+               handle = a->handle + 1;
+       }
+
+       if (0xffff - handle + 1 >= nitems)
+               return handle;
+
+       return 0;
+}
+
+static uint16_t find_uuid128_avail(struct btd_adapter *adapter, uint16_t nitems)
+{
+       uint16_t handle = 0, end = 0xffff;
+       struct gatt_server *server;
+       GList *dl;
+       GSList *l;
+
+       l = g_slist_find_custom(servers, adapter, adapter_cmp);
+       if (l == NULL)
+               return 0;
+
+       server = l->data;
+       if (server->database == NULL)
+               return 0xffff - nitems + 1;
+
+       for (dl = g_list_last(server->database); dl; dl = dl->prev) {
+               struct attribute *a = dl->data;
+
+               if (handle == 0)
+                       handle = a->handle;
+
+               if (bt_uuid_cmp(&a->uuid, &prim_uuid) != 0 &&
+                               bt_uuid_cmp(&a->uuid, &snd_uuid) != 0)
+                       continue;
+
+               if (end - handle >= nitems)
+                       return end - nitems + 1;
+
+               if (a->len == 2) {
+                       /* 16 bit UUID service definition */
+                       return 0;
+               }
+
+               if (a->handle == 0x0001)
+                       return 0;
+
+               end = a->handle - 1;
+               handle = 0;
+       }
+
+       if (end - 0x0001 >= nitems)
+               return end - nitems + 1;
+
+       return 0;
+}
+
+uint16_t attrib_db_find_avail(struct btd_adapter *adapter, bt_uuid_t *svc_uuid,
+                                                               uint16_t nitems)
+{
+       g_assert(nitems > 0);
+
+       if (svc_uuid->type == BT_UUID16)
+               return find_uuid16_avail(adapter, nitems);
+       else if (svc_uuid->type == BT_UUID128)
+               return find_uuid128_avail(adapter, nitems);
+       else {
+               char uuidstr[MAX_LEN_UUID_STR];
+
+               bt_uuid_to_string(svc_uuid, uuidstr, MAX_LEN_UUID_STR);
+               error("Service uuid: %s is neither a 16-bit nor a 128-bit uuid",
+                                                               uuidstr);
+               return 0;
+       }
+}
+
+struct attribute *attrib_db_add(struct btd_adapter *adapter, uint16_t handle,
+                               bt_uuid_t *uuid, int read_reqs, int write_reqs,
+                                               const uint8_t *value, int len)
+{
+       GSList *l;
+
+       l = g_slist_find_custom(servers, adapter, adapter_cmp);
+       if (l == NULL)
+               return NULL;
+
+       return attrib_db_add_new(l->data, handle, uuid, read_reqs, write_reqs,
+                                                               value, len);
+}
+
+int attrib_db_update(struct btd_adapter *adapter, uint16_t handle,
+                                       bt_uuid_t *uuid, const uint8_t *value,
+                                       int len, struct attribute **attr)
+{
+       struct gatt_server *server;
+       struct attribute *a;
+       GSList *l;
+       GList *dl;
+       guint h = handle;
+
+       l = g_slist_find_custom(servers, adapter, adapter_cmp);
+       if (l == NULL)
+               return -ENOENT;
+
+       server = l->data;
+
+       DBG("handle=0x%04x", handle);
+
+       dl = g_list_find_custom(server->database, GUINT_TO_POINTER(h),
+                                                               handle_cmp);
+       if (dl == NULL)
+               return -ENOENT;
+
+       a = dl->data;
+
+       a->data = g_try_realloc(a->data, len);
+       if (len && a->data == NULL)
+               return -ENOMEM;
+
+       a->len = len;
+       memcpy(a->data, value, len);
+
+       if (uuid != NULL)
+               a->uuid = *uuid;
+
+       if (attr)
+               *attr = a;
+
+       return 0;
+}
+
+int attrib_db_del(struct btd_adapter *adapter, uint16_t handle)
+{
+       struct gatt_server *server;
+       struct attribute *a;
+       GSList *l;
+       GList *dl;
+       guint h = handle;
+
+       l = g_slist_find_custom(servers, adapter, adapter_cmp);
+       if (l == NULL)
+               return -ENOENT;
+
+       server = l->data;
+
+       DBG("handle=0x%04x", handle);
+
+       dl = g_list_find_custom(server->database, GUINT_TO_POINTER(h),
+                                                               handle_cmp);
+       if (dl == NULL)
+               return -ENOENT;
+
+       a = dl->data;
+       server->database = g_list_remove(server->database, a);
+       g_free(a->data);
+       g_free(a);
+
+       return 0;
+}
+
+int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid,
+                                               const uint8_t *value, int len)
+{
+       struct gatt_server *server;
+       uint16_t handle;
+       GSList *l;
+
+       l = g_slist_find_custom(servers, adapter, adapter_cmp);
+       if (l == NULL)
+               return -ENOENT;
+
+       server = l->data;
+
+       /* FIXME: Missing Privacy and Reconnection Address */
+
+       switch (uuid) {
+       case GATT_CHARAC_DEVICE_NAME:
+               handle = server->name_handle;
+               break;
+       case GATT_CHARAC_APPEARANCE:
+               handle = server->appearance_handle;
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return attrib_db_update(adapter, handle, NULL, value, len, NULL);
+}
diff --git a/src/attrib-server.h b/src/attrib-server.h
new file mode 100644 (file)
index 0000000..7af0cfa
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010  Nokia Corporation
+ *  Copyright (C) 2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+uint16_t attrib_db_find_avail(struct btd_adapter *adapter, bt_uuid_t *svc_uuid,
+                                                       uint16_t nitems);
+struct attribute *attrib_db_add(struct btd_adapter *adapter, uint16_t handle,
+                               bt_uuid_t *uuid, int read_reqs, int write_reqs,
+                               const uint8_t *value, int len);
+int attrib_db_update(struct btd_adapter *adapter, uint16_t handle,
+                                       bt_uuid_t *uuid, const uint8_t *value,
+                                       int len, struct attribute **attr);
+int attrib_db_del(struct btd_adapter *adapter, uint16_t handle);
+int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid,
+                                               const uint8_t *value, int len);
+uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle,
+                                                       const char *name);
+void attrib_free_sdp(uint32_t sdp_handle);
+guint attrib_channel_attach(GAttrib *attrib);
+gboolean attrib_channel_detach(GAttrib *attrib, guint id);
diff --git a/src/bluetooth.conf b/src/bluetooth.conf
new file mode 100644 (file)
index 0000000..664dbd9
--- /dev/null
@@ -0,0 +1,35 @@
+<!-- This configuration file specifies the required security policies
+     for Bluetooth core daemon to work. -->
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+  <!-- ../system.conf have denied everything, so we just punch some holes -->
+
+  <policy user="root">
+    <allow own="org.bluez"/>
+    <allow send_destination="org.bluez"/>
+    <allow send_interface="org.bluez.Agent"/>
+    <allow send_interface="org.bluez.HandsfreeAgent"/>
+    <allow send_interface="org.bluez.MediaEndpoint"/>
+    <allow send_interface="org.bluez.MediaPlayer"/>
+    <allow send_interface="org.bluez.Watcher"/>
+    <allow send_interface="org.bluez.ThermometerWatcher"/>
+  </policy>
+
+  <policy at_console="true">
+    <allow send_destination="org.bluez"/>
+  </policy>
+
+  <!-- allow users of lp group (printing subsystem) to 
+       communicate with bluetoothd -->
+  <policy group="lp">
+    <allow send_destination="org.bluez"/>
+  </policy>
+
+  <policy context="default">
+    <deny send_destination="org.bluez"/>
+  </policy>
+
+</busconfig>
diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in
new file mode 100644 (file)
index 0000000..2a576a3
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Bluetooth service
+
+[Service]
+Type=dbus
+BusName=org.bluez
+ExecStart=@prefix@/sbin/bluetoothd -n
+
+[Install]
+WantedBy=bluetooth.target
+Alias=dbus-org.bluez.service
diff --git a/src/bluetooth.ver b/src/bluetooth.ver
new file mode 100644 (file)
index 0000000..b71c70d
--- /dev/null
@@ -0,0 +1,10 @@
+{
+       global:
+               btd_*;
+               g_dbus_*;
+               info;
+               error;
+               debug;
+       local:
+               *;
+};
diff --git a/src/bluetoothd.8.in b/src/bluetoothd.8.in
new file mode 100644 (file)
index 0000000..a7ae77b
--- /dev/null
@@ -0,0 +1,91 @@
+.\"
+.TH "BLUETOOTHD" "8" "March 2004" "Bluetooth daemon" "System management commands"
+.SH "NAME"
+bluetoothd \- Bluetooth daemon
+
+.SH "SYNOPSIS"
+.B bluetoothd
+[
+.B \-n
+]
+
+.SH "DESCRIPTION"
+This manual page documents briefly the
+.B bluetoothd
+daemon, which manages all the Bluetooth devices.
+.B bluetoothd
+itself does not accept many command\-line options, as most of its
+configuration is done in the
+.B @CONFIGDIR@/main.conf
+file, which has its own man page.
+.B bluetoothd
+can also provide a number of services via the D-Bus message bus
+system.
+.SH "OPTIONS"
+.TP
+.BI \-n
+Don't run as daemon in background.
+.TP
+.BI \-d
+Enable debug information output.
+.TP
+.BI \-m\ mtu\-size
+Use specific MTU size for SDP server.
+
+.SH "FILES"
+.TP
+.I @CONFIGDIR@/main.conf
+Default location of the global configuration file.
+
+.TP
+.I @STORAGEDIR@/nn:nn:nn:nn:nn:nn/linkkeys
+Default location for link keys of paired devices. The directory
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP
+is the address of the local device. The file is line separated, with
+the following columns separated by whitespace:
+
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP Remote device address.
+
+\fInnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn\fP Link key.
+
+\fIn\fP Link type integer.
+
+.TP
+.I @STORAGEDIR@/nn:nn:nn:nn:nn:nn/names
+Default location for the device name cache. The directory
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP
+is the address of the local device. The file is line separated, with
+the following columns separated by whitespace:
+
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP Remote device address.
+
+\fIname\fP Remote device name, terminated with newline.
+
+.TP
+.I @STORAGEDIR@/nn:nn:nn:nn:nn:nn/features
+Default location for the features cache. The directory
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP
+is the address of the local device. The file is line separated, with
+the following columns separated by whitespace:
+
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP Remote device address.
+
+\fInnnnnnnnnnnnnnnn\fP Remote device LMP features coded as an 8 byte bitfield.
+
+.TP
+.I @STORAGEDIR@/nn:nn:nn:nn:nn:nn/manufacturers
+Default location for the manufacturers cache. The directory
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP
+is the address of the local device. The file is line separated, with
+the following columns separated by whitespace:
+
+\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP\fB:\fP\fInn\fP Remote device address.
+
+\fIn\fP Remote device manufacturer integer.
+
+\fIn\fP Remote device LMP version integer.
+
+\fIn\fP Remote device LMP sub-version integer.
+
+.SH "AUTHOR"
+This manual page was written by Marcel Holtmann, Philipp Matthias Hahn and Fredrik Noring.
diff --git a/src/dbus-common.c b/src/dbus-common.c
new file mode 100644 (file)
index 0000000..7e1bc94
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2005-2007  Johan Hedberg <johan.hedberg@nokia.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "dbus-common.h"
+
+static DBusConnection *connection = NULL;
+
+static void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+       DBusMessageIter value;
+       char sig[2] = { type, '\0' };
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
+
+       dbus_message_iter_append_basic(&value, type, val);
+
+       dbus_message_iter_close_container(iter, &value);
+}
+
+static void append_array_variant(DBusMessageIter *iter, int type, void *val,
+                                                       int n_elements)
+{
+       DBusMessageIter variant, array;
+       char type_sig[2] = { type, '\0' };
+       char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' };
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                               array_sig, &variant);
+
+       dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
+                                               type_sig, &array);
+
+       if (dbus_type_is_fixed(type) == TRUE) {
+               dbus_message_iter_append_fixed_array(&array, type, val,
+                                                       n_elements);
+       } else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+               const char ***str_array = val;
+               int i;
+
+               for (i = 0; i < n_elements; i++)
+                       dbus_message_iter_append_basic(&array, type,
+                                                       &((*str_array)[i]));
+       }
+
+       dbus_message_iter_close_container(&variant, &array);
+
+       dbus_message_iter_close_container(iter, &variant);
+}
+
+void dict_append_entry(DBusMessageIter *dict,
+                       const char *key, int type, void *val)
+{
+       DBusMessageIter entry;
+
+       if (type == DBUS_TYPE_STRING) {
+               const char *str = *((const char **) val);
+               if (str == NULL)
+                       return;
+       }
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       append_variant(&entry, type, val);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+void dict_append_array(DBusMessageIter *dict, const char *key, int type,
+                       void *val, int n_elements)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                               NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       append_array_variant(&entry, type, val, n_elements);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+dbus_bool_t emit_property_changed(DBusConnection *conn,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *name,
+                                       int type, void *value)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+
+       if (!signal) {
+               error("Unable to allocate new %s.PropertyChanged signal",
+                               interface);
+               return FALSE;
+       }
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+       append_variant(&iter, type, value);
+
+       return g_dbus_send_message(conn, signal);
+}
+
+dbus_bool_t emit_array_property_changed(DBusConnection *conn,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *name,
+                                       int type, void *value, int num)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+
+       if (!signal) {
+               error("Unable to allocate new %s.PropertyChanged signal",
+                               interface);
+               return FALSE;
+       }
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+       append_array_variant(&iter, type, value, num);
+
+       return g_dbus_send_message(conn, signal);
+}
+
+void set_dbus_connection(DBusConnection *conn)
+{
+       connection = conn;
+}
+
+DBusConnection *get_dbus_connection(void)
+{
+       return connection;
+}
+
+const char *class_to_icon(uint32_t class)
+{
+       switch ((class & 0x1f00) >> 8) {
+       case 0x01:
+               return "computer";
+       case 0x02:
+               switch ((class & 0xfc) >> 2) {
+               case 0x01:
+               case 0x02:
+               case 0x03:
+               case 0x05:
+                       return "phone";
+               case 0x04:
+                       return "modem";
+               }
+               break;
+       case 0x03:
+               return "network-wireless";
+       case 0x04:
+               switch ((class & 0xfc) >> 2) {
+               case 0x01:
+               case 0x02:
+                       return "audio-card";    /* Headset */
+               case 0x06:
+                       return "audio-card";    /* Headphone */
+               case 0x0b: /* VCR */
+               case 0x0c: /* Video Camera */
+               case 0x0d: /* Camcorder */
+                       return "camera-video";
+               default:
+                       return "audio-card";    /* Other audio device */
+               }
+               break;
+       case 0x05:
+               switch ((class & 0xc0) >> 6) {
+               case 0x00:
+                       switch ((class & 0x1e) >> 2) {
+                       case 0x01:
+                       case 0x02:
+                               return "input-gaming";
+                       }
+                       break;
+               case 0x01:
+                       return "input-keyboard";
+               case 0x02:
+                       switch ((class & 0x1e) >> 2) {
+                       case 0x05:
+                               return "input-tablet";
+                       default:
+                               return "input-mouse";
+                       }
+               }
+               break;
+       case 0x06:
+               if (class & 0x80)
+                       return "printer";
+               if (class & 0x20)
+                       return "camera-photo";
+               break;
+       }
+
+       return NULL;
+}
+
+const char *gap_appearance_to_icon(uint16_t appearance)
+{
+       switch ((appearance & 0xffc0) >> 6) {
+       case 0x01:
+               return "phone";
+       case 0x02:
+               return "computer";
+       case 0x05:
+               return "video-display";
+       case 0x0a:
+               return "multimedia-player";
+       case 0x0b:
+               return "scanner";
+       case 0x0f: /* HID Generic */
+               switch (appearance & 0x3f) {
+               case 0x01:
+                       return "input-keyboard";
+               case 0x02:
+                       return "input-mouse";
+               case 0x03:
+               case 0x04:
+                       return "input-gaming";
+               case 0x05:
+                       return "input-tablet";
+               case 0x08:
+                       return "scanner";
+               }
+               break;
+       }
+
+       return NULL;
+}
diff --git a/src/dbus-common.h b/src/dbus-common.h
new file mode 100644 (file)
index 0000000..b9531f2
--- /dev/null
@@ -0,0 +1,48 @@
+/* *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define MAX_PATH_LENGTH 64
+
+void dict_append_entry(DBusMessageIter *dict,
+                       const char *key, int type, void *val);
+
+void dict_append_array(DBusMessageIter *dict, const char *key, int type,
+                       void *val, int n_elements);
+
+dbus_bool_t emit_property_changed(DBusConnection *conn,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *name,
+                                       int type, void *value);
+
+dbus_bool_t emit_array_property_changed(DBusConnection *conn,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *name,
+                                       int type, void *value, int num);
+
+void set_dbus_connection(DBusConnection *conn);
+DBusConnection *get_dbus_connection(void);
+
+const char *class_to_icon(uint32_t class);
+const char *gap_appearance_to_icon(uint16_t appearance);
diff --git a/src/device.c b/src/device.c
new file mode 100644 (file)
index 0000000..c210bcb
--- /dev/null
@@ -0,0 +1,3164 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "att.h"
+#include "hcid.h"
+#include "adapter.h"
+#include "gattrib.h"
+#include "attio.h"
+#include "device.h"
+#include "dbus-common.h"
+#include "error.h"
+#include "glib-helper.h"
+#include "sdp-client.h"
+#include "gatt.h"
+#include "agent.h"
+#include "sdp-xml.h"
+#include "storage.h"
+#include "btio.h"
+#include "attrib-server.h"
+#include "attrib/client.h"
+
+#define DISCONNECT_TIMER       2
+#define DISCOVERY_TIMER                2
+
+#define AUTO_CONNECTION_INTERVAL       5 /* Next connection attempt */
+
+/* When all services should trust a remote device */
+#define GLOBAL_TRUST "[all]"
+
+#define APPEARANCE_CHR_UUID 0x2a01
+
+struct btd_disconnect_data {
+       guint id;
+       disconnect_watch watch;
+       void *user_data;
+       GDestroyNotify destroy;
+};
+
+struct bonding_req {
+       DBusConnection *conn;
+       DBusMessage *msg;
+       guint listener_id;
+       struct btd_device *device;
+};
+
+struct authentication_req {
+       auth_type_t type;
+       void *cb;
+       struct agent *agent;
+       struct btd_device *device;
+       uint32_t passkey;
+       char *pincode;
+       gboolean secure;
+};
+
+struct browse_req {
+       DBusConnection *conn;
+       DBusMessage *msg;
+       struct btd_device *device;
+       GSList *match_uuids;
+       GSList *profiles_added;
+       GSList *profiles_removed;
+       sdp_list_t *records;
+       int search_uuid;
+       int reconnect_attempt;
+       guint listener_id;
+};
+
+struct attio_data {
+       guint id;
+       attio_connect_cb cfunc;
+       attio_disconnect_cb dcfunc;
+       gpointer user_data;
+};
+
+typedef void (*attio_error_cb) (const GError *gerr, gpointer user_data);
+typedef void (*attio_success_cb) (gpointer user_data);
+
+struct att_callbacks {
+       attio_error_cb error;           /* Callback for error */
+       attio_success_cb success;       /* Callback for success */
+       gpointer user_data;
+};
+
+struct btd_device {
+       bdaddr_t        bdaddr;
+       uint8_t         bdaddr_type;
+       gchar           *path;
+       char            name[MAX_NAME_LENGTH + 1];
+       char            *alias;
+       uint16_t        vendor_src;
+       uint16_t        vendor;
+       uint16_t        product;
+       uint16_t        version;
+       struct btd_adapter      *adapter;
+       GSList          *uuids;
+       GSList          *services;              /* Primary services path */
+       GSList          *primaries;             /* List of primary services */
+       GSList          *drivers;               /* List of device drivers */
+       GSList          *watches;               /* List of disconnect_data */
+       gboolean        temporary;
+       struct agent    *agent;
+       guint           disconn_timer;
+       guint           discov_timer;
+       struct browse_req *browse;              /* service discover request */
+       struct bonding_req *bonding;
+       struct authentication_req *authr;       /* authentication request */
+       GSList          *disconnects;           /* disconnects message */
+       GAttrib         *attrib;
+       GSList          *attios;
+       GSList          *attios_offline;
+       guint           attachid;               /* Attrib server attach */
+       guint           auto_id;                /* Auto connect source id */
+
+       gboolean        connected;
+
+       sdp_list_t      *tmp_records;
+
+       gboolean        trusted;
+       gboolean        paired;
+       gboolean        blocked;
+       gboolean        bonded;
+       gboolean        auto_connect;
+
+       gboolean        authorizing;
+       gint            ref;
+
+       GIOChannel      *att_io;
+       guint           cleanup_id;
+};
+
+static uint16_t uuid_list[] = {
+       L2CAP_UUID,
+       PNP_INFO_SVCLASS_ID,
+       PUBLIC_BROWSE_GROUP,
+       0
+};
+
+static GSList *device_drivers = NULL;
+
+static void browse_request_free(struct browse_req *req)
+{
+       if (req->listener_id)
+               g_dbus_remove_watch(req->conn, req->listener_id);
+       if (req->msg)
+               dbus_message_unref(req->msg);
+       if (req->conn)
+               dbus_connection_unref(req->conn);
+       if (req->device)
+               btd_device_unref(req->device);
+       g_slist_free_full(req->profiles_added, g_free);
+       g_slist_free(req->profiles_removed);
+       if (req->records)
+               sdp_list_free(req->records, (sdp_free_func_t) sdp_record_free);
+
+       g_free(req);
+}
+
+static void att_cleanup(struct btd_device *device)
+{
+       if (device->attachid) {
+               attrib_channel_detach(device->attrib, device->attachid);
+               device->attachid = 0;
+       }
+
+       if (device->cleanup_id) {
+               g_source_remove(device->cleanup_id);
+               device->cleanup_id = 0;
+       }
+
+       if (device->att_io) {
+               g_io_channel_shutdown(device->att_io, FALSE, NULL);
+               g_io_channel_unref(device->att_io);
+               device->att_io = NULL;
+       }
+
+       if (device->attrib) {
+               g_attrib_unref(device->attrib);
+               device->attrib = NULL;
+       }
+}
+
+static void browse_request_cancel(struct browse_req *req)
+{
+       struct btd_device *device = req->device;
+       struct btd_adapter *adapter = device->adapter;
+       bdaddr_t src;
+
+       if (device_is_creating(device, NULL))
+               device_set_temporary(device, TRUE);
+
+       adapter_get_address(adapter, &src);
+
+       bt_cancel_discovery(&src, &device->bdaddr);
+
+       att_cleanup(device);
+
+       device->browse = NULL;
+       browse_request_free(req);
+}
+
+static void device_free(gpointer user_data)
+{
+       struct btd_device *device = user_data;
+       struct btd_adapter *adapter = device->adapter;
+       struct agent *agent = adapter_get_agent(adapter);
+
+       if (device->agent)
+               agent_free(device->agent);
+
+       if (agent && (agent_is_busy(agent, device) ||
+                               agent_is_busy(agent, device->authr)))
+               agent_cancel(agent);
+
+       g_slist_free_full(device->services, g_free);
+       g_slist_free_full(device->uuids, g_free);
+       g_slist_free_full(device->primaries, g_free);
+       g_slist_free_full(device->attios, g_free);
+       g_slist_free_full(device->attios_offline, g_free);
+
+       att_cleanup(device);
+
+       if (device->tmp_records)
+               sdp_list_free(device->tmp_records,
+                                       (sdp_free_func_t) sdp_record_free);
+
+       if (device->disconn_timer)
+               g_source_remove(device->disconn_timer);
+
+       if (device->discov_timer)
+               g_source_remove(device->discov_timer);
+
+       if (device->auto_id)
+               g_source_remove(device->auto_id);
+
+       DBG("%p", device);
+
+       if (device->authr)
+               g_free(device->authr->pincode);
+       g_free(device->authr);
+       g_free(device->path);
+       g_free(device->alias);
+       g_free(device);
+}
+
+gboolean device_is_bredr(struct btd_device *device)
+{
+       return (device->bdaddr_type == BDADDR_BREDR);
+}
+
+gboolean device_is_le(struct btd_device *device)
+{
+       return (device->bdaddr_type != BDADDR_BREDR);
+}
+
+gboolean device_is_paired(struct btd_device *device)
+{
+       return device->paired;
+}
+
+gboolean device_is_bonded(struct btd_device *device)
+{
+       return device->bonded;
+}
+
+gboolean device_is_trusted(struct btd_device *device)
+{
+       return device->trusted;
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                               DBusMessage *msg, void *user_data)
+{
+       struct btd_device *device = user_data;
+       struct btd_adapter *adapter = device->adapter;
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       bdaddr_t src;
+       char name[MAX_NAME_LENGTH + 1], srcaddr[18], dstaddr[18];
+       char **str;
+       const char *ptr, *icon = NULL;
+       dbus_bool_t boolean;
+       uint32_t class;
+       uint16_t app;
+       int i;
+       GSList *l;
+
+       ba2str(&device->bdaddr, dstaddr);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       /* Address */
+       ptr = dstaddr;
+       dict_append_entry(&dict, "Address", DBUS_TYPE_STRING, &ptr);
+
+       /* Name */
+       ptr = NULL;
+       memset(name, 0, sizeof(name));
+       adapter_get_address(adapter, &src);
+       ba2str(&src, srcaddr);
+
+       ptr = device->name;
+       dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &ptr);
+
+       /* Alias (fallback to name or address) */
+       if (device->alias != NULL)
+               ptr = device->alias;
+       else if (strlen(ptr) == 0) {
+               g_strdelimit(dstaddr, ":", '-');
+               ptr = dstaddr;
+       }
+
+       dict_append_entry(&dict, "Alias", DBUS_TYPE_STRING, &ptr);
+
+       /* Class */
+       if (read_remote_class(&src, &device->bdaddr, &class) == 0) {
+               icon = class_to_icon(class);
+
+               dict_append_entry(&dict, "Class", DBUS_TYPE_UINT32, &class);
+       } else if (read_remote_appearance(&src, &device->bdaddr,
+                                               device->bdaddr_type, &app) == 0)
+               /* Appearance */
+               icon = gap_appearance_to_icon(app);
+
+       dict_append_entry(&dict, "Icon", DBUS_TYPE_STRING, &icon);
+
+       /* Vendor */
+       if (device->vendor)
+               dict_append_entry(&dict, "Vendor", DBUS_TYPE_UINT16,
+                                                       &device->vendor);
+
+       /* Vendor Source*/
+       if (device->vendor_src)
+               dict_append_entry(&dict, "VendorSource", DBUS_TYPE_UINT16,
+                                                       &device->vendor_src);
+
+       /* Product */
+       if (device->product)
+               dict_append_entry(&dict, "Product", DBUS_TYPE_UINT16,
+                                                       &device->product);
+
+       /* Version */
+       if (device->version)
+               dict_append_entry(&dict, "Version", DBUS_TYPE_UINT16,
+                                                       &device->version);
+
+       /* Paired */
+       boolean = device_is_paired(device);
+       dict_append_entry(&dict, "Paired", DBUS_TYPE_BOOLEAN, &boolean);
+
+       /* Trusted */
+       boolean = device_is_trusted(device);
+       dict_append_entry(&dict, "Trusted", DBUS_TYPE_BOOLEAN, &boolean);
+
+       /* Blocked */
+       boolean = device->blocked;
+       dict_append_entry(&dict, "Blocked", DBUS_TYPE_BOOLEAN, &boolean);
+
+       /* Connected */
+       dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN,
+                                                       &device->connected);
+
+       /* UUIDs */
+       str = g_new0(char *, g_slist_length(device->uuids) + 1);
+       for (i = 0, l = device->uuids; l; l = l->next, i++)
+               str[i] = l->data;
+       dict_append_array(&dict, "UUIDs", DBUS_TYPE_STRING, &str, i);
+       g_free(str);
+
+       /* Services */
+       str = g_new0(char *, g_slist_length(device->services) + 1);
+       for (i = 0, l = device->services; l; l = l->next, i++)
+               str[i] = l->data;
+       dict_append_array(&dict, "Services", DBUS_TYPE_OBJECT_PATH, &str, i);
+       g_free(str);
+
+       /* Adapter */
+       ptr = adapter_get_path(adapter);
+       dict_append_entry(&dict, "Adapter", DBUS_TYPE_OBJECT_PATH, &ptr);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static DBusMessage *set_alias(DBusConnection *conn, DBusMessage *msg,
+                                       const char *alias, void *data)
+{
+       struct btd_device *device = data;
+       struct btd_adapter *adapter = device->adapter;
+       char srcaddr[18], dstaddr[18];
+       bdaddr_t src;
+       int err;
+
+       /* No change */
+       if ((device->alias == NULL && g_str_equal(alias, "")) ||
+                       g_strcmp0(device->alias, alias) == 0)
+               return dbus_message_new_method_return(msg);
+
+       adapter_get_address(adapter, &src);
+       ba2str(&src, srcaddr);
+       ba2str(&device->bdaddr, dstaddr);
+
+       /* Remove alias if empty string */
+       err = write_device_alias(srcaddr, dstaddr,
+                       g_str_equal(alias, "") ? NULL : alias);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       g_free(device->alias);
+       device->alias = g_str_equal(alias, "") ? NULL : g_strdup(alias);
+
+       emit_property_changed(conn, dbus_message_get_path(msg),
+                               DEVICE_INTERFACE, "Alias",
+                               DBUS_TYPE_STRING, &alias);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_trust(DBusConnection *conn, DBusMessage *msg,
+                                       gboolean value, void *data)
+{
+       struct btd_device *device = data;
+       struct btd_adapter *adapter = device->adapter;
+       char srcaddr[18], dstaddr[18];
+       bdaddr_t src;
+       int err;
+
+       if (device->trusted == value)
+               return dbus_message_new_method_return(msg);
+
+       adapter_get_address(adapter, &src);
+       ba2str(&src, srcaddr);
+       ba2str(&device->bdaddr, dstaddr);
+
+       err = write_trust(srcaddr, dstaddr, GLOBAL_TRUST, value);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       device->trusted = value;
+
+       emit_property_changed(conn, dbus_message_get_path(msg),
+                               DEVICE_INTERFACE, "Trusted",
+                               DBUS_TYPE_BOOLEAN, &value);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static void driver_remove(struct btd_device_driver *driver,
+                                               struct btd_device *device)
+{
+       driver->remove(device);
+
+       device->drivers = g_slist_remove(device->drivers, driver);
+}
+
+static gboolean do_disconnect(gpointer user_data)
+{
+       struct btd_device *device = user_data;
+
+       device->disconn_timer = 0;
+
+       btd_adapter_disconnect_device(device->adapter, &device->bdaddr,
+                                                       device->bdaddr_type);
+
+       return FALSE;
+}
+
+int device_block(DBusConnection *conn, struct btd_device *device,
+                                               gboolean update_only)
+{
+       int err = 0;
+       bdaddr_t src;
+
+       if (device->blocked)
+               return 0;
+
+       if (device->connected)
+               do_disconnect(device);
+
+       g_slist_foreach(device->drivers, (GFunc) driver_remove, device);
+
+       if (!update_only)
+               err = btd_adapter_block_address(device->adapter,
+                                       &device->bdaddr, device->bdaddr_type);
+
+       if (err < 0)
+               return err;
+
+       device->blocked = TRUE;
+
+       adapter_get_address(device->adapter, &src);
+
+       err = write_blocked(&src, &device->bdaddr, TRUE);
+       if (err < 0)
+               error("write_blocked(): %s (%d)", strerror(-err), -err);
+
+       device_set_temporary(device, FALSE);
+
+       emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Blocked",
+                                       DBUS_TYPE_BOOLEAN, &device->blocked);
+
+       return 0;
+}
+
+int device_unblock(DBusConnection *conn, struct btd_device *device,
+                               gboolean silent, gboolean update_only)
+{
+       int err = 0;
+       bdaddr_t src;
+
+       if (!device->blocked)
+               return 0;
+
+       if (!update_only)
+               err = btd_adapter_unblock_address(device->adapter,
+                                       &device->bdaddr, device->bdaddr_type);
+
+       if (err < 0)
+               return err;
+
+       device->blocked = FALSE;
+
+       adapter_get_address(device->adapter, &src);
+
+       err = write_blocked(&src, &device->bdaddr, FALSE);
+       if (err < 0)
+               error("write_blocked(): %s (%d)", strerror(-err), -err);
+
+       if (!silent) {
+               emit_property_changed(conn, device->path,
+                                       DEVICE_INTERFACE, "Blocked",
+                                       DBUS_TYPE_BOOLEAN, &device->blocked);
+               device_probe_drivers(device, device->uuids);
+       }
+
+       return 0;
+}
+
+static DBusMessage *set_blocked(DBusConnection *conn, DBusMessage *msg,
+                                               gboolean value, void *data)
+{
+       struct btd_device *device = data;
+       int err;
+
+       if (value)
+               err = device_block(conn, device, FALSE);
+       else
+               err = device_unblock(conn, device, FALSE, FALSE);
+
+       switch (-err) {
+       case 0:
+               return dbus_message_new_method_return(msg);
+       case EINVAL:
+               return btd_error_failed(msg, "Kernel lacks blacklist support");
+       default:
+               return btd_error_failed(msg, strerror(-err));
+       }
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+                               DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter;
+       DBusMessageIter sub;
+       const char *property;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return btd_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return btd_error_invalid_args(msg);
+       dbus_message_iter_recurse(&iter, &sub);
+
+       if (g_str_equal("Trusted", property)) {
+               dbus_bool_t value;
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+                       return btd_error_invalid_args(msg);
+               dbus_message_iter_get_basic(&sub, &value);
+
+               return set_trust(conn, msg, value, data);
+       } else if (g_str_equal("Alias", property)) {
+               const char *alias;
+
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+                       return btd_error_invalid_args(msg);
+               dbus_message_iter_get_basic(&sub, &alias);
+
+               return set_alias(conn, msg, alias, data);
+       } else if (g_str_equal("Blocked", property)) {
+               dbus_bool_t value;
+
+               if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+                       return btd_error_invalid_args(msg);
+
+               dbus_message_iter_get_basic(&sub, &value);
+
+               return set_blocked(conn, msg, value, data);
+       }
+
+       return btd_error_invalid_args(msg);
+}
+
+static void discover_services_req_exit(DBusConnection *conn, void *user_data)
+{
+       struct browse_req *req = user_data;
+
+       DBG("DiscoverServices requestor exited");
+
+       browse_request_cancel(req);
+}
+
+static DBusMessage *discover_services(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct btd_device *device = user_data;
+       const char *pattern;
+       int err;
+
+       if (device->browse)
+               return btd_error_in_progress(msg);
+
+       if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               return btd_error_invalid_args(msg);
+
+       if (strlen(pattern) == 0) {
+               err = device_browse_sdp(device, conn, msg, NULL, FALSE);
+               if (err < 0)
+                       goto fail;
+       } else {
+               uuid_t uuid;
+
+               if (bt_string2uuid(&uuid, pattern) < 0)
+                       return btd_error_invalid_args(msg);
+
+               sdp_uuid128_to_uuid(&uuid);
+
+               err = device_browse_sdp(device, conn, msg, &uuid, FALSE);
+               if (err < 0)
+                       goto fail;
+       }
+
+       return NULL;
+
+fail:
+       return btd_error_failed(msg, strerror(-err));
+}
+
+static const char *browse_request_get_requestor(struct browse_req *req)
+{
+       if (!req->msg)
+               return NULL;
+
+       return dbus_message_get_sender(req->msg);
+}
+
+static void iter_append_record(DBusMessageIter *dict, uint32_t handle,
+                                                       const char *record)
+{
+       DBusMessageIter entry;
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_UINT32, &handle);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &record);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static void discover_services_reply(struct browse_req *req, int err,
+                                                       sdp_list_t *recs)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, dict;
+       sdp_list_t *seq;
+
+       if (err) {
+               const char *err_if;
+
+               if (err == -EHOSTDOWN)
+                       err_if = ERROR_INTERFACE ".ConnectionAttemptFailed";
+               else
+                       err_if = ERROR_INTERFACE ".Failed";
+
+               reply = dbus_message_new_error(req->msg, err_if,
+                                                       strerror(-err));
+               g_dbus_send_message(req->conn, reply);
+               return;
+       }
+
+       reply = dbus_message_new_method_return(req->msg);
+       if (!reply)
+               return;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_UINT32_AS_STRING DBUS_TYPE_STRING_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       for (seq = recs; seq; seq = seq->next) {
+               sdp_record_t *rec = (sdp_record_t *) seq->data;
+               GString *result;
+
+               if (!rec)
+                       break;
+
+               result = g_string_new(NULL);
+
+               convert_sdp_record_to_xml(rec, result,
+                               (void *) g_string_append);
+
+               if (result->len)
+                       iter_append_record(&dict, rec->handle, result->str);
+
+               g_string_free(result, TRUE);
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       g_dbus_send_message(req->conn, reply);
+}
+
+static DBusMessage *cancel_discover(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct btd_device *device = user_data;
+       const char *sender = dbus_message_get_sender(msg);
+       const char *requestor;
+
+       if (!device->browse)
+               return btd_error_does_not_exist(msg);
+
+       if (!dbus_message_is_method_call(device->browse->msg, DEVICE_INTERFACE,
+                                       "DiscoverServices"))
+               return btd_error_not_authorized(msg);
+
+       requestor = browse_request_get_requestor(device->browse);
+
+       /* only the discover requestor can cancel the inquiry process */
+       if (!requestor || !g_str_equal(requestor, sender))
+               return btd_error_not_authorized(msg);
+
+       discover_services_reply(device->browse, -ECANCELED, NULL);
+
+       browse_request_cancel(device->browse);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static void bonding_request_cancel(struct bonding_req *bonding)
+{
+       struct btd_device *device = bonding->device;
+       struct btd_adapter *adapter = device->adapter;
+
+       adapter_cancel_bonding(adapter, &device->bdaddr);
+}
+
+void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
+{
+       DBusConnection *conn = get_dbus_connection();
+
+       if (device->bonding)
+               bonding_request_cancel(device->bonding);
+
+       if (device->browse) {
+               discover_services_reply(device->browse, -ECANCELED, NULL);
+               browse_request_cancel(device->browse);
+       }
+
+       if (msg)
+               device->disconnects = g_slist_append(device->disconnects,
+                                               dbus_message_ref(msg));
+
+       if (device->disconn_timer)
+               return;
+
+       while (device->watches) {
+               struct btd_disconnect_data *data = device->watches->data;
+
+               if (data->watch)
+                       /* temporary is set if device is going to be removed */
+                       data->watch(device, device->temporary,
+                                                       data->user_data);
+
+               /* Check if the watch has been removed by callback function */
+               if (!g_slist_find(device->watches, data))
+                       continue;
+
+               device->watches = g_slist_remove(device->watches, data);
+               g_free(data);
+       }
+
+       device->disconn_timer = g_timeout_add_seconds(DISCONNECT_TIMER,
+                                               do_disconnect, device);
+
+       g_dbus_emit_signal(conn, device->path,
+                       DEVICE_INTERFACE, "DisconnectRequested",
+                       DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
+                                                       void *user_data)
+{
+       struct btd_device *device = user_data;
+
+       if (!device->connected)
+               return btd_error_not_connected(msg);
+
+       device_request_disconnect(device, msg);
+
+       return NULL;
+}
+
+static const GDBusMethodTable device_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                               NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                               get_properties) },
+       { GDBUS_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+                       set_property) },
+       { GDBUS_ASYNC_METHOD("DiscoverServices",
+                       GDBUS_ARGS({ "pattern", "s" }),
+                       GDBUS_ARGS({ "services", "a{us}" }),
+                       discover_services) },
+       { GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) },
+       { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, disconnect) },
+       { }
+};
+
+static const GDBusSignalTable device_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { GDBUS_SIGNAL("DisconnectRequested", NULL) },
+       { }
+};
+
+gboolean device_is_connected(struct btd_device *device)
+{
+       return device->connected;
+}
+
+void device_add_connection(struct btd_device *device, DBusConnection *conn)
+{
+       if (device->connected) {
+               char addr[18];
+               ba2str(&device->bdaddr, addr);
+               error("Device %s is already connected", addr);
+               return;
+       }
+
+       device->connected = TRUE;
+
+       emit_property_changed(conn, device->path,
+                                       DEVICE_INTERFACE, "Connected",
+                                       DBUS_TYPE_BOOLEAN, &device->connected);
+}
+
+void device_remove_connection(struct btd_device *device, DBusConnection *conn)
+{
+       if (!device->connected) {
+               char addr[18];
+               ba2str(&device->bdaddr, addr);
+               error("Device %s isn't connected", addr);
+               return;
+       }
+
+       device->connected = FALSE;
+
+       if (device->disconn_timer > 0) {
+               g_source_remove(device->disconn_timer);
+               device->disconn_timer = 0;
+       }
+
+       while (device->disconnects) {
+               DBusMessage *msg = device->disconnects->data;
+
+               g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
+               device->disconnects = g_slist_remove(device->disconnects, msg);
+       }
+
+       if (device_is_paired(device) && !device_is_bonded(device))
+               device_set_paired(device, FALSE);
+
+       emit_property_changed(conn, device->path,
+                                       DEVICE_INTERFACE, "Connected",
+                                       DBUS_TYPE_BOOLEAN, &device->connected);
+}
+
+guint device_add_disconnect_watch(struct btd_device *device,
+                               disconnect_watch watch, void *user_data,
+                               GDestroyNotify destroy)
+{
+       struct btd_disconnect_data *data;
+       static guint id = 0;
+
+       data = g_new0(struct btd_disconnect_data, 1);
+       data->id = ++id;
+       data->watch = watch;
+       data->user_data = user_data;
+       data->destroy = destroy;
+
+       device->watches = g_slist_append(device->watches, data);
+
+       return data->id;
+}
+
+void device_remove_disconnect_watch(struct btd_device *device, guint id)
+{
+       GSList *l;
+
+       for (l = device->watches; l; l = l->next) {
+               struct btd_disconnect_data *data = l->data;
+
+               if (data->id == id) {
+                       device->watches = g_slist_remove(device->watches,
+                                                       data);
+                       if (data->destroy)
+                               data->destroy(data->user_data);
+                       g_free(data);
+                       return;
+               }
+       }
+}
+
+static void device_set_vendor(struct btd_device *device, uint16_t value)
+{
+       DBusConnection *conn = get_dbus_connection();
+
+       if (device->vendor == value)
+               return;
+
+       device->vendor = value;
+
+       emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Vendor",
+                               DBUS_TYPE_UINT16, &value);
+}
+
+static void device_set_vendor_src(struct btd_device *device, uint16_t value)
+{
+       DBusConnection *conn = get_dbus_connection();
+
+       if (device->vendor_src == value)
+               return;
+
+       device->vendor_src = value;
+
+       emit_property_changed(conn, device->path, DEVICE_INTERFACE,
+                               "VendorSource", DBUS_TYPE_UINT16, &value);
+}
+
+static void device_set_product(struct btd_device *device, uint16_t value)
+{
+       DBusConnection *conn = get_dbus_connection();
+
+       if (device->product == value)
+               return;
+
+       device->product = value;
+
+       emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Product",
+                               DBUS_TYPE_UINT16, &value);
+}
+
+static void device_set_version(struct btd_device *device, uint16_t value)
+{
+       DBusConnection *conn = get_dbus_connection();
+
+       if (device->version == value)
+               return;
+
+       device->version = value;
+
+       emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Version",
+                               DBUS_TYPE_UINT16, &value);
+}
+
+struct btd_device *device_create(DBusConnection *conn,
+                               struct btd_adapter *adapter,
+                               const gchar *address, uint8_t bdaddr_type)
+{
+       gchar *address_up;
+       struct btd_device *device;
+       const gchar *adapter_path = adapter_get_path(adapter);
+       bdaddr_t src;
+       char srcaddr[18], alias[MAX_NAME_LENGTH + 1];
+       uint16_t vendor, product, version;
+
+       device = g_try_malloc0(sizeof(struct btd_device));
+       if (device == NULL)
+               return NULL;
+
+       address_up = g_ascii_strup(address, -1);
+       device->path = g_strdup_printf("%s/dev_%s", adapter_path, address_up);
+       g_strdelimit(device->path, ":", '_');
+       g_free(address_up);
+
+       DBG("Creating device %s", device->path);
+
+       if (g_dbus_register_interface(conn, device->path, DEVICE_INTERFACE,
+                               device_methods, device_signals, NULL,
+                               device, device_free) == FALSE) {
+               device_free(device);
+               return NULL;
+       }
+
+       str2ba(address, &device->bdaddr);
+       device->adapter = adapter;
+       device->bdaddr_type = bdaddr_type;
+       adapter_get_address(adapter, &src);
+       ba2str(&src, srcaddr);
+       read_device_name(srcaddr, address, device->name);
+       if (read_device_alias(srcaddr, address, alias, sizeof(alias)) == 0)
+               device->alias = g_strdup(alias);
+       device->trusted = read_trust(&src, address, GLOBAL_TRUST);
+
+       if (read_blocked(&src, &device->bdaddr))
+               device_block(conn, device, FALSE);
+
+       if (read_link_key(&src, &device->bdaddr, NULL, NULL) == 0) {
+               device_set_paired(device, TRUE);
+               device_set_bonded(device, TRUE);
+       }
+
+       if (device_is_le(device) && has_longtermkeys(&src, &device->bdaddr,
+                                                       device->bdaddr_type)) {
+               device_set_paired(device, TRUE);
+               device_set_bonded(device, TRUE);
+       }
+
+       if (read_device_id(srcaddr, address, NULL, &vendor, &product, &version)
+                                                                       == 0) {
+               device_set_vendor(device, vendor);
+               device_set_product(device, product);
+               device_set_version(device, version);
+       }
+
+       return btd_device_ref(device);
+}
+
+void device_set_name(struct btd_device *device, const char *name)
+{
+       DBusConnection *conn = get_dbus_connection();
+
+       if (strncmp(name, device->name, MAX_NAME_LENGTH) == 0)
+               return;
+
+       strncpy(device->name, name, MAX_NAME_LENGTH);
+
+       emit_property_changed(conn, device->path,
+                               DEVICE_INTERFACE, "Name",
+                               DBUS_TYPE_STRING, &name);
+
+       if (device->alias != NULL)
+               return;
+
+       emit_property_changed(conn, device->path,
+                               DEVICE_INTERFACE, "Alias",
+                               DBUS_TYPE_STRING, &name);
+}
+
+void device_get_name(struct btd_device *device, char *name, size_t len)
+{
+       strncpy(name, device->name, len);
+}
+
+uint16_t btd_device_get_vendor(struct btd_device *device)
+{
+       return device->vendor;
+}
+
+uint16_t btd_device_get_vendor_src(struct btd_device *device)
+{
+       return device->vendor_src;
+}
+
+uint16_t btd_device_get_product(struct btd_device *device)
+{
+       return device->product;
+}
+
+uint16_t btd_device_get_version(struct btd_device *device)
+{
+       return device->version;
+}
+
+static void device_remove_stored(struct btd_device *device)
+{
+       bdaddr_t src;
+       char key[20];
+       DBusConnection *conn = get_dbus_connection();
+
+       adapter_get_address(device->adapter, &src);
+       ba2str(&device->bdaddr, key);
+
+       /* key: address only */
+       delete_entry(&src, "profiles", key);
+       delete_entry(&src, "trusts", key);
+
+       if (device_is_bonded(device)) {
+               delete_entry(&src, "linkkeys", key);
+               delete_entry(&src, "aliases", key);
+
+               /* key: address#type */
+               sprintf(&key[17], "#%hhu", device->bdaddr_type);
+
+               delete_entry(&src, "longtermkeys", key);
+
+               device_set_bonded(device, FALSE);
+               device->paired = FALSE;
+               btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
+                                                       device->bdaddr_type);
+       }
+
+       delete_all_records(&src, &device->bdaddr);
+       delete_device_service(&src, &device->bdaddr, device->bdaddr_type);
+
+       if (device->blocked)
+               device_unblock(conn, device, TRUE, FALSE);
+}
+
+void device_remove(struct btd_device *device, gboolean remove_stored)
+{
+
+       DBG("Removing device %s", device->path);
+
+       if (device->agent)
+               agent_free(device->agent);
+
+       if (device->bonding) {
+               uint8_t status;
+
+               if (device->connected)
+                       status = HCI_OE_USER_ENDED_CONNECTION;
+               else
+                       status = HCI_PAGE_TIMEOUT;
+
+               device_cancel_bonding(device, status);
+       }
+
+       if (device->browse) {
+               discover_services_reply(device->browse, -ECANCELED, NULL);
+               browse_request_cancel(device->browse);
+       }
+
+       if (device->connected)
+               do_disconnect(device);
+
+       if (remove_stored)
+               device_remove_stored(device);
+
+       g_slist_foreach(device->drivers, (GFunc) driver_remove, device);
+       g_slist_free(device->drivers);
+       device->drivers = NULL;
+
+       attrib_client_unregister(device->services);
+
+       btd_device_unref(device);
+}
+
+gint device_address_cmp(struct btd_device *device, const gchar *address)
+{
+       char addr[18];
+
+       ba2str(&device->bdaddr, addr);
+       return strcasecmp(addr, address);
+}
+
+static gboolean record_has_uuid(const sdp_record_t *rec,
+                               const char *profile_uuid)
+{
+       sdp_list_t *pat;
+
+       for (pat = rec->pattern; pat != NULL; pat = pat->next) {
+               char *uuid;
+               int ret;
+
+               uuid = bt_uuid2string(pat->data);
+               if (!uuid)
+                       continue;
+
+               ret = strcasecmp(uuid, profile_uuid);
+
+               g_free(uuid);
+
+               if (ret == 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+static GSList *device_match_pattern(struct btd_device *device,
+                                       const char *match_uuid,
+                                       GSList *profiles)
+{
+       GSList *l, *uuids = NULL;
+
+       for (l = profiles; l; l = l->next) {
+               char *profile_uuid = l->data;
+               const sdp_record_t *rec;
+
+               rec = btd_device_get_record(device, profile_uuid);
+               if (!rec)
+                       continue;
+
+               if (record_has_uuid(rec, match_uuid))
+                       uuids = g_slist_append(uuids, profile_uuid);
+       }
+
+       return uuids;
+}
+
+static GSList *device_match_driver(struct btd_device *device,
+                                       struct btd_device_driver *driver,
+                                       GSList *profiles)
+{
+       const char **uuid;
+       GSList *uuids = NULL;
+
+       for (uuid = driver->uuids; *uuid; uuid++) {
+               GSList *match;
+
+               /* skip duplicated uuids */
+               if (g_slist_find_custom(uuids, *uuid,
+                               (GCompareFunc) strcasecmp))
+                       continue;
+
+               /* match profile driver */
+               match = g_slist_find_custom(profiles, *uuid,
+                                       (GCompareFunc) strcasecmp);
+               if (match) {
+                       uuids = g_slist_append(uuids, match->data);
+                       continue;
+               }
+
+               /* match pattern driver */
+               match = device_match_pattern(device, *uuid, profiles);
+               uuids = g_slist_concat(uuids, match);
+       }
+
+       return uuids;
+}
+
+void device_probe_drivers(struct btd_device *device, GSList *profiles)
+{
+       GSList *list;
+       char addr[18];
+       int err;
+
+       ba2str(&device->bdaddr, addr);
+
+       if (device->blocked) {
+               DBG("Skipping drivers for blocked device %s", addr);
+               goto add_uuids;
+       }
+
+       DBG("Probing drivers for %s", addr);
+
+       for (list = device_drivers; list; list = list->next) {
+               struct btd_device_driver *driver = list->data;
+               GSList *probe_uuids;
+
+               probe_uuids = device_match_driver(device, driver, profiles);
+
+               if (!probe_uuids)
+                       continue;
+
+               err = driver->probe(device, probe_uuids);
+               if (err < 0) {
+                       error("%s driver probe failed for device %s",
+                                                       driver->name, addr);
+                       g_slist_free(probe_uuids);
+                       continue;
+               }
+
+               device->drivers = g_slist_append(device->drivers, driver);
+               g_slist_free(probe_uuids);
+       }
+
+add_uuids:
+       for (list = profiles; list; list = list->next) {
+               GSList *l = g_slist_find_custom(device->uuids, list->data,
+                                               (GCompareFunc) strcasecmp);
+               if (l)
+                       continue;
+
+               device->uuids = g_slist_insert_sorted(device->uuids,
+                                               g_strdup(list->data),
+                                               (GCompareFunc) strcasecmp);
+       }
+}
+
+static void device_remove_drivers(struct btd_device *device, GSList *uuids)
+{
+       struct btd_adapter *adapter = device_get_adapter(device);
+       GSList *list, *next;
+       char srcaddr[18], dstaddr[18];
+       bdaddr_t src;
+       sdp_list_t *records;
+
+       adapter_get_address(adapter, &src);
+       ba2str(&src, srcaddr);
+       ba2str(&device->bdaddr, dstaddr);
+
+       records = read_records(&src, &device->bdaddr);
+
+       DBG("Removing drivers for %s", dstaddr);
+
+       for (list = device->drivers; list; list = next) {
+               struct btd_device_driver *driver = list->data;
+               const char **uuid;
+
+               next = list->next;
+
+               for (uuid = driver->uuids; *uuid; uuid++) {
+                       if (!g_slist_find_custom(uuids, *uuid,
+                                               (GCompareFunc) strcasecmp))
+                               continue;
+
+                       DBG("UUID %s was removed from device %s",
+                                                       *uuid, dstaddr);
+
+                       driver->remove(device);
+                       device->drivers = g_slist_remove(device->drivers,
+                                                               driver);
+                       break;
+               }
+       }
+
+       for (list = uuids; list; list = list->next) {
+               sdp_record_t *rec;
+
+               device->uuids = g_slist_remove(device->uuids, list->data);
+
+               rec = find_record_in_list(records, list->data);
+               if (!rec)
+                       continue;
+
+               delete_record(srcaddr, dstaddr, rec->handle);
+
+               records = sdp_list_remove(records, rec);
+               sdp_record_free(rec);
+
+       }
+
+       if (records)
+               sdp_list_free(records, (sdp_free_func_t) sdp_record_free);
+}
+
+static void services_changed(struct btd_device *device)
+{
+       DBusConnection *conn = get_dbus_connection();
+       char **uuids;
+       GSList *l;
+       int i;
+
+       uuids = g_new0(char *, g_slist_length(device->uuids) + 1);
+       for (i = 0, l = device->uuids; l; l = l->next, i++)
+               uuids[i] = l->data;
+
+       emit_array_property_changed(conn, device->path, DEVICE_INTERFACE,
+                                       "UUIDs", DBUS_TYPE_STRING, &uuids, i);
+
+       g_free(uuids);
+}
+
+static int rec_cmp(const void *a, const void *b)
+{
+       const sdp_record_t *r1 = a;
+       const sdp_record_t *r2 = b;
+
+       return r1->handle - r2->handle;
+}
+
+static void update_services(struct browse_req *req, sdp_list_t *recs)
+{
+       struct btd_device *device = req->device;
+       struct btd_adapter *adapter = device_get_adapter(device);
+       sdp_list_t *seq;
+       char srcaddr[18], dstaddr[18];
+       bdaddr_t src;
+
+       adapter_get_address(adapter, &src);
+       ba2str(&src, srcaddr);
+       ba2str(&device->bdaddr, dstaddr);
+
+       for (seq = recs; seq; seq = seq->next) {
+               sdp_record_t *rec = (sdp_record_t *) seq->data;
+               sdp_list_t *svcclass = NULL;
+               gchar *profile_uuid;
+               GSList *l;
+
+               if (!rec)
+                       break;
+
+               if (sdp_get_service_classes(rec, &svcclass) < 0)
+                       continue;
+
+               /* Check for empty service classes list */
+               if (svcclass == NULL) {
+                       DBG("Skipping record with no service classes");
+                       continue;
+               }
+
+               /* Extract the first element and skip the remainning */
+               profile_uuid = bt_uuid2string(svcclass->data);
+               if (!profile_uuid) {
+                       sdp_list_free(svcclass, free);
+                       continue;
+               }
+
+               if (!strcasecmp(profile_uuid, PNP_UUID)) {
+                       uint16_t source, vendor, product, version;
+                       sdp_data_t *pdlist;
+
+                       pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID_SOURCE);
+                       source = pdlist ? pdlist->val.uint16 : 0x0000;
+
+                       pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
+                       vendor = pdlist ? pdlist->val.uint16 : 0x0000;
+
+                       device_set_vendor(device, vendor);
+
+                       pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
+                       product = pdlist ? pdlist->val.uint16 : 0x0000;
+
+                       device_set_product(device, product);
+
+                       pdlist = sdp_data_get(rec, SDP_ATTR_VERSION);
+                       version = pdlist ? pdlist->val.uint16 : 0x0000;
+
+                       device_set_version(device, version);
+
+                       if (source || vendor || product || version)
+                               store_device_id(srcaddr, dstaddr, source,
+                                               vendor, product, version);
+               }
+
+               /* Check for duplicates */
+               if (sdp_list_find(req->records, rec, rec_cmp)) {
+                       g_free(profile_uuid);
+                       sdp_list_free(svcclass, free);
+                       continue;
+               }
+
+               store_record(srcaddr, dstaddr, rec);
+
+               /* Copy record */
+               req->records = sdp_list_append(req->records,
+                                                       sdp_copy_record(rec));
+
+               l = g_slist_find_custom(device->uuids, profile_uuid,
+                                                       (GCompareFunc) strcmp);
+               if (!l)
+                       req->profiles_added =
+                                       g_slist_append(req->profiles_added,
+                                                       profile_uuid);
+               else {
+                       req->profiles_removed =
+                                       g_slist_remove(req->profiles_removed,
+                                                       l->data);
+                       g_free(profile_uuid);
+               }
+
+               sdp_list_free(svcclass, free);
+       }
+}
+
+static void store_profiles(struct btd_device *device)
+{
+       struct btd_adapter *adapter = device->adapter;
+       bdaddr_t src;
+       char *str;
+
+       adapter_get_address(adapter, &src);
+
+       if (!device->uuids) {
+               write_device_profiles(&src, &device->bdaddr, "");
+               return;
+       }
+
+       str = bt_list2string(device->uuids);
+       write_device_profiles(&src, &device->bdaddr, str);
+       g_free(str);
+}
+
+static void create_device_reply(struct btd_device *device, struct browse_req *req)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(req->msg);
+       if (!reply)
+               return;
+
+       dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &device->path,
+                                       DBUS_TYPE_INVALID);
+
+       g_dbus_send_message(req->conn, reply);
+}
+
+GSList *device_services_from_record(struct btd_device *device, GSList *profiles)
+{
+       GSList *l, *prim_list = NULL;
+       char *att_uuid;
+       uuid_t proto_uuid;
+
+       sdp_uuid16_create(&proto_uuid, ATT_UUID);
+       att_uuid = bt_uuid2string(&proto_uuid);
+
+       for (l = profiles; l; l = l->next) {
+               const char *profile_uuid = l->data;
+               const sdp_record_t *rec;
+               struct gatt_primary *prim;
+               uint16_t start = 0, end = 0, psm = 0;
+               uuid_t prim_uuid;
+
+               rec = btd_device_get_record(device, profile_uuid);
+               if (!rec)
+                       continue;
+
+               if (!record_has_uuid(rec, att_uuid))
+                       continue;
+
+               if (!gatt_parse_record(rec, &prim_uuid, &psm, &start, &end))
+                       continue;
+
+               prim = g_new0(struct gatt_primary, 1);
+               prim->range.start = start;
+               prim->range.end = end;
+               sdp_uuid2strn(&prim_uuid, prim->uuid, sizeof(prim->uuid));
+
+               prim_list = g_slist_append(prim_list, prim);
+       }
+
+       g_free(att_uuid);
+
+       return prim_list;
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+       struct browse_req *req = user_data;
+       struct btd_device *device = req->device;
+       char addr[18];
+
+       ba2str(&device->bdaddr, addr);
+
+       if (err < 0) {
+               error("%s: error updating services: %s (%d)",
+                               addr, strerror(-err), -err);
+               goto send_reply;
+       }
+
+       update_services(req, recs);
+
+       if (device->tmp_records)
+               sdp_list_free(device->tmp_records,
+                                       (sdp_free_func_t) sdp_record_free);
+
+       device->tmp_records = req->records;
+       req->records = NULL;
+
+       if (!req->profiles_added && !req->profiles_removed) {
+               DBG("%s: No service update", addr);
+               goto send_reply;
+       }
+
+       /* Probe matching drivers for services added */
+       if (req->profiles_added) {
+               GSList *list;
+
+               list = device_services_from_record(device, req->profiles_added);
+               if (list)
+                       device_register_services(req->conn, device, list,
+                                                               ATT_PSM);
+
+               device_probe_drivers(device, req->profiles_added);
+       }
+
+       /* Remove drivers for services removed */
+       if (req->profiles_removed)
+               device_remove_drivers(device, req->profiles_removed);
+
+       /* Propagate services changes */
+       services_changed(req->device);
+
+send_reply:
+       if (!req->msg)
+               goto cleanup;
+
+       if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
+                                       "DiscoverServices"))
+               discover_services_reply(req, err, device->tmp_records);
+       else if (dbus_message_is_method_call(req->msg, ADAPTER_INTERFACE,
+                                               "CreatePairedDevice"))
+               create_device_reply(device, req);
+       else if (dbus_message_is_method_call(req->msg, ADAPTER_INTERFACE,
+                                               "CreateDevice")) {
+               if (err < 0) {
+                       DBusMessage *reply;
+                       reply = btd_error_failed(req->msg, strerror(-err));
+                       g_dbus_send_message(req->conn, reply);
+                       goto cleanup;
+               }
+
+               create_device_reply(device, req);
+               device_set_temporary(device, FALSE);
+       }
+
+cleanup:
+       if (!device->temporary) {
+               bdaddr_t sba, dba;
+
+               adapter_get_address(device->adapter, &sba);
+               device_get_address(device, &dba, NULL);
+
+               store_profiles(device);
+       }
+
+       device->browse = NULL;
+       browse_request_free(req);
+}
+
+static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
+{
+       struct browse_req *req = user_data;
+       struct btd_device *device = req->device;
+       struct btd_adapter *adapter = device->adapter;
+       bdaddr_t src;
+       uuid_t uuid;
+
+       /* If we have a valid response and req->search_uuid == 2, then L2CAP
+        * UUID & PNP searching was successful -- we are done */
+       if (err < 0 || (req->search_uuid == 2 && req->records)) {
+               if (err == -ECONNRESET && req->reconnect_attempt < 1) {
+                       req->search_uuid--;
+                       req->reconnect_attempt++;
+               } else
+                       goto done;
+       }
+
+       update_services(req, recs);
+
+       adapter_get_address(adapter, &src);
+
+       /* Search for mandatory uuids */
+       if (uuid_list[req->search_uuid]) {
+               sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+               bt_search_service(&src, &device->bdaddr, &uuid,
+                                               browse_cb, user_data, NULL);
+               return;
+       }
+
+done:
+       search_cb(recs, err, user_data);
+}
+
+static void init_browse(struct browse_req *req, gboolean reverse)
+{
+       GSList *l;
+
+       /* If we are doing reverse-SDP don't try to detect removed profiles
+        * since some devices hide their service records while they are
+        * connected
+        */
+       if (reverse)
+               return;
+
+       for (l = req->device->uuids; l; l = l->next)
+               req->profiles_removed = g_slist_append(req->profiles_removed,
+                                               l->data);
+}
+
+static char *primary_list_to_string(GSList *primary_list)
+{
+       GString *services;
+       GSList *l;
+
+       services = g_string_new(NULL);
+
+       for (l = primary_list; l; l = l->next) {
+               struct gatt_primary *primary = l->data;
+               char service[64];
+
+               memset(service, 0, sizeof(service));
+
+               snprintf(service, sizeof(service), "%04X#%04X#%s ",
+                               primary->range.start, primary->range.end, primary->uuid);
+
+               services = g_string_append(services, service);
+       }
+
+       return g_string_free(services, FALSE);
+}
+
+static void store_services(struct btd_device *device)
+{
+       struct btd_adapter *adapter = device->adapter;
+       bdaddr_t dba, sba;
+       char *str = primary_list_to_string(device->primaries);
+
+       adapter_get_address(adapter, &sba);
+       device_get_address(device, &dba, NULL);
+
+       write_device_services(&sba, &dba, device->bdaddr_type, str);
+
+       g_free(str);
+}
+
+static void attio_connected(gpointer data, gpointer user_data)
+{
+       struct attio_data *attio = data;
+       GAttrib *attrib = user_data;
+
+       if (attio->cfunc)
+               attio->cfunc(attrib, attio->user_data);
+}
+
+static void attio_disconnected(gpointer data, gpointer user_data)
+{
+       struct attio_data *attio = data;
+
+       if (attio->dcfunc)
+               attio->dcfunc(attio->user_data);
+}
+
+static void att_connect_dispatched(gpointer user_data)
+{
+       struct btd_device *device = user_data;
+
+       device->auto_id = 0;
+}
+
+static gboolean att_connect(gpointer user_data);
+
+static gboolean attrib_disconnected_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct btd_device *device = user_data;
+       int sock, err = 0;
+       socklen_t len;
+
+       if (device->browse)
+               goto done;
+
+       sock = g_io_channel_unix_get_fd(io);
+       len = sizeof(err);
+       getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len);
+
+       g_slist_foreach(device->attios, attio_disconnected, NULL);
+
+       if (device->auto_connect == FALSE || err != ETIMEDOUT)
+               goto done;
+
+       device->auto_id = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT_IDLE,
+                                               AUTO_CONNECTION_INTERVAL,
+                                               att_connect, device,
+                                               att_connect_dispatched);
+
+done:
+       att_cleanup(device);
+
+       return FALSE;
+}
+
+static void appearance_cb(guint8 status, const guint8 *pdu, guint16 plen,
+                                                       gpointer user_data)
+{
+       struct btd_device *device = user_data;
+       struct btd_adapter *adapter = device->adapter;
+       struct att_data_list *list =  NULL;
+       uint16_t app;
+       bdaddr_t src;
+       uint8_t *atval;
+
+       if (status != 0) {
+               DBG("Read characteristics by UUID failed: %s\n",
+                                                       att_ecode2str(status));
+               goto done;
+       }
+
+       list = dec_read_by_type_resp(pdu, plen);
+       if (list == NULL)
+               goto done;
+
+       if (list->len != 4) {
+               DBG("Appearance value: invalid data");
+               goto done;
+       }
+
+       /* A device shall have only one instance of the
+       Appearance characteristic. */
+       atval = list->data[0] + 2; /* skip handle value */
+       app = att_get_u16(atval);
+
+       adapter_get_address(adapter, &src);
+       write_remote_appearance(&src, &device->bdaddr, device->bdaddr_type,
+                                                                       app);
+
+done:
+       att_data_list_free(list);
+       if (device->attios == NULL && device->attios_offline == NULL)
+               att_cleanup(device);
+}
+
+static void primary_cb(GSList *services, guint8 status, gpointer user_data)
+{
+       struct browse_req *req = user_data;
+       struct btd_device *device = req->device;
+       struct gatt_primary *gap_prim = NULL;
+       GSList *l, *uuids = NULL;
+
+       if (status) {
+               if (req->msg) {
+                       DBusMessage *reply;
+                       reply = btd_error_failed(req->msg,
+                                                       att_ecode2str(status));
+                       g_dbus_send_message(req->conn, reply);
+               }
+               goto done;
+       }
+
+       device_set_temporary(device, FALSE);
+
+       for (l = services; l; l = l->next) {
+               struct gatt_primary *prim = l->data;
+
+               if (strcmp(prim->uuid, GAP_SVC_UUID) == 0)
+                       gap_prim = prim;
+
+               uuids = g_slist_append(uuids, prim->uuid);
+       }
+
+       device_register_services(req->conn, device, g_slist_copy(services), -1);
+       device_probe_drivers(device, uuids);
+
+       if (gap_prim) {
+               /* Read appearance characteristic */
+               bt_uuid_t uuid;
+
+               bt_uuid16_create(&uuid, APPEARANCE_CHR_UUID);
+
+               gatt_read_char_by_uuid(device->attrib, gap_prim->range.start,
+                       gap_prim->range.end, &uuid, appearance_cb, device);
+       } else if (device->attios == NULL && device->attios_offline == NULL)
+               att_cleanup(device);
+
+       g_slist_free(uuids);
+
+       services_changed(device);
+       if (req->msg)
+               create_device_reply(device, req);
+
+       store_services(device);
+
+done:
+       device->browse = NULL;
+       browse_request_free(req);
+}
+
+static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+       struct att_callbacks *attcb = user_data;
+       struct btd_device *device = attcb->user_data;
+       GAttrib *attrib;
+
+       g_io_channel_unref(device->att_io);
+       device->att_io = NULL;
+
+       if (gerr) {
+               DBG("%s", gerr->message);
+
+               if (attcb->error)
+                       attcb->error(gerr, user_data);
+
+               goto done;
+       }
+
+       attrib = g_attrib_new(io);
+       device->attachid = attrib_channel_attach(attrib);
+       if (device->attachid == 0)
+               error("Attribute server attach failure!");
+
+       device->attrib = attrib;
+       device->cleanup_id = g_io_add_watch(io, G_IO_HUP,
+                                       attrib_disconnected_cb, device);
+
+       if (attcb->success)
+               attcb->success(user_data);
+done:
+       g_free(attcb);
+}
+
+static void att_error_cb(const GError *gerr, gpointer user_data)
+{
+       struct att_callbacks *attcb = user_data;
+       struct btd_device *device = attcb->user_data;
+
+       if (device->auto_connect == FALSE)
+               return;
+
+       device->auto_id = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT_IDLE,
+                                               AUTO_CONNECTION_INTERVAL,
+                                               att_connect, device,
+                                               att_connect_dispatched);
+
+       DBG("Enabling automatic connections");
+}
+
+static void att_success_cb(gpointer user_data)
+{
+       struct att_callbacks *attcb = user_data;
+       struct btd_device *device = attcb->user_data;
+
+       if (device->attios == NULL)
+               return;
+
+       g_slist_foreach(device->attios, attio_connected, device->attrib);
+}
+
+static gboolean att_connect(gpointer user_data)
+{
+       struct btd_device *device = user_data;
+       struct btd_adapter *adapter = device->adapter;
+       struct att_callbacks *attcb;
+       GIOChannel *io;
+       GError *gerr = NULL;
+       char addr[18];
+       bdaddr_t sba;
+
+       adapter_get_address(adapter, &sba);
+       ba2str(&device->bdaddr, addr);
+
+       DBG("Connection attempt to: %s", addr);
+
+       attcb = g_new0(struct att_callbacks, 1);
+       attcb->error = att_error_cb;
+       attcb->success = att_success_cb;
+       attcb->user_data = device;
+
+       if (device_is_bredr(device)) {
+               io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
+                                       attcb, NULL, &gerr,
+                                       BT_IO_OPT_SOURCE_BDADDR, &sba,
+                                       BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+                                       BT_IO_OPT_PSM, ATT_PSM,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                                       BT_IO_OPT_INVALID);
+       } else {
+               io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
+                               attcb, NULL, &gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &sba,
+                               BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+                               BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
+                               BT_IO_OPT_CID, ATT_CID,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_INVALID);
+       }
+
+       if (io == NULL) {
+               error("ATT bt_io_connect(%s): %s", addr, gerr->message);
+               g_error_free(gerr);
+               g_free(attcb);
+               return FALSE;
+       }
+
+       device->att_io = io;
+
+       return FALSE;
+}
+
+static void att_browse_error_cb(const GError *gerr, gpointer user_data)
+{
+       struct att_callbacks *attcb = user_data;
+       struct btd_device *device = attcb->user_data;
+       struct browse_req *req = device->browse;
+
+       if (req->msg) {
+               DBusMessage *reply;
+
+               reply = btd_error_failed(req->msg, gerr->message);
+               g_dbus_send_message(req->conn, reply);
+       }
+
+       device->browse = NULL;
+       browse_request_free(req);
+}
+
+static void att_browse_cb(gpointer user_data)
+{
+       struct att_callbacks *attcb = user_data;
+       struct btd_device *device = attcb->user_data;
+
+       gatt_discover_primary(device->attrib, NULL, primary_cb,
+                                                       device->browse);
+}
+
+int device_browse_primary(struct btd_device *device, DBusConnection *conn,
+                               DBusMessage *msg, gboolean secure)
+{
+       struct btd_adapter *adapter = device->adapter;
+       struct att_callbacks *attcb;
+       struct browse_req *req;
+       BtIOSecLevel sec_level;
+       bdaddr_t src;
+
+       if (device->browse)
+               return -EBUSY;
+
+       /* FIXME: GATT service updates (implemented in update_services() for
+        * SDP) are not supported yet. It will be supported once client side
+        * "Services Changed" characteristic handling is implemented. */
+       if (device->primaries) {
+               error("Could not update GATT services");
+               return -ENOSYS;
+       }
+
+       req = g_new0(struct browse_req, 1);
+       req->device = btd_device_ref(device);
+       adapter_get_address(adapter, &src);
+
+       device->browse = req;
+
+       if (device->attrib) {
+               gatt_discover_primary(device->attrib, NULL, primary_cb, req);
+               goto done;
+       }
+
+       sec_level = secure ? BT_IO_SEC_HIGH : BT_IO_SEC_LOW;
+
+       attcb = g_new0(struct att_callbacks, 1);
+       attcb->error = att_browse_error_cb;
+       attcb->success = att_browse_cb;
+       attcb->user_data = device;
+
+       device->att_io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
+                               attcb, NULL, NULL,
+                               BT_IO_OPT_SOURCE_BDADDR, &src,
+                               BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+                               BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
+                               BT_IO_OPT_CID, ATT_CID,
+                               BT_IO_OPT_SEC_LEVEL, sec_level,
+                               BT_IO_OPT_INVALID);
+
+       if (device->att_io == NULL) {
+               device->browse = NULL;
+               browse_request_free(req);
+               g_free(attcb);
+               return -EIO;
+       }
+
+done:
+       if (conn == NULL)
+               conn = get_dbus_connection();
+
+       req->conn = dbus_connection_ref(conn);
+
+       if (msg) {
+               const char *sender = dbus_message_get_sender(msg);
+
+               req->msg = dbus_message_ref(msg);
+               /* Track the request owner to cancel it
+                * automatically if the owner exits */
+               req->listener_id = g_dbus_add_disconnect_watch(conn,
+                                               sender,
+                                               discover_services_req_exit,
+                                               req, NULL);
+       }
+
+       return 0;
+}
+
+int device_browse_sdp(struct btd_device *device, DBusConnection *conn,
+                       DBusMessage *msg, uuid_t *search, gboolean reverse)
+{
+       struct btd_adapter *adapter = device->adapter;
+       struct browse_req *req;
+       bt_callback_t cb;
+       bdaddr_t src;
+       uuid_t uuid;
+       int err;
+
+       if (device->browse)
+               return -EBUSY;
+
+       adapter_get_address(adapter, &src);
+
+       req = g_new0(struct browse_req, 1);
+       req->device = btd_device_ref(device);
+       if (search) {
+               memcpy(&uuid, search, sizeof(uuid_t));
+               cb = search_cb;
+       } else {
+               sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
+               init_browse(req, reverse);
+               cb = browse_cb;
+       }
+
+       err = bt_search_service(&src, &device->bdaddr, &uuid, cb, req, NULL);
+       if (err < 0) {
+               browse_request_free(req);
+               return err;
+       }
+
+       if (conn == NULL)
+               conn = get_dbus_connection();
+
+       req->conn = dbus_connection_ref(conn);
+       device->browse = req;
+
+       if (msg) {
+               const char *sender = dbus_message_get_sender(msg);
+
+               req->msg = dbus_message_ref(msg);
+               /* Track the request owner to cancel it
+                * automatically if the owner exits */
+               req->listener_id = g_dbus_add_disconnect_watch(conn,
+                                               sender,
+                                               discover_services_req_exit,
+                                               req, NULL);
+       }
+
+       return err;
+}
+
+struct btd_adapter *device_get_adapter(struct btd_device *device)
+{
+       if (!device)
+               return NULL;
+
+       return device->adapter;
+}
+
+void device_get_address(struct btd_device *device, bdaddr_t *bdaddr,
+                                                       uint8_t *bdaddr_type)
+{
+       bacpy(bdaddr, &device->bdaddr);
+       if (bdaddr_type != NULL)
+               *bdaddr_type = device->bdaddr_type;
+}
+
+void device_set_addr_type(struct btd_device *device, uint8_t bdaddr_type)
+{
+       if (device == NULL)
+               return;
+
+       device->bdaddr_type = bdaddr_type;
+}
+
+uint8_t device_get_addr_type(struct btd_device *device)
+{
+       return device->bdaddr_type;
+}
+
+const gchar *device_get_path(struct btd_device *device)
+{
+       if (!device)
+               return NULL;
+
+       return device->path;
+}
+
+struct agent *device_get_agent(struct btd_device *device)
+{
+       if (!device)
+               return NULL;
+
+       if (device->agent)
+               return device->agent;
+
+       return adapter_get_agent(device->adapter);
+}
+
+gboolean device_is_busy(struct btd_device *device)
+{
+       return device->browse ? TRUE : FALSE;
+}
+
+gboolean device_is_temporary(struct btd_device *device)
+{
+       return device->temporary;
+}
+
+void device_set_temporary(struct btd_device *device, gboolean temporary)
+{
+       if (!device)
+               return;
+
+       DBG("temporary %d", temporary);
+
+       device->temporary = temporary;
+}
+
+void device_set_bonded(struct btd_device *device, gboolean bonded)
+{
+       if (!device)
+               return;
+
+       DBG("bonded %d", bonded);
+
+       device->bonded = bonded;
+}
+
+void device_set_auto_connect(struct btd_device *device, gboolean enable)
+{
+       char addr[18];
+
+       if (!device)
+               return;
+
+       ba2str(&device->bdaddr, addr);
+
+       DBG("%s auto connect: %d", addr, enable);
+
+       device->auto_connect = enable;
+
+       /* Disabling auto connect */
+       if (enable == FALSE) {
+               if (device->auto_id)
+                       g_source_remove(device->auto_id);
+               return;
+       }
+
+       /* Enabling auto connect */
+       if (device->auto_id != 0)
+               return;
+
+       if (device->attrib) {
+               DBG("Already connected");
+               return;
+       }
+
+       if (device->attios == NULL && device->attios_offline == NULL)
+               return;
+
+       device->auto_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
+                                               att_connect, device,
+                                               att_connect_dispatched);
+}
+
+static gboolean start_discovery(gpointer user_data)
+{
+       struct btd_device *device = user_data;
+
+       if (device_is_bredr(device))
+               device_browse_sdp(device, NULL, NULL, NULL, TRUE);
+       else
+               device_browse_primary(device, NULL, NULL, FALSE);
+
+       device->discov_timer = 0;
+
+       return FALSE;
+}
+
+static DBusMessage *new_authentication_return(DBusMessage *msg, int status)
+{
+       switch (status) {
+       case 0x00: /* success */
+               return dbus_message_new_method_return(msg);
+
+       case 0x04: /* page timeout */
+               return dbus_message_new_error(msg,
+                               ERROR_INTERFACE ".ConnectionAttemptFailed",
+                               "Page Timeout");
+       case 0x08: /* connection timeout */
+               return dbus_message_new_error(msg,
+                               ERROR_INTERFACE ".ConnectionAttemptFailed",
+                               "Connection Timeout");
+       case 0x10: /* connection accept timeout */
+       case 0x22: /* LMP response timeout */
+       case 0x28: /* instant passed - is this a timeout? */
+               return dbus_message_new_error(msg,
+                                       ERROR_INTERFACE ".AuthenticationTimeout",
+                                       "Authentication Timeout");
+       case 0x17: /* too frequent pairing attempts */
+               return dbus_message_new_error(msg,
+                                       ERROR_INTERFACE ".RepeatedAttempts",
+                                       "Repeated Attempts");
+
+       case 0x06:
+       case 0x18: /* pairing not allowed (e.g. gw rejected attempt) */
+               return dbus_message_new_error(msg,
+                                       ERROR_INTERFACE ".AuthenticationRejected",
+                                       "Authentication Rejected");
+
+       case 0x07: /* memory capacity */
+       case 0x09: /* connection limit */
+       case 0x0a: /* synchronous connection limit */
+       case 0x0d: /* limited resources */
+       case 0x13: /* user ended the connection */
+       case 0x14: /* terminated due to low resources */
+       case 0x16: /* connection terminated */
+               return dbus_message_new_error(msg,
+                                       ERROR_INTERFACE ".AuthenticationCanceled",
+                                       "Authentication Canceled");
+
+       case 0x05: /* authentication failure */
+       case 0x0E: /* rejected due to security reasons - is this auth failure? */
+       case 0x25: /* encryption mode not acceptable - is this auth failure? */
+       case 0x26: /* link key cannot be changed - is this auth failure? */
+       case 0x29: /* pairing with unit key unsupported - is this auth failure? */
+       case 0x2f: /* insufficient security - is this auth failure? */
+       default:
+               return dbus_message_new_error(msg,
+                                       ERROR_INTERFACE ".AuthenticationFailed",
+                                       "Authentication Failed");
+       }
+}
+
+static void bonding_request_free(struct bonding_req *bonding)
+{
+       struct btd_device *device;
+
+       if (!bonding)
+               return;
+
+       if (bonding->listener_id)
+               g_dbus_remove_watch(bonding->conn, bonding->listener_id);
+
+       if (bonding->msg)
+               dbus_message_unref(bonding->msg);
+
+       if (bonding->conn)
+               dbus_connection_unref(bonding->conn);
+
+       device = bonding->device;
+       g_free(bonding);
+
+       if (!device)
+               return;
+
+       device->bonding = NULL;
+
+       if (!device->agent)
+               return;
+
+       agent_cancel(device->agent);
+       agent_free(device->agent);
+       device->agent = NULL;
+}
+
+void device_set_paired(struct btd_device *device, gboolean value)
+{
+       DBusConnection *conn = get_dbus_connection();
+
+       if (device->paired == value)
+               return;
+
+       if (!value)
+               btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
+                                                       device->bdaddr_type);
+
+       device->paired = value;
+
+       emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Paired",
+                               DBUS_TYPE_BOOLEAN, &value);
+}
+
+static void device_agent_removed(struct agent *agent, void *user_data)
+{
+       struct btd_device *device = user_data;
+
+       device->agent = NULL;
+
+       if (device->authr)
+               device->authr->agent = NULL;
+}
+
+static struct bonding_req *bonding_request_new(DBusConnection *conn,
+                                               DBusMessage *msg,
+                                               struct btd_device *device,
+                                               const char *agent_path,
+                                               uint8_t capability)
+{
+       struct bonding_req *bonding;
+       const char *name = dbus_message_get_sender(msg);
+       char addr[18];
+
+       ba2str(&device->bdaddr, addr);
+       DBG("Requesting bonding for %s", addr);
+
+       if (!agent_path)
+               goto proceed;
+
+       device->agent = agent_create(device->adapter, name, agent_path,
+                                       capability,
+                                       device_agent_removed,
+                                       device);
+
+       DBG("Temporary agent registered for %s at %s:%s",
+                       addr, name, agent_path);
+
+proceed:
+       bonding = g_new0(struct bonding_req, 1);
+
+       bonding->conn = dbus_connection_ref(conn);
+       bonding->msg = dbus_message_ref(msg);
+
+       return bonding;
+}
+
+static void create_bond_req_exit(DBusConnection *conn, void *user_data)
+{
+       struct btd_device *device = user_data;
+       char addr[18];
+
+       ba2str(&device->bdaddr, addr);
+       DBG("%s: requestor exited before bonding was completed", addr);
+
+       if (device->authr)
+               device_cancel_authentication(device, FALSE);
+
+       if (device->bonding) {
+               device->bonding->listener_id = 0;
+               device_request_disconnect(device, NULL);
+       }
+}
+
+DBusMessage *device_create_bonding(struct btd_device *device,
+                                       DBusConnection *conn,
+                                       DBusMessage *msg,
+                                       const char *agent_path,
+                                       uint8_t capability)
+{
+       struct btd_adapter *adapter = device->adapter;
+       struct bonding_req *bonding;
+       int err;
+
+       if (device->bonding)
+               return btd_error_in_progress(msg);
+
+       if (device_is_bonded(device))
+               return btd_error_already_exists(msg);
+
+       if (device_is_le(device)) {
+               struct att_callbacks *attcb;
+               GError *gerr = NULL;
+               bdaddr_t sba;
+
+               adapter_get_address(adapter, &sba);
+
+               attcb = g_new0(struct att_callbacks, 1);
+               attcb->user_data = device;
+
+               device->att_io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
+                               attcb, NULL, &gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &sba,
+                               BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+                               BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
+                               BT_IO_OPT_CID, ATT_CID,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+                               BT_IO_OPT_INVALID);
+
+               if (device->att_io == NULL) {
+                       DBusMessage *reply = btd_error_failed(msg,
+                                                               gerr->message);
+
+                       error("Bonding bt_io_connect(): %s", gerr->message);
+                       g_error_free(gerr);
+                       g_free(attcb);
+                       return reply;
+               }
+       }
+
+       err = adapter_create_bonding(adapter, &device->bdaddr,
+                                       device->bdaddr_type, capability);
+       if (err < 0)
+               return btd_error_failed(msg, strerror(-err));
+
+       bonding = bonding_request_new(conn, msg, device, agent_path,
+                                       capability);
+
+       bonding->listener_id = g_dbus_add_disconnect_watch(conn,
+                                               dbus_message_get_sender(msg),
+                                               create_bond_req_exit, device,
+                                               NULL);
+
+       device->bonding = bonding;
+       bonding->device = device;
+
+       return NULL;
+}
+
+void device_simple_pairing_complete(struct btd_device *device, uint8_t status)
+{
+       struct authentication_req *auth = device->authr;
+
+       if (auth && (auth->type == AUTH_TYPE_NOTIFY_PASSKEY
+                    || auth->type == AUTH_TYPE_NOTIFY_PINCODE) && auth->agent)
+               agent_cancel(auth->agent);
+}
+
+static void device_auth_req_free(struct btd_device *device)
+{
+       if (device->authr)
+               g_free(device->authr->pincode);
+       g_free(device->authr);
+       device->authr = NULL;
+}
+
+void device_bonding_complete(struct btd_device *device, uint8_t status)
+{
+       struct bonding_req *bonding = device->bonding;
+       struct authentication_req *auth = device->authr;
+
+       DBG("bonding %p status 0x%02x", bonding, status);
+
+       if (auth && (auth->type == AUTH_TYPE_NOTIFY_PASSKEY
+                    || auth->type == AUTH_TYPE_NOTIFY_PINCODE) && auth->agent)
+               agent_cancel(auth->agent);
+
+       if (status) {
+               device_cancel_authentication(device, TRUE);
+               device_cancel_bonding(device, status);
+               return;
+       }
+
+       device_auth_req_free(device);
+
+       /* If we're already paired nothing more is needed */
+       if (device->paired)
+               return;
+
+       device_set_paired(device, TRUE);
+
+       /* If we were initiators start service discovery immediately.
+        * However if the other end was the initator wait a few seconds
+        * before SDP. This is due to potential IOP issues if the other
+        * end starts doing SDP at the same time as us */
+       if (bonding) {
+               DBG("Proceeding with service discovery");
+               /* If we are initiators remove any discovery timer and just
+                * start discovering services directly */
+               if (device->discov_timer) {
+                       g_source_remove(device->discov_timer);
+                       device->discov_timer = 0;
+               }
+
+               if (device_is_bredr(device))
+                       device_browse_sdp(device, bonding->conn, bonding->msg,
+                                                               NULL, FALSE);
+               else
+                       device_browse_primary(device, bonding->conn,
+                                                       bonding->msg, FALSE);
+
+               bonding_request_free(bonding);
+       } else {
+               if (!device->browse && !device->discov_timer &&
+                               main_opts.reverse_sdp) {
+                       /* If we are not initiators and there is no currently
+                        * active discovery or discovery timer, set discovery
+                        * timer */
+                       DBG("setting timer for reverse service discovery");
+                       device->discov_timer = g_timeout_add_seconds(
+                                                       DISCOVERY_TIMER,
+                                                       start_discovery,
+                                                       device);
+               }
+       }
+}
+
+gboolean device_is_creating(struct btd_device *device, const char *sender)
+{
+       DBusMessage *msg;
+
+       if (device->bonding && device->bonding->msg)
+               msg = device->bonding->msg;
+       else if (device->browse && device->browse->msg)
+               msg = device->browse->msg;
+       else
+               return FALSE;
+
+       if (!dbus_message_is_method_call(msg, ADAPTER_INTERFACE,
+                                               "CreatePairedDevice") &&
+                       !dbus_message_is_method_call(msg, ADAPTER_INTERFACE,
+                                                       "CreateDevice"))
+               return FALSE;
+
+       if (sender == NULL)
+               return TRUE;
+
+       return g_str_equal(sender, dbus_message_get_sender(msg));
+}
+
+gboolean device_is_bonding(struct btd_device *device, const char *sender)
+{
+       struct bonding_req *bonding = device->bonding;
+
+       if (!device->bonding)
+               return FALSE;
+
+       if (!sender)
+               return TRUE;
+
+       return g_str_equal(sender, dbus_message_get_sender(bonding->msg));
+}
+
+void device_cancel_bonding(struct btd_device *device, uint8_t status)
+{
+       struct bonding_req *bonding = device->bonding;
+       DBusMessage *reply;
+       char addr[18];
+
+       if (!bonding)
+               return;
+
+       ba2str(&device->bdaddr, addr);
+       DBG("Canceling bonding request for %s", addr);
+
+       if (device->authr)
+               device_cancel_authentication(device, FALSE);
+
+       reply = new_authentication_return(bonding->msg, status);
+       g_dbus_send_message(bonding->conn, reply);
+
+       bonding_request_cancel(bonding);
+       bonding_request_free(bonding);
+}
+
+static void pincode_cb(struct agent *agent, DBusError *err,
+                                       const char *pincode, void *data)
+{
+       struct authentication_req *auth = data;
+       struct btd_device *device = auth->device;
+       struct btd_adapter *adapter = device_get_adapter(device);
+       struct agent *adapter_agent = adapter_get_agent(adapter);
+
+       if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+                               g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
+
+               if (auth->agent == adapter_agent || adapter_agent == NULL)
+                       goto done;
+
+               if (agent_request_pincode(adapter_agent, device, pincode_cb,
+                                               auth->secure, auth, NULL) < 0)
+                       goto done;
+
+               auth->agent = adapter_agent;
+               return;
+       }
+
+done:
+       /* No need to reply anything if the authentication already failed */
+       if (auth->cb == NULL)
+               return;
+
+       ((agent_pincode_cb) auth->cb)(agent, err, pincode, device);
+
+       device->authr->cb = NULL;
+       device->authr->agent = NULL;
+}
+
+static void confirm_cb(struct agent *agent, DBusError *err, void *data)
+{
+       struct authentication_req *auth = data;
+       struct btd_device *device = auth->device;
+       struct btd_adapter *adapter = device_get_adapter(device);
+       struct agent *adapter_agent = adapter_get_agent(adapter);
+
+       if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+                               g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
+
+               if (auth->agent == adapter_agent || adapter_agent == NULL)
+                       goto done;
+
+               if (agent_request_confirmation(adapter_agent, device,
+                                               auth->passkey, confirm_cb,
+                                               auth, NULL) < 0)
+                       goto done;
+
+               auth->agent = adapter_agent;
+               return;
+       }
+
+done:
+       /* No need to reply anything if the authentication already failed */
+       if (auth->cb == NULL)
+               return;
+
+       ((agent_cb) auth->cb)(agent, err, device);
+
+       device->authr->cb = NULL;
+       device->authr->agent = NULL;
+}
+
+static void passkey_cb(struct agent *agent, DBusError *err,
+                                               uint32_t passkey, void *data)
+{
+       struct authentication_req *auth = data;
+       struct btd_device *device = auth->device;
+       struct btd_adapter *adapter = device_get_adapter(device);
+       struct agent *adapter_agent = adapter_get_agent(adapter);
+
+       if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+                               g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
+
+               if (auth->agent == adapter_agent || adapter_agent == NULL)
+                       goto done;
+
+               if (agent_request_passkey(adapter_agent, device, passkey_cb,
+                                                       auth, NULL) < 0)
+                       goto done;
+
+               auth->agent = adapter_agent;
+               return;
+       }
+
+done:
+       /* No need to reply anything if the authentication already failed */
+       if (auth->cb == NULL)
+               return;
+
+       ((agent_passkey_cb) auth->cb)(agent, err, passkey, device);
+
+       device->authr->cb = NULL;
+       device->authr->agent = NULL;
+}
+
+static void display_pincode_cb(struct agent *agent, DBusError *err, void *data)
+{
+       struct authentication_req *auth = data;
+       struct btd_device *device = auth->device;
+       struct btd_adapter *adapter = device_get_adapter(device);
+       struct agent *adapter_agent = adapter_get_agent(adapter);
+
+       if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+                               g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
+
+               /* Request a pincode if we fail to display one */
+               if (auth->agent == adapter_agent || adapter_agent == NULL) {
+                       if (agent_request_pincode(agent, device, pincode_cb,
+                                               auth->secure, auth, NULL) < 0)
+                               goto done;
+                       return;
+               }
+
+               if (agent_display_pincode(adapter_agent, device, auth->pincode,
+                                       display_pincode_cb, auth, NULL) < 0)
+                       goto done;
+
+               auth->agent = adapter_agent;
+               return;
+       }
+
+done:
+       /* No need to reply anything if the authentication already failed */
+       if (auth->cb == NULL)
+               return;
+
+       ((agent_pincode_cb) auth->cb)(agent, err, auth->pincode, device);
+
+       g_free(device->authr->pincode);
+       device->authr->pincode = NULL;
+       device->authr->cb = NULL;
+       device->authr->agent = NULL;
+}
+
+
+int device_request_authentication(struct btd_device *device, auth_type_t type,
+                                       void *data, gboolean secure, void *cb)
+{
+       struct authentication_req *auth;
+       struct agent *agent;
+       char addr[18];
+       int err;
+
+       ba2str(&device->bdaddr, addr);
+       DBG("Requesting agent authentication for %s", addr);
+
+       if (device->authr) {
+               error("Authentication already requested for %s", addr);
+               return -EALREADY;
+       }
+
+       agent = device_get_agent(device);
+       if (!agent) {
+               error("No agent available for request type %d", type);
+               return -EPERM;
+       }
+
+       auth = g_new0(struct authentication_req, 1);
+       auth->agent = agent;
+       auth->device = device;
+       auth->cb = cb;
+       auth->type = type;
+       auth->secure = secure;
+       device->authr = auth;
+
+       switch (type) {
+       case AUTH_TYPE_PINCODE:
+               err = agent_request_pincode(agent, device, pincode_cb, secure,
+                                                               auth, NULL);
+               break;
+       case AUTH_TYPE_PASSKEY:
+               err = agent_request_passkey(agent, device, passkey_cb,
+                                                               auth, NULL);
+               break;
+       case AUTH_TYPE_CONFIRM:
+               auth->passkey = *((uint32_t *) data);
+               err = agent_request_confirmation(agent, device, auth->passkey,
+                                               confirm_cb, auth, NULL);
+               break;
+       case AUTH_TYPE_NOTIFY_PASSKEY:
+               auth->passkey = *((uint32_t *) data);
+               err = agent_display_passkey(agent, device, auth->passkey);
+               break;
+       case AUTH_TYPE_NOTIFY_PINCODE:
+               auth->pincode = g_strdup((const char *) data);
+               err = agent_display_pincode(agent, device, auth->pincode,
+                                               display_pincode_cb, auth, NULL);
+               break;
+       default:
+               err = -EINVAL;
+       }
+
+       if (err < 0) {
+               error("Failed requesting authentication");
+               device_auth_req_free(device);
+       }
+
+       return err;
+}
+
+static void cancel_authentication(struct authentication_req *auth)
+{
+       struct btd_device *device;
+       struct agent *agent;
+       DBusError err;
+
+       if (!auth || !auth->cb)
+               return;
+
+       device = auth->device;
+       agent = auth->agent;
+
+       dbus_error_init(&err);
+       dbus_set_error_const(&err, "org.bluez.Error.Canceled", NULL);
+
+       switch (auth->type) {
+       case AUTH_TYPE_PINCODE:
+               ((agent_pincode_cb) auth->cb)(agent, &err, NULL, device);
+               break;
+       case AUTH_TYPE_CONFIRM:
+               ((agent_cb) auth->cb)(agent, &err, device);
+               break;
+       case AUTH_TYPE_PASSKEY:
+               ((agent_passkey_cb) auth->cb)(agent, &err, 0, device);
+               break;
+       case AUTH_TYPE_NOTIFY_PASSKEY:
+               /* User Notify doesn't require any reply */
+               break;
+       case AUTH_TYPE_NOTIFY_PINCODE:
+               ((agent_pincode_cb) auth->cb)(agent, &err, NULL, device);
+               break;
+       }
+
+       dbus_error_free(&err);
+       auth->cb = NULL;
+}
+
+void device_cancel_authentication(struct btd_device *device, gboolean aborted)
+{
+       struct authentication_req *auth = device->authr;
+       char addr[18];
+
+       if (!auth)
+               return;
+
+       ba2str(&device->bdaddr, addr);
+       DBG("Canceling authentication request for %s", addr);
+
+       if (auth->agent)
+               agent_cancel(auth->agent);
+
+       if (!aborted)
+               cancel_authentication(auth);
+
+       device_auth_req_free(device);
+}
+
+gboolean device_is_authenticating(struct btd_device *device)
+{
+       return (device->authr != NULL);
+}
+
+gboolean device_is_authorizing(struct btd_device *device)
+{
+       return device->authorizing;
+}
+
+void device_set_authorizing(struct btd_device *device, gboolean auth)
+{
+       device->authorizing = auth;
+}
+
+void device_register_services(DBusConnection *conn, struct btd_device *device,
+                                               GSList *prim_list, int psm)
+{
+       device->primaries = g_slist_concat(device->primaries, prim_list);
+       device->services = attrib_client_register(conn, device, psm, NULL,
+                                                               prim_list);
+}
+
+GSList *btd_device_get_primaries(struct btd_device *device)
+{
+       return device->primaries;
+}
+
+void btd_device_add_uuid(struct btd_device *device, const char *uuid)
+{
+       GSList *uuid_list;
+       char *new_uuid;
+
+       if (g_slist_find_custom(device->uuids, uuid,
+                               (GCompareFunc) strcasecmp))
+               return;
+
+       new_uuid = g_strdup(uuid);
+       uuid_list = g_slist_append(NULL, new_uuid);
+
+       device_probe_drivers(device, uuid_list);
+
+       g_free(new_uuid);
+       g_slist_free(uuid_list);
+
+       store_profiles(device);
+       services_changed(device);
+}
+
+const sdp_record_t *btd_device_get_record(struct btd_device *device,
+                                                       const char *uuid)
+{
+       bdaddr_t src;
+
+       if (device->tmp_records) {
+               const sdp_record_t *record;
+
+               record = find_record_in_list(device->tmp_records, uuid);
+               if (record != NULL)
+                       return record;
+       }
+
+       adapter_get_address(device->adapter, &src);
+
+       device->tmp_records = read_records(&src, &device->bdaddr);
+       if (!device->tmp_records)
+               return NULL;
+
+       return find_record_in_list(device->tmp_records, uuid);
+}
+
+int btd_register_device_driver(struct btd_device_driver *driver)
+{
+       device_drivers = g_slist_append(device_drivers, driver);
+
+       return 0;
+}
+
+void btd_unregister_device_driver(struct btd_device_driver *driver)
+{
+       device_drivers = g_slist_remove(device_drivers, driver);
+}
+
+struct btd_device *btd_device_ref(struct btd_device *device)
+{
+       device->ref++;
+
+       DBG("%p: ref=%d", device, device->ref);
+
+       return device;
+}
+
+void btd_device_unref(struct btd_device *device)
+{
+       DBusConnection *conn = get_dbus_connection();
+       gchar *path;
+
+       device->ref--;
+
+       DBG("%p: ref=%d", device, device->ref);
+
+       if (device->ref > 0)
+               return;
+
+       path = g_strdup(device->path);
+
+       g_dbus_unregister_interface(conn, path, DEVICE_INTERFACE);
+
+       g_free(path);
+}
+
+void device_set_class(struct btd_device *device, uint32_t value)
+{
+       DBusConnection *conn = get_dbus_connection();
+
+       emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Class",
+                               DBUS_TYPE_UINT32, &value);
+}
+
+static gboolean notify_attios(gpointer user_data)
+{
+       struct btd_device *device = user_data;
+
+       if (device->attrib == NULL)
+               return FALSE;
+
+       g_slist_foreach(device->attios_offline, attio_connected, device->attrib);
+       device->attios = g_slist_concat(device->attios, device->attios_offline);
+       device->attios_offline = NULL;
+
+       return FALSE;
+}
+
+guint btd_device_add_attio_callback(struct btd_device *device,
+                                               attio_connect_cb cfunc,
+                                               attio_disconnect_cb dcfunc,
+                                               gpointer user_data)
+{
+       struct attio_data *attio;
+       static guint attio_id = 0;
+
+       DBG("%p registered ATT connection callback", device);
+
+       attio = g_new0(struct attio_data, 1);
+       attio->id = ++attio_id;
+       attio->cfunc = cfunc;
+       attio->dcfunc = dcfunc;
+       attio->user_data = user_data;
+
+       if (device->attrib && cfunc) {
+               device->attios_offline = g_slist_append(device->attios_offline,
+                                                                       attio);
+               g_idle_add(notify_attios, device);
+               return attio->id;
+       }
+
+       device->attios = g_slist_append(device->attios, attio);
+
+       if (device->auto_id == 0)
+               device->auto_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
+                                               att_connect, device,
+                                               att_connect_dispatched);
+
+       return attio->id;
+}
+
+static int attio_id_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct attio_data *attio = a;
+       guint id = GPOINTER_TO_UINT(b);
+
+       return attio->id - id;
+}
+
+gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id)
+{
+       struct attio_data *attio;
+       GSList *l;
+
+       l = g_slist_find_custom(device->attios, GUINT_TO_POINTER(id),
+                                                               attio_id_cmp);
+       if (l) {
+               attio = l->data;
+               device->attios = g_slist_remove(device->attios, attio);
+       } else {
+               l = g_slist_find_custom(device->attios_offline,
+                                       GUINT_TO_POINTER(id), attio_id_cmp);
+               if (!l)
+                       return FALSE;
+
+               attio = l->data;
+               device->attios_offline = g_slist_remove(device->attios_offline,
+                                                                       attio);
+       }
+
+       g_free(attio);
+
+       if (device->attios != NULL || device->attios_offline != NULL)
+               return TRUE;
+
+       if (device->auto_id) {
+               g_source_remove(device->auto_id);
+               device->auto_id = 0;
+       }
+
+       att_cleanup(device);
+
+       return TRUE;
+}
+
+void device_set_pnpid(struct btd_device *device, uint8_t vendor_id_src,
+                       uint16_t vendor_id, uint16_t product_id,
+                       uint16_t product_ver)
+{
+       device_set_vendor(device, vendor_id);
+       device_set_vendor_src(device, vendor_id_src);
+       device_set_product(device, product_id);
+       device_set_version(device, product_ver);
+}
diff --git a/src/device.h b/src/device.h
new file mode 100644 (file)
index 0000000..26e17f7
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define DEVICE_INTERFACE       "org.bluez.Device"
+
+struct btd_device;
+
+typedef enum {
+       AUTH_TYPE_PINCODE,
+       AUTH_TYPE_PASSKEY,
+       AUTH_TYPE_CONFIRM,
+       AUTH_TYPE_NOTIFY_PASSKEY,
+       AUTH_TYPE_NOTIFY_PINCODE,
+} auth_type_t;
+
+struct btd_device *device_create(DBusConnection *conn,
+                                       struct btd_adapter *adapter,
+                                       const char *address, uint8_t bdaddr_type);
+void device_set_name(struct btd_device *device, const char *name);
+void device_get_name(struct btd_device *device, char *name, size_t len);
+uint16_t btd_device_get_vendor(struct btd_device *device);
+uint16_t btd_device_get_vendor_src(struct btd_device *device);
+uint16_t btd_device_get_product(struct btd_device *device);
+uint16_t btd_device_get_version(struct btd_device *device);
+void device_remove(struct btd_device *device, gboolean remove_stored);
+gint device_address_cmp(struct btd_device *device, const gchar *address);
+int device_browse_primary(struct btd_device *device, DBusConnection *conn,
+                               DBusMessage *msg, gboolean secure);
+int device_browse_sdp(struct btd_device *device, DBusConnection *conn,
+                       DBusMessage *msg, uuid_t *search, gboolean reverse);
+void device_probe_drivers(struct btd_device *device, GSList *profiles);
+const sdp_record_t *btd_device_get_record(struct btd_device *device,
+                                               const char *uuid);
+GSList *btd_device_get_primaries(struct btd_device *device);
+void device_register_services(DBusConnection *conn, struct btd_device *device,
+                                               GSList *prim_list, int psm);
+GSList *device_services_from_record(struct btd_device *device,
+                                                       GSList *profiles);
+void btd_device_add_uuid(struct btd_device *device, const char *uuid);
+struct btd_adapter *device_get_adapter(struct btd_device *device);
+void device_get_address(struct btd_device *device, bdaddr_t *bdaddr,
+                                                       uint8_t *bdaddr_type);
+void device_set_addr_type(struct btd_device *device, uint8_t bdaddr_type);
+uint8_t device_get_addr_type(struct btd_device *device);
+const gchar *device_get_path(struct btd_device *device);
+struct agent *device_get_agent(struct btd_device *device);
+gboolean device_is_bredr(struct btd_device *device);
+gboolean device_is_le(struct btd_device *device);
+gboolean device_is_busy(struct btd_device *device);
+gboolean device_is_temporary(struct btd_device *device);
+gboolean device_is_paired(struct btd_device *device);
+gboolean device_is_bonded(struct btd_device *device);
+gboolean device_is_trusted(struct btd_device *device);
+void device_set_paired(struct btd_device *device, gboolean paired);
+void device_set_temporary(struct btd_device *device, gboolean temporary);
+void device_set_bonded(struct btd_device *device, gboolean bonded);
+void device_set_auto_connect(struct btd_device *device, gboolean enable);
+gboolean device_is_connected(struct btd_device *device);
+DBusMessage *device_create_bonding(struct btd_device *device,
+                               DBusConnection *conn, DBusMessage *msg,
+                               const char *agent_path, uint8_t capability);
+void device_bonding_complete(struct btd_device *device, uint8_t status);
+void device_simple_pairing_complete(struct btd_device *device, uint8_t status);
+gboolean device_is_creating(struct btd_device *device, const char *sender);
+gboolean device_is_bonding(struct btd_device *device, const char *sender);
+void device_cancel_bonding(struct btd_device *device, uint8_t status);
+int device_request_authentication(struct btd_device *device, auth_type_t type,
+                                       void *data, gboolean secure, void *cb);
+void device_cancel_authentication(struct btd_device *device, gboolean aborted);
+gboolean device_is_authenticating(struct btd_device *device);
+gboolean device_is_authorizing(struct btd_device *device);
+void device_set_authorizing(struct btd_device *device, gboolean auth);
+void device_add_connection(struct btd_device *device, DBusConnection *conn);
+void device_remove_connection(struct btd_device *device, DBusConnection *conn);
+void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
+
+typedef void (*disconnect_watch) (struct btd_device *device, gboolean removal,
+                                       void *user_data);
+
+guint device_add_disconnect_watch(struct btd_device *device,
+                               disconnect_watch watch, void *user_data,
+                               GDestroyNotify destroy);
+void device_remove_disconnect_watch(struct btd_device *device, guint id);
+void device_set_class(struct btd_device *device, uint32_t value);
+
+#define BTD_UUIDS(args...) ((const char *[]) { args, NULL } )
+
+struct btd_device_driver {
+       const char *name;
+       const char **uuids;
+       int (*probe) (struct btd_device *device, GSList *uuids);
+       void (*remove) (struct btd_device *device);
+};
+
+int btd_register_device_driver(struct btd_device_driver *driver);
+void btd_unregister_device_driver(struct btd_device_driver *driver);
+
+struct btd_device *btd_device_ref(struct btd_device *device);
+void btd_device_unref(struct btd_device *device);
+
+int device_block(DBusConnection *conn, struct btd_device *device,
+                                               gboolean update_only);
+int device_unblock(DBusConnection *conn, struct btd_device *device,
+                                       gboolean silent, gboolean update_only);
+void device_set_pnpid(struct btd_device *device, uint8_t vendor_id_src,
+                       uint16_t vendor_id, uint16_t product_id,
+                       uint16_t product_ver);
diff --git a/src/eir.c b/src/eir.c
new file mode 100644 (file)
index 0000000..9d42917
--- /dev/null
+++ b/src/eir.c
@@ -0,0 +1,397 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "glib-helper.h"
+#include "eir.h"
+
+void eir_data_free(struct eir_data *eir)
+{
+       g_slist_free_full(eir->services, g_free);
+       eir->services = NULL;
+       g_free(eir->name);
+       eir->name = NULL;
+}
+
+static void eir_parse_uuid16(struct eir_data *eir, void *data, uint8_t len)
+{
+       uint16_t *uuid16 = data;
+       uuid_t service;
+       char *uuid_str;
+       unsigned int i;
+
+       service.type = SDP_UUID16;
+       for (i = 0; i < len / 2; i++, uuid16++) {
+               service.value.uuid16 = btohs(bt_get_unaligned(uuid16));
+               uuid_str = bt_uuid2string(&service);
+               eir->services = g_slist_append(eir->services, uuid_str);
+       }
+}
+
+static void eir_parse_uuid32(struct eir_data *eir, void *data, uint8_t len)
+{
+       uint32_t *uuid32 = data;
+       uuid_t service;
+       char *uuid_str;
+       unsigned int i;
+
+       service.type = SDP_UUID32;
+       for (i = 0; i < len / 4; i++, uuid32++) {
+               service.value.uuid32 = btohl(bt_get_unaligned(uuid32));
+               uuid_str = bt_uuid2string(&service);
+               eir->services = g_slist_append(eir->services, uuid_str);
+       }
+}
+
+static void eir_parse_uuid128(struct eir_data *eir, uint8_t *data, uint8_t len)
+{
+       uint8_t *uuid_ptr = data;
+       uuid_t service;
+       char *uuid_str;
+       unsigned int i;
+       int k;
+
+       service.type = SDP_UUID128;
+       for (i = 0; i < len / 16; i++) {
+               for (k = 0; k < 16; k++)
+                       service.value.uuid128.data[k] = uuid_ptr[16 - k - 1];
+               uuid_str = bt_uuid2string(&service);
+               eir->services = g_slist_append(eir->services, uuid_str);
+               uuid_ptr += 16;
+       }
+}
+
+int eir_parse(struct eir_data *eir, uint8_t *eir_data, uint8_t eir_len)
+{
+       uint16_t len = 0;
+
+       eir->flags = -1;
+
+       /* No EIR data to parse */
+       if (eir_data == NULL)
+               return 0;
+
+       while (len < eir_len - 1) {
+               uint8_t field_len = eir_data[0];
+               uint8_t data_len, *data = &eir_data[2];
+
+               /* Check for the end of EIR */
+               if (field_len == 0)
+                       break;
+
+               len += field_len + 1;
+
+               /* Do not continue EIR Data parsing if got incorrect length */
+               if (len > eir_len)
+                       break;
+
+               data_len = field_len - 1;
+
+               switch (eir_data[1]) {
+               case EIR_UUID16_SOME:
+               case EIR_UUID16_ALL:
+                       eir_parse_uuid16(eir, data, data_len);
+                       break;
+
+               case EIR_UUID32_SOME:
+               case EIR_UUID32_ALL:
+                       eir_parse_uuid32(eir, data, data_len);
+                       break;
+
+               case EIR_UUID128_SOME:
+               case EIR_UUID128_ALL:
+                       eir_parse_uuid128(eir, data, data_len);
+                       break;
+
+               case EIR_FLAGS:
+                       if (data_len > 0)
+                               eir->flags = *data;
+                       break;
+
+               case EIR_NAME_SHORT:
+               case EIR_NAME_COMPLETE:
+                       /* Some vendors put a NUL byte terminator into
+                        * the name */
+                       while (data_len > 0 && data[data_len - 1] == '\0')
+                               data_len--;
+
+                       if (!g_utf8_validate((char *) data, data_len, NULL))
+                               break;
+
+                       g_free(eir->name);
+
+                       eir->name = g_strndup((char *) data, data_len);
+                       eir->name_complete = eir_data[1] == EIR_NAME_COMPLETE;
+                       break;
+
+               case EIR_CLASS_OF_DEV:
+                       if (data_len < 3)
+                               break;
+                       memcpy(eir->dev_class, data, 3);
+                       break;
+
+               case EIR_GAP_APPEARANCE:
+                       if (data_len < 2)
+                               break;
+                       eir->appearance = bt_get_le16(data);
+                       break;
+               }
+
+               eir_data += field_len + 1;
+       }
+
+       return 0;
+}
+
+#define SIZEOF_UUID128 16
+
+static void eir_generate_uuid128(GSList *list, uint8_t *ptr, uint16_t *eir_len)
+{
+       int i, k, uuid_count = 0;
+       uint16_t len = *eir_len;
+       uint8_t *uuid128;
+       gboolean truncated = FALSE;
+
+       /* Store UUIDs in place, skip 2 bytes to write type and length later */
+       uuid128 = ptr + 2;
+
+       for (; list; list = list->next) {
+               struct uuid_info *uuid = list->data;
+               uint8_t *uuid128_data = uuid->uuid.value.uuid128.data;
+
+               if (uuid->uuid.type != SDP_UUID128)
+                       continue;
+
+               /* Stop if not enough space to put next UUID128 */
+               if ((len + 2 + SIZEOF_UUID128) > HCI_MAX_EIR_LENGTH) {
+                       truncated = TRUE;
+                       break;
+               }
+
+               /* Check for duplicates, EIR data is Little Endian */
+               for (i = 0; i < uuid_count; i++) {
+                       for (k = 0; k < SIZEOF_UUID128; k++) {
+                               if (uuid128[i * SIZEOF_UUID128 + k] !=
+                                       uuid128_data[SIZEOF_UUID128 - 1 - k])
+                                       break;
+                       }
+                       if (k == SIZEOF_UUID128)
+                               break;
+               }
+
+               if (i < uuid_count)
+                       continue;
+
+               /* EIR data is Little Endian */
+               for (k = 0; k < SIZEOF_UUID128; k++)
+                       uuid128[uuid_count * SIZEOF_UUID128 + k] =
+                               uuid128_data[SIZEOF_UUID128 - 1 - k];
+
+               len += SIZEOF_UUID128;
+               uuid_count++;
+       }
+
+       if (uuid_count > 0 || truncated) {
+               /* EIR Data length */
+               ptr[0] = (uuid_count * SIZEOF_UUID128) + 1;
+               /* EIR Data type */
+               ptr[1] = truncated ? EIR_UUID128_SOME : EIR_UUID128_ALL;
+               len += 2;
+               *eir_len = len;
+       }
+}
+
+void eir_create(const char *name, int8_t tx_power, uint16_t did_vendor,
+                       uint16_t did_product, uint16_t did_version,
+                       uint16_t did_source, GSList *uuids, uint8_t *data)
+{
+       GSList *l;
+       uint8_t *ptr = data;
+       uint16_t eir_len = 0;
+       uint16_t uuid16[HCI_MAX_EIR_LENGTH / 2];
+       int i, uuid_count = 0;
+       gboolean truncated = FALSE;
+       size_t name_len;
+
+       name_len = strlen(name);
+
+       if (name_len > 0) {
+               /* EIR Data type */
+               if (name_len > 48) {
+                       name_len = 48;
+                       ptr[1] = EIR_NAME_SHORT;
+               } else
+                       ptr[1] = EIR_NAME_COMPLETE;
+
+               /* EIR Data length */
+               ptr[0] = name_len + 1;
+
+               memcpy(ptr + 2, name, name_len);
+
+               eir_len += (name_len + 2);
+               ptr += (name_len + 2);
+       }
+
+       if (tx_power != 0) {
+               *ptr++ = 2;
+               *ptr++ = EIR_TX_POWER;
+               *ptr++ = (uint8_t) tx_power;
+               eir_len += 3;
+       }
+
+       if (did_vendor != 0x0000) {
+               *ptr++ = 9;
+               *ptr++ = EIR_DEVICE_ID;
+               *ptr++ = (did_source & 0x00ff);
+               *ptr++ = (did_source & 0xff00) >> 8;
+               *ptr++ = (did_vendor & 0x00ff);
+               *ptr++ = (did_vendor & 0xff00) >> 8;
+               *ptr++ = (did_product & 0x00ff);
+               *ptr++ = (did_product & 0xff00) >> 8;
+               *ptr++ = (did_version & 0x00ff);
+               *ptr++ = (did_version & 0xff00) >> 8;
+               eir_len += 10;
+       }
+
+       /* Group all UUID16 types */
+       for (l = uuids; l != NULL; l = g_slist_next(l)) {
+               struct uuid_info *uuid = l->data;
+
+               if (uuid->uuid.type != SDP_UUID16)
+                       continue;
+
+               if (uuid->uuid.value.uuid16 < 0x1100)
+                       continue;
+
+               if (uuid->uuid.value.uuid16 == PNP_INFO_SVCLASS_ID)
+                       continue;
+
+               /* Stop if not enough space to put next UUID16 */
+               if ((eir_len + 2 + sizeof(uint16_t)) > HCI_MAX_EIR_LENGTH) {
+                       truncated = TRUE;
+                       break;
+               }
+
+               /* Check for duplicates */
+               for (i = 0; i < uuid_count; i++)
+                       if (uuid16[i] == uuid->uuid.value.uuid16)
+                               break;
+
+               if (i < uuid_count)
+                       continue;
+
+               uuid16[uuid_count++] = uuid->uuid.value.uuid16;
+               eir_len += sizeof(uint16_t);
+       }
+
+       if (uuid_count > 0) {
+               /* EIR Data length */
+               ptr[0] = (uuid_count * sizeof(uint16_t)) + 1;
+               /* EIR Data type */
+               ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
+
+               ptr += 2;
+               eir_len += 2;
+
+               for (i = 0; i < uuid_count; i++) {
+                       *ptr++ = (uuid16[i] & 0x00ff);
+                       *ptr++ = (uuid16[i] & 0xff00) >> 8;
+               }
+       }
+
+       /* Group all UUID128 types */
+       if (eir_len <= HCI_MAX_EIR_LENGTH - 2)
+               eir_generate_uuid128(uuids, ptr, &eir_len);
+}
+
+gboolean eir_has_data_type(uint8_t *data, size_t len, uint8_t type)
+{
+       uint8_t field_len;
+       size_t parsed = 0;
+
+       while (parsed < len - 1) {
+               field_len = data[0];
+
+               if (field_len == 0)
+                       break;
+
+               parsed += field_len + 1;
+
+               if (parsed > len)
+                       break;
+
+               if (data[1] == type)
+                       return TRUE;
+
+               data += field_len + 1;
+       }
+
+       return FALSE;
+}
+
+size_t eir_append_data(uint8_t *eir, size_t eir_len, uint8_t type,
+                                               uint8_t *data, size_t data_len)
+{
+       eir[eir_len++] = sizeof(type) + data_len;
+       eir[eir_len++] = type;
+       memcpy(&eir[eir_len], data, data_len);
+       eir_len += data_len;
+
+       return eir_len;
+}
+
+size_t eir_length(uint8_t *eir, size_t maxlen)
+{
+       uint8_t field_len;
+       size_t parsed = 0, length = 0;
+
+       while (parsed < maxlen - 1) {
+               field_len = eir[0];
+
+               if (field_len == 0)
+                       break;
+
+               parsed += field_len + 1;
+
+               if (parsed > maxlen)
+                       break;
+
+               length = parsed;
+               eir += field_len + 1;
+       }
+
+       return length;
+}
diff --git a/src/eir.h b/src/eir.h
new file mode 100644 (file)
index 0000000..3c81024
--- /dev/null
+++ b/src/eir.h
@@ -0,0 +1,63 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define EIR_FLAGS                   0x01  /* flags */
+#define EIR_UUID16_SOME             0x02  /* 16-bit UUID, more available */
+#define EIR_UUID16_ALL              0x03  /* 16-bit UUID, all listed */
+#define EIR_UUID32_SOME             0x04  /* 32-bit UUID, more available */
+#define EIR_UUID32_ALL              0x05  /* 32-bit UUID, all listed */
+#define EIR_UUID128_SOME            0x06  /* 128-bit UUID, more available */
+#define EIR_UUID128_ALL             0x07  /* 128-bit UUID, all listed */
+#define EIR_NAME_SHORT              0x08  /* shortened local name */
+#define EIR_NAME_COMPLETE           0x09  /* complete local name */
+#define EIR_TX_POWER                0x0A  /* transmit power level */
+#define EIR_CLASS_OF_DEV            0x0D  /* Class of Device */
+#define EIR_DEVICE_ID               0x10  /* device ID */
+#define EIR_GAP_APPEARANCE          0x19  /* GAP appearance */
+
+struct uuid_info {
+       uuid_t uuid;
+       uint8_t svc_hint;
+};
+
+struct eir_data {
+       GSList *services;
+       int flags;
+       char *name;
+       uint8_t dev_class[3];
+       uint16_t appearance;
+       gboolean name_complete;
+};
+
+void eir_data_free(struct eir_data *eir);
+int eir_parse(struct eir_data *eir, uint8_t *eir_data, uint8_t eir_len);
+void eir_create(const char *name, int8_t tx_power, uint16_t did_vendor,
+                       uint16_t did_product, uint16_t did_version,
+                       uint16_t did_source, GSList *uuids, uint8_t *data);
+
+gboolean eir_has_data_type(uint8_t *data, size_t len, uint8_t type);
+
+size_t eir_append_data(uint8_t *eir, size_t eir_len, uint8_t type,
+                                               uint8_t *data, size_t data_len);
+size_t eir_length(uint8_t *eir, size_t maxlen);
diff --git a/src/error.c b/src/error.c
new file mode 100644 (file)
index 0000000..c2d9baa
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2007-2008  Fabien Chevalier <fabchevalier@free.fr>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gdbus.h>
+
+#include "error.h"
+
+DBusMessage *btd_error_invalid_args(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+}
+
+DBusMessage *btd_error_busy(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress",
+                                       "Operation already in progress");
+}
+
+DBusMessage *btd_error_already_exists(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyExists",
+                                       "Already Exists");
+}
+
+DBusMessage *btd_error_not_supported(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".NotSupported",
+                                       "Operation is not supported");
+}
+
+DBusMessage *btd_error_not_connected(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".NotConnected",
+                                       "Not Connected");
+}
+
+DBusMessage *btd_error_already_connected(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".AlreadyConnected",
+                                       "Already Connected");
+}
+
+DBusMessage *btd_error_in_progress(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress",
+                                       "In Progress");
+}
+
+DBusMessage *btd_error_not_available(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable",
+                                       "Operation currently not available");
+}
+
+DBusMessage *btd_error_does_not_exist(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExist",
+                                       "Does Not Exist");
+}
+
+DBusMessage *btd_error_not_authorized(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAuthorized",
+                                       "Operation Not Authorized");
+}
+
+DBusMessage *btd_error_no_such_adapter(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".NoSuchAdapter",
+                                       "No such adapter");
+}
+
+DBusMessage *btd_error_agent_not_available(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".AgentNotAvailable",
+                                       "Agent Not Available");
+}
+
+DBusMessage *btd_error_not_ready(DBusMessage *msg)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE ".NotReady",
+                                       "Resource Not Ready");
+}
+
+DBusMessage *btd_error_failed(DBusMessage *msg, const char *str)
+{
+       return g_dbus_create_error(msg, ERROR_INTERFACE
+                                       ".Failed", "%s", str);
+}
diff --git a/src/error.h b/src/error.h
new file mode 100644 (file)
index 0000000..cdb8919
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2007-2008  Fabien Chevalier <fabchevalier@free.fr>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <dbus/dbus.h>
+
+#define ERROR_INTERFACE "org.bluez.Error"
+
+DBusMessage *btd_error_invalid_args(DBusMessage *msg);
+DBusMessage *btd_error_busy(DBusMessage *msg);
+DBusMessage *btd_error_already_exists(DBusMessage *msg);
+DBusMessage *btd_error_not_supported(DBusMessage *msg);
+DBusMessage *btd_error_not_connected(DBusMessage *msg);
+DBusMessage *btd_error_already_connected(DBusMessage *msg);
+DBusMessage *btd_error_not_available(DBusMessage *msg);
+DBusMessage *btd_error_in_progress(DBusMessage *msg);
+DBusMessage *btd_error_does_not_exist(DBusMessage *msg);
+DBusMessage *btd_error_not_authorized(DBusMessage *msg);
+DBusMessage *btd_error_no_such_adapter(DBusMessage *msg);
+DBusMessage *btd_error_agent_not_available(DBusMessage *msg);
+DBusMessage *btd_error_not_ready(DBusMessage *msg);
+DBusMessage *btd_error_failed(DBusMessage *msg, const char *str);
diff --git a/src/event.c b/src/event.c
new file mode 100644 (file)
index 0000000..ec5926f
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "log.h"
+
+#include "adapter.h"
+#include "manager.h"
+#include "device.h"
+#include "error.h"
+#include "dbus-common.h"
+#include "agent.h"
+#include "storage.h"
+#include "event.h"
+
+static gboolean get_adapter_and_device(bdaddr_t *src, bdaddr_t *dst,
+                                       struct btd_adapter **adapter,
+                                       struct btd_device **device,
+                                       gboolean create)
+{
+       DBusConnection *conn = get_dbus_connection();
+       char peer_addr[18];
+
+       *adapter = manager_find_adapter(src);
+       if (!*adapter) {
+               error("Unable to find matching adapter");
+               return FALSE;
+       }
+
+       ba2str(dst, peer_addr);
+
+       if (create)
+               *device = adapter_get_device(conn, *adapter, peer_addr);
+       else
+               *device = adapter_find_device(*adapter, peer_addr);
+
+       if (create && !*device) {
+               error("Unable to get device object!");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+/*****************************************************************
+ *
+ *  Section reserved to HCI commands confirmation handling and low
+ *  level events(eg: device attached/dettached.
+ *
+ *****************************************************************/
+
+static void pincode_cb(struct agent *agent, DBusError *derr,
+                               const char *pincode, struct btd_device *device)
+{
+       struct btd_adapter *adapter = device_get_adapter(device);
+       bdaddr_t dba;
+       int err;
+
+       device_get_address(device, &dba, NULL);
+
+       if (derr) {
+               err = btd_adapter_pincode_reply(adapter, &dba, NULL, 0);
+               if (err < 0)
+                       goto fail;
+               return;
+       }
+
+       err = btd_adapter_pincode_reply(adapter, &dba, pincode,
+                                               pincode ? strlen(pincode) : 0);
+       if (err < 0)
+               goto fail;
+
+       return;
+
+fail:
+       error("Sending PIN code reply failed: %s (%d)", strerror(-err), -err);
+}
+
+int btd_event_request_pin(bdaddr_t *sba, bdaddr_t *dba, gboolean secure)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       char pin[17];
+       ssize_t pinlen;
+       gboolean display = FALSE;
+
+       if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
+               return -ENODEV;
+
+       memset(pin, 0, sizeof(pin));
+       pinlen = btd_adapter_get_pin(adapter, device, pin, &display);
+       if (pinlen > 0 && (!secure || pinlen == 16)) {
+               if (display && device_is_bonding(device, NULL))
+                       return device_request_authentication(device,
+                                               AUTH_TYPE_NOTIFY_PINCODE, pin,
+                                               secure, pincode_cb);
+
+               btd_adapter_pincode_reply(adapter, dba, pin, pinlen);
+               return 0;
+       }
+
+       return device_request_authentication(device, AUTH_TYPE_PINCODE, NULL,
+                                                       secure, pincode_cb);
+}
+
+static int confirm_reply(struct btd_adapter *adapter,
+                               struct btd_device *device, gboolean success)
+{
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+
+       device_get_address(device, &bdaddr, &bdaddr_type);
+
+       return btd_adapter_confirm_reply(adapter, &bdaddr, bdaddr_type,
+                                                               success);
+}
+
+static void confirm_cb(struct agent *agent, DBusError *err, void *user_data)
+{
+       struct btd_device *device = user_data;
+       struct btd_adapter *adapter = device_get_adapter(device);
+       gboolean success = (err == NULL) ? TRUE : FALSE;
+
+       confirm_reply(adapter, device, success);
+}
+
+static void passkey_cb(struct agent *agent, DBusError *err, uint32_t passkey,
+                       void *user_data)
+{
+       struct btd_device *device = user_data;
+       struct btd_adapter *adapter = device_get_adapter(device);
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+
+       device_get_address(device, &bdaddr, &bdaddr_type);
+
+       if (err)
+               passkey = INVALID_PASSKEY;
+
+       btd_adapter_passkey_reply(adapter, &bdaddr, bdaddr_type, passkey);
+}
+
+int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+
+       if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
+               return -ENODEV;
+
+       return device_request_authentication(device, AUTH_TYPE_CONFIRM,
+                                               &passkey, FALSE, confirm_cb);
+}
+
+int btd_event_user_passkey(bdaddr_t *sba, bdaddr_t *dba)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+
+       if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
+               return -ENODEV;
+
+       return device_request_authentication(device, AUTH_TYPE_PASSKEY, NULL,
+                                                       FALSE, passkey_cb);
+}
+
+int btd_event_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+
+       if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
+               return -ENODEV;
+
+       return device_request_authentication(device, AUTH_TYPE_NOTIFY_PASSKEY,
+                                                       &passkey, FALSE, NULL);
+}
+
+void btd_event_simple_pairing_complete(bdaddr_t *local, bdaddr_t *peer,
+                                                               uint8_t status)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       gboolean create;
+
+       DBG("status=%02x", status);
+
+       create = status ? FALSE : TRUE;
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, create))
+               return;
+
+       if (!device)
+               return;
+
+       device_simple_pairing_complete(device, status);
+}
+
+static void update_lastseen(bdaddr_t *sba, bdaddr_t *dba)
+{
+       time_t t;
+       struct tm *tm;
+
+       t = time(NULL);
+       tm = gmtime(&t);
+
+       write_lastseen_info(sba, dba, tm);
+}
+
+static void update_lastused(bdaddr_t *sba, bdaddr_t *dba)
+{
+       time_t t;
+       struct tm *tm;
+
+       t = time(NULL);
+       tm = gmtime(&t);
+
+       write_lastused_info(sba, dba, tm);
+}
+
+void btd_event_device_found(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                       int8_t rssi, uint8_t confirm_name,
+                                       uint8_t *data, uint8_t data_len)
+{
+       struct btd_adapter *adapter;
+
+       adapter = manager_find_adapter(local);
+       if (!adapter) {
+               error("No matching adapter found");
+               return;
+       }
+
+       update_lastseen(local, peer);
+
+       if (data)
+               write_remote_eir(local, peer, data, data_len);
+
+       adapter_update_found_devices(adapter, peer, bdaddr_type, rssi,
+                                               confirm_name, data, data_len);
+}
+
+void btd_event_set_legacy_pairing(bdaddr_t *local, bdaddr_t *peer,
+                                                       gboolean legacy)
+{
+       struct btd_adapter *adapter;
+       struct remote_dev_info *dev;
+
+       adapter = manager_find_adapter(local);
+       if (!adapter) {
+               error("No matching adapter found");
+               return;
+       }
+
+       dev = adapter_search_found_devices(adapter, peer);
+       if (dev)
+               dev->legacy = legacy;
+}
+
+void btd_event_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       uint32_t old_class = 0;
+
+       read_remote_class(local, peer, &old_class);
+
+       if (old_class == class)
+               return;
+
+       write_remote_class(local, peer, class);
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+               return;
+
+       if (!device)
+               return;
+
+       device_set_class(device, class);
+}
+
+void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, char *name)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       struct remote_dev_info *dev_info;
+
+       if (!g_utf8_validate(name, -1, NULL)) {
+               int i;
+
+               /* Assume ASCII, and replace all non-ASCII with spaces */
+               for (i = 0; name[i] != '\0'; i++) {
+                       if (!isascii(name[i]))
+                               name[i] = ' ';
+               }
+               /* Remove leading and trailing whitespace characters */
+               g_strstrip(name);
+       }
+
+       write_device_name(local, peer, name);
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+               return;
+
+       dev_info = adapter_search_found_devices(adapter, peer);
+       if (dev_info) {
+               g_free(dev_info->name);
+               dev_info->name = g_strdup(name);
+               adapter_emit_device_found(adapter, dev_info);
+       }
+
+       if (device)
+               device_set_name(device, name);
+}
+
+static char *buf2str(uint8_t *data, int datalen)
+{
+       char *buf;
+       int i;
+
+       buf = g_try_new0(char, (datalen * 2) + 1);
+       if (buf == NULL)
+               return NULL;
+
+       for (i = 0; i < datalen; i++)
+               sprintf(buf + (i * 2), "%2.2x", data[i]);
+
+       return buf;
+}
+
+static int store_longtermkey(bdaddr_t *local, bdaddr_t *peer,
+                               uint8_t bdaddr_type, unsigned char *key,
+                               uint8_t master, uint8_t authenticated,
+                               uint8_t enc_size, uint16_t ediv, uint8_t rand[8])
+{
+       GString *newkey;
+       char *val, *str;
+       int err;
+
+       val = buf2str(key, 16);
+       if (val == NULL)
+               return -ENOMEM;
+
+       newkey = g_string_new(val);
+       g_free(val);
+
+       g_string_append_printf(newkey, " %d %d %d %d ", authenticated, master,
+                                                               enc_size, ediv);
+
+       str = buf2str(rand, 8);
+       if (str == NULL) {
+               g_string_free(newkey, TRUE);
+               return -ENOMEM;
+       }
+
+       newkey = g_string_append(newkey, str);
+       g_free(str);
+
+       err = write_longtermkeys(local, peer, bdaddr_type, newkey->str);
+
+       g_string_free(newkey, TRUE);
+
+       return err;
+}
+
+int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer,
+                               uint8_t *key, uint8_t key_type,
+                               uint8_t pin_length)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       int ret;
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE))
+               return -ENODEV;
+
+       DBG("storing link key of type 0x%02x", key_type);
+
+       ret = write_link_key(local, peer, key, key_type, pin_length);
+
+       if (ret == 0) {
+               device_set_bonded(device, TRUE);
+
+               if (device_is_temporary(device))
+                       device_set_temporary(device, FALSE);
+       }
+
+       return ret;
+}
+
+int btd_event_ltk_notify(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                       uint8_t *key, uint8_t master,
+                                       uint8_t authenticated, uint8_t enc_size,
+                                       uint16_t ediv, uint8_t rand[8])
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       int ret;
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE))
+               return -ENODEV;
+
+       ret = store_longtermkey(local, peer, bdaddr_type, key, master,
+                                       authenticated, enc_size, ediv, rand);
+       if (ret == 0) {
+               device_set_bonded(device, TRUE);
+
+               if (device_is_temporary(device))
+                       device_set_temporary(device, FALSE);
+       }
+
+       return ret;
+}
+
+void btd_event_conn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                               char *name, uint8_t *dev_class)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE))
+               return;
+
+       update_lastused(local, peer);
+
+       if (dev_class != NULL) {
+               uint32_t class = dev_class[0] | (dev_class[1] << 8) |
+                                                       (dev_class[2] << 16);
+
+               if (class != 0)
+                       write_remote_class(local, peer, class);
+       }
+
+       device_set_addr_type(device, bdaddr_type);
+
+       adapter_add_connection(adapter, device);
+
+       if (name != NULL)
+               btd_event_remote_name(local, peer, name);
+}
+
+void btd_event_conn_failed(bdaddr_t *local, bdaddr_t *peer, uint8_t status)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       DBusConnection *conn = get_dbus_connection();
+
+       DBG("status 0x%02x", status);
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+               return;
+
+       if (!device)
+               return;
+
+       if (device_is_bonding(device, NULL))
+               device_cancel_bonding(device, status);
+
+       if (device_is_temporary(device))
+               adapter_remove_device(conn, adapter, device, TRUE);
+}
+
+void btd_event_disconn_complete(bdaddr_t *local, bdaddr_t *peer)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+
+       DBG("");
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+               return;
+
+       if (!device)
+               return;
+
+       adapter_remove_connection(adapter, device);
+}
+
+void btd_event_device_blocked(bdaddr_t *local, bdaddr_t *peer)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+
+       DBusConnection *conn = get_dbus_connection();
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+               return;
+
+       if (device)
+               device_block(conn, device, TRUE);
+}
+
+void btd_event_device_unblocked(bdaddr_t *local, bdaddr_t *peer)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+
+       DBusConnection *conn = get_dbus_connection();
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+               return;
+
+       if (device)
+               device_unblock(conn, device, FALSE, TRUE);
+}
+
+void btd_event_device_unpaired(bdaddr_t *local, bdaddr_t *peer)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       DBusConnection *conn = get_dbus_connection();
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+               return;
+
+       device_set_temporary(device, TRUE);
+
+       if (device_is_connected(device))
+               device_request_disconnect(device, NULL);
+       else
+               adapter_remove_device(conn, adapter, device, TRUE);
+}
+
+/* Section reserved to device HCI callbacks */
+
+void btd_event_returned_link_key(bdaddr_t *local, bdaddr_t *peer)
+{
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+
+       if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE))
+               return;
+
+       device_set_paired(device, TRUE);
+}
diff --git a/src/event.h b/src/event.h
new file mode 100644 (file)
index 0000000..dfc158d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int btd_event_request_pin(bdaddr_t *sba, bdaddr_t *dba, gboolean secure);
+void btd_event_device_found(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                       int8_t rssi, uint8_t confirm_name,
+                                       uint8_t *data, uint8_t data_len);
+void btd_event_set_legacy_pairing(bdaddr_t *local, bdaddr_t *peer, gboolean legacy);
+void btd_event_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class);
+void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, char *name);
+void btd_event_conn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                       char *name, uint8_t *dev_class);
+void btd_event_conn_failed(bdaddr_t *local, bdaddr_t *peer, uint8_t status);
+void btd_event_disconn_complete(bdaddr_t *local, bdaddr_t *peer);
+void btd_event_simple_pairing_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t status);
+void btd_event_returned_link_key(bdaddr_t *local, bdaddr_t *peer);
+int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey);
+int btd_event_user_passkey(bdaddr_t *sba, bdaddr_t *dba);
+int btd_event_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey);
+void btd_event_device_blocked(bdaddr_t *local, bdaddr_t *peer);
+void btd_event_device_unblocked(bdaddr_t *local, bdaddr_t *peer);
+void btd_event_device_unpaired(bdaddr_t *local, bdaddr_t *peer);
+int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer, uint8_t *key,
+                                       uint8_t key_type, uint8_t pin_length);
+int btd_event_ltk_notify(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                       uint8_t *key, uint8_t master,
+                                       uint8_t authenticated, uint8_t enc_size,
+                                       uint16_t ediv, uint8_t rand[8]);
diff --git a/src/genbuiltin b/src/genbuiltin
new file mode 100755 (executable)
index 0000000..8b6f047
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+for i in $*
+do
+       echo "extern struct bluetooth_plugin_desc __bluetooth_builtin_$i;"
+done
+
+echo
+echo "static struct bluetooth_plugin_desc *__bluetooth_builtin[] = {"
+
+for i in $*
+do
+       echo "  &__bluetooth_builtin_$i,"
+done
+
+echo "  NULL"
+echo "};"
diff --git a/src/glib-helper.c b/src/glib-helper.c
new file mode 100644 (file)
index 0000000..419c655
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include "btio.h"
+#include "glib-helper.h"
+
+char *bt_uuid2string(uuid_t *uuid)
+{
+       gchar *str;
+       uuid_t uuid128;
+       unsigned int data0;
+       unsigned short data1;
+       unsigned short data2;
+       unsigned short data3;
+       unsigned int data4;
+       unsigned short data5;
+
+       if (!uuid)
+               return NULL;
+
+       switch (uuid->type) {
+       case SDP_UUID16:
+               sdp_uuid16_to_uuid128(&uuid128, uuid);
+               break;
+       case SDP_UUID32:
+               sdp_uuid32_to_uuid128(&uuid128, uuid);
+               break;
+       case SDP_UUID128:
+               memcpy(&uuid128, uuid, sizeof(uuid_t));
+               break;
+       default:
+               /* Type of UUID unknown */
+               return NULL;
+       }
+
+       memcpy(&data0, &uuid128.value.uuid128.data[0], 4);
+       memcpy(&data1, &uuid128.value.uuid128.data[4], 2);
+       memcpy(&data2, &uuid128.value.uuid128.data[6], 2);
+       memcpy(&data3, &uuid128.value.uuid128.data[8], 2);
+       memcpy(&data4, &uuid128.value.uuid128.data[10], 4);
+       memcpy(&data5, &uuid128.value.uuid128.data[14], 2);
+
+       str = g_try_malloc0(MAX_LEN_UUID_STR);
+       if (!str)
+               return NULL;
+
+       sprintf(str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+                       g_ntohl(data0), g_ntohs(data1),
+                       g_ntohs(data2), g_ntohs(data3),
+                       g_ntohl(data4), g_ntohs(data5));
+
+       return str;
+}
+
+static struct {
+       const char      *name;
+       uint16_t        class;
+} bt_services[] = {
+       { "vcp",        VIDEO_CONF_SVCLASS_ID           },
+       { "pbap",       PBAP_SVCLASS_ID                 },
+       { "sap",        SAP_SVCLASS_ID                  },
+       { "ftp",        OBEX_FILETRANS_SVCLASS_ID       },
+       { "bpp",        BASIC_PRINTING_SVCLASS_ID       },
+       { "bip",        IMAGING_SVCLASS_ID              },
+       { "synch",      IRMC_SYNC_SVCLASS_ID            },
+       { "dun",        DIALUP_NET_SVCLASS_ID           },
+       { "opp",        OBEX_OBJPUSH_SVCLASS_ID         },
+       { "fax",        FAX_SVCLASS_ID                  },
+       { "spp",        SERIAL_PORT_SVCLASS_ID          },
+       { "hsp",        HEADSET_SVCLASS_ID              },
+       { "hfp",        HANDSFREE_SVCLASS_ID            },
+       { }
+};
+
+static uint16_t name2class(const char *pattern)
+{
+       int i;
+
+       for (i = 0; bt_services[i].name; i++) {
+               if (strcasecmp(bt_services[i].name, pattern) == 0)
+                       return bt_services[i].class;
+       }
+
+       return 0;
+}
+
+static inline gboolean is_uuid128(const char *string)
+{
+       return (strlen(string) == 36 &&
+                       string[8] == '-' &&
+                       string[13] == '-' &&
+                       string[18] == '-' &&
+                       string[23] == '-');
+}
+
+static int string2uuid16(uuid_t *uuid, const char *string)
+{
+       int length = strlen(string);
+       char *endptr = NULL;
+       uint16_t u16;
+
+       if (length != 4 && length != 6)
+               return -EINVAL;
+
+       u16 = strtol(string, &endptr, 16);
+       if (endptr && *endptr == '\0') {
+               sdp_uuid16_create(uuid, u16);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+char *bt_name2string(const char *pattern)
+{
+       uuid_t uuid;
+       uint16_t uuid16;
+       int i;
+
+       /* UUID 128 string format */
+       if (is_uuid128(pattern))
+               return g_strdup(pattern);
+
+       /* Friendly service name format */
+       uuid16 = name2class(pattern);
+       if (uuid16)
+               goto proceed;
+
+       /* HEX format */
+       uuid16 = strtol(pattern, NULL, 16);
+       for (i = 0; bt_services[i].class; i++) {
+               if (bt_services[i].class == uuid16)
+                       goto proceed;
+       }
+
+       return NULL;
+
+proceed:
+       sdp_uuid16_create(&uuid, uuid16);
+
+       return bt_uuid2string(&uuid);
+}
+
+int bt_string2uuid(uuid_t *uuid, const char *string)
+{
+       uint32_t data0, data4;
+       uint16_t data1, data2, data3, data5;
+
+       if (is_uuid128(string) &&
+                       sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
+                               &data0, &data1, &data2, &data3, &data4, &data5) == 6) {
+               uint8_t val[16];
+
+               data0 = g_htonl(data0);
+               data1 = g_htons(data1);
+               data2 = g_htons(data2);
+               data3 = g_htons(data3);
+               data4 = g_htonl(data4);
+               data5 = g_htons(data5);
+
+               memcpy(&val[0], &data0, 4);
+               memcpy(&val[4], &data1, 2);
+               memcpy(&val[6], &data2, 2);
+               memcpy(&val[8], &data3, 2);
+               memcpy(&val[10], &data4, 4);
+               memcpy(&val[14], &data5, 2);
+
+               sdp_uuid128_create(uuid, val);
+
+               return 0;
+       } else {
+               uint16_t class = name2class(string);
+               if (class) {
+                       sdp_uuid16_create(uuid, class);
+                       return 0;
+               }
+
+               return string2uuid16(uuid, string);
+       }
+}
+
+gchar *bt_list2string(GSList *list)
+{
+       GSList *l;
+       gchar *str, *tmp;
+
+       if (!list)
+               return NULL;
+
+       str = g_strdup((const gchar *) list->data);
+
+       for (l = list->next; l; l = l->next) {
+               tmp = g_strconcat(str, " " , (const gchar *) l->data, NULL);
+               g_free(str);
+               str = tmp;
+       }
+
+       return str;
+}
+
+GSList *bt_string2list(const gchar *str)
+{
+       GSList *l = NULL;
+       gchar **uuids;
+       int i = 0;
+
+       if (!str)
+               return NULL;
+
+       /* FIXME: eglib doesn't support g_strsplit */
+       uuids = g_strsplit(str, " ", 0);
+       if (!uuids)
+               return NULL;
+
+       while (uuids[i]) {
+               l = g_slist_append(l, uuids[i]);
+               i++;
+       }
+
+       g_free(uuids);
+
+       return l;
+}
diff --git a/src/glib-helper.h b/src/glib-helper.h
new file mode 100644 (file)
index 0000000..8836804
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+gchar *bt_uuid2string(uuid_t *uuid);
+char *bt_name2string(const char *string);
+int bt_string2uuid(uuid_t *uuid, const char *string);
+gchar *bt_list2string(GSList *list);
+GSList *bt_string2list(const gchar *str);
diff --git a/src/hcid.h b/src/hcid.h
new file mode 100644 (file)
index 0000000..1e5e15a
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct main_opts {
+       char            host_name[40];
+       unsigned long   flags;
+       char            *name;
+       uint32_t        class;
+       uint16_t        pageto;
+       uint16_t        autoto;
+       uint32_t        discovto;
+       uint32_t        pairto;
+       uint16_t        link_mode;
+       uint16_t        link_policy;
+       gboolean        remember_powered;
+       gboolean        reverse_sdp;
+       gboolean        name_resolv;
+       gboolean        debug_keys;
+       gboolean        gatt_enabled;
+
+       uint8_t         mode;
+
+       uint16_t        did_source;
+       uint16_t        did_vendor;
+       uint16_t        did_product;
+       uint16_t        did_version;
+};
+
+enum {
+       HCID_SET_PAGETO,
+};
+
+extern struct main_opts main_opts;
+
+void btd_start_exit_timer(void);
+void btd_stop_exit_timer(void);
+
+gboolean plugin_init(GKeyFile *config, const char *enable,
+                                                       const char *disable);
+void plugin_cleanup(void);
+
+void rfkill_init(void);
+void rfkill_exit(void);
diff --git a/src/log.c b/src/log.c
new file mode 100644 (file)
index 0000000..75a98a9
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,144 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+#include <glib.h>
+
+#include "log.h"
+
+void info(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_INFO, format, ap);
+
+       va_end(ap);
+}
+
+void warn(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_WARNING, format, ap);
+
+       va_end(ap);
+}
+
+void error(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_ERR, format, ap);
+
+       va_end(ap);
+}
+
+void btd_debug(const char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+
+       vsyslog(LOG_DEBUG, format, ap);
+
+       va_end(ap);
+}
+
+extern struct btd_debug_desc __start___debug[];
+extern struct btd_debug_desc __stop___debug[];
+
+static gchar **enabled = NULL;
+
+static gboolean is_enabled(struct btd_debug_desc *desc)
+{
+       int i;
+
+       if (enabled == NULL)
+               return 0;
+
+       for (i = 0; enabled[i] != NULL; i++)
+               if (desc->file != NULL && g_pattern_match_simple(enabled[i],
+                                                       desc->file) == TRUE)
+                       return 1;
+
+       return 0;
+}
+
+void __btd_enable_debug(struct btd_debug_desc *start,
+                                       struct btd_debug_desc *stop)
+{
+       struct btd_debug_desc *desc;
+
+       if (start == NULL || stop == NULL)
+               return;
+
+       for (desc = start; desc < stop; desc++) {
+               if (is_enabled(desc))
+                       desc->flags |= BTD_DEBUG_FLAG_PRINT;
+       }
+}
+
+void __btd_toggle_debug(void)
+{
+       struct btd_debug_desc *desc;
+
+       for (desc = __start___debug; desc < __stop___debug; desc++)
+               desc->flags |= BTD_DEBUG_FLAG_PRINT;
+}
+
+void __btd_log_init(const char *debug, int detach)
+{
+       int option = LOG_NDELAY | LOG_PID;
+
+       if (debug != NULL)
+               enabled = g_strsplit_set(debug, ":, ", 0);
+
+       __btd_enable_debug(__start___debug, __stop___debug);
+
+       if (!detach)
+               option |= LOG_PERROR;
+
+       openlog("bluetoothd", option, LOG_DAEMON);
+
+       syslog(LOG_INFO, "Bluetooth daemon %s", VERSION);
+}
+
+void __btd_log_cleanup(void)
+{
+       closelog();
+
+       g_strfreev(enabled);
+}
diff --git a/src/log.h b/src/log.h
new file mode 100644 (file)
index 0000000..3d34fa3
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,59 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+void info(const char *format, ...) __attribute__((format(printf, 1, 2)));
+void warn(const char *format, ...) __attribute__((format(printf, 1, 2)));
+void error(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+void btd_debug(const char *format, ...) __attribute__((format(printf, 1, 2)));
+
+void __btd_log_init(const char *debug, int detach);
+void __btd_log_cleanup(void);
+void __btd_toggle_debug(void);
+
+struct btd_debug_desc {
+       const char *file;
+#define BTD_DEBUG_FLAG_DEFAULT (0)
+#define BTD_DEBUG_FLAG_PRINT   (1 << 0)
+       unsigned int flags;
+} __attribute__((aligned(8)));
+
+void __btd_enable_debug(struct btd_debug_desc *start,
+                                       struct btd_debug_desc *stop);
+
+/**
+ * DBG:
+ * @fmt: format string
+ * @arg...: list of arguments
+ *
+ * Simple macro around btd_debug() which also include the function
+ * name it is called in.
+ */
+#define DBG(fmt, arg...) do { \
+       static struct btd_debug_desc __btd_debug_desc \
+       __attribute__((used, section("__debug"), aligned(8))) = { \
+               .file = __FILE__, .flags = BTD_DEBUG_FLAG_DEFAULT, \
+       }; \
+       if (__btd_debug_desc.flags & BTD_DEBUG_FLAG_PRINT) \
+               btd_debug("%s:%s() " fmt,  __FILE__, __FUNCTION__ , ## arg); \
+} while (0)
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..286baa0
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include <gdbus.h>
+
+#include "log.h"
+
+#include "hcid.h"
+#include "sdpd.h"
+#include "adapter.h"
+#include "dbus-common.h"
+#include "agent.h"
+#include "manager.h"
+
+#define BLUEZ_NAME "org.bluez"
+
+#define LAST_ADAPTER_EXIT_TIMEOUT 30
+
+#define DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */
+#define DEFAULT_AUTO_CONNECT_TIMEOUT  60 /* 60 seconds */
+
+struct main_opts main_opts;
+
+static GKeyFile *load_config(const char *file)
+{
+       GError *err = NULL;
+       GKeyFile *keyfile;
+
+       keyfile = g_key_file_new();
+
+       g_key_file_set_list_separator(keyfile, ',');
+
+       if (!g_key_file_load_from_file(keyfile, file, 0, &err)) {
+               error("Parsing %s failed: %s", file, err->message);
+               g_error_free(err);
+               g_key_file_free(keyfile);
+               return NULL;
+       }
+
+       return keyfile;
+}
+
+static void parse_did(const char *did)
+{
+       int result;
+       uint16_t vendor, product, version , source;
+
+       /* version and source are optional */
+       version = 0x0000;
+       source = 0x0002;
+
+       result = sscanf(did, "bluetooth:%4hx:%4hx:%4hx", &vendor, &product, &version);
+       if (result != EOF && result >= 2) {
+               source = 0x0001;
+               goto done;
+       }
+
+       result = sscanf(did, "usb:%4hx:%4hx:%4hx", &vendor, &product, &version);
+       if (result != EOF && result >= 2)
+               goto done;
+
+       result = sscanf(did, "%4hx:%4hx:%4hx", &vendor, &product, &version);
+       if (result == EOF || result < 2)
+               return;
+
+done:
+       main_opts.did_source = source;
+       main_opts.did_vendor = vendor;
+       main_opts.did_product = product;
+       main_opts.did_version = version;
+}
+
+static void parse_config(GKeyFile *config)
+{
+       GError *err = NULL;
+       char *str;
+       int val;
+       gboolean boolean;
+
+       if (!config)
+               return;
+
+       DBG("parsing main.conf");
+
+       val = g_key_file_get_integer(config, "General",
+                                               "DiscoverableTimeout", &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else {
+               DBG("discovto=%d", val);
+               main_opts.discovto = val;
+       }
+
+       val = g_key_file_get_integer(config, "General",
+                                               "PairableTimeout", &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else {
+               DBG("pairto=%d", val);
+               main_opts.pairto = val;
+       }
+
+       val = g_key_file_get_integer(config, "General", "PageTimeout", &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else {
+               DBG("pageto=%d", val);
+               main_opts.pageto = val;
+               main_opts.flags |= 1 << HCID_SET_PAGETO;
+       }
+
+       val = g_key_file_get_integer(config, "General", "AutoConnectTimeout",
+                                                                       &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else {
+               DBG("auto_to=%d", val);
+               main_opts.autoto = val;
+       }
+
+       str = g_key_file_get_string(config, "General", "Name", &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else {
+               DBG("name=%s", str);
+               g_free(main_opts.name);
+               main_opts.name = g_strdup(str);
+               g_free(str);
+       }
+
+       str = g_key_file_get_string(config, "General", "Class", &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else {
+               DBG("class=%s", str);
+               main_opts.class = strtol(str, NULL, 16);
+               g_free(str);
+       }
+
+       boolean = g_key_file_get_boolean(config, "General",
+                                               "InitiallyPowered", &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else if (boolean == FALSE)
+               main_opts.mode = MODE_OFF;
+
+       boolean = g_key_file_get_boolean(config, "General",
+                                               "RememberPowered", &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else
+               main_opts.remember_powered = boolean;
+
+       str = g_key_file_get_string(config, "General", "DeviceID", &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else {
+               DBG("deviceid=%s", str);
+               parse_did(str);
+               g_free(str);
+       }
+
+       boolean = g_key_file_get_boolean(config, "General",
+                                               "ReverseServiceDiscovery", &err);
+       if (err) {
+               DBG("%s", err->message);
+               g_clear_error(&err);
+       } else
+               main_opts.reverse_sdp = boolean;
+
+       boolean = g_key_file_get_boolean(config, "General",
+                                               "NameResolving", &err);
+       if (err)
+               g_clear_error(&err);
+       else
+               main_opts.name_resolv = boolean;
+
+       boolean = g_key_file_get_boolean(config, "General",
+                                               "DebugKeys", &err);
+       if (err)
+               g_clear_error(&err);
+       else
+               main_opts.debug_keys = boolean;
+
+       boolean = g_key_file_get_boolean(config, "General",
+                                               "EnableGatt", &err);
+       if (err)
+               g_clear_error(&err);
+       else
+               main_opts.gatt_enabled = boolean;
+
+       main_opts.link_mode = HCI_LM_ACCEPT;
+
+       main_opts.link_policy = HCI_LP_RSWITCH | HCI_LP_SNIFF |
+                                               HCI_LP_HOLD | HCI_LP_PARK;
+}
+
+static void init_defaults(void)
+{
+       /* Default HCId settings */
+       memset(&main_opts, 0, sizeof(main_opts));
+       main_opts.mode  = MODE_CONNECTABLE;
+       main_opts.name  = g_strdup("BlueZ");
+       main_opts.discovto      = DEFAULT_DISCOVERABLE_TIMEOUT;
+       main_opts.autoto = DEFAULT_AUTO_CONNECT_TIMEOUT;
+       main_opts.remember_powered = TRUE;
+       main_opts.reverse_sdp = TRUE;
+       main_opts.name_resolv = TRUE;
+
+       if (gethostname(main_opts.host_name, sizeof(main_opts.host_name) - 1) < 0)
+               strcpy(main_opts.host_name, "noname");
+}
+
+static GMainLoop *event_loop;
+
+static unsigned int __terminated = 0;
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct signalfd_siginfo si;
+       ssize_t result;
+       int fd;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+               return FALSE;
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       result = read(fd, &si, sizeof(si));
+       if (result != sizeof(si))
+               return FALSE;
+
+       switch (si.ssi_signo) {
+       case SIGINT:
+       case SIGTERM:
+               if (__terminated == 0) {
+                       info("Terminating");
+                       g_main_loop_quit(event_loop);
+               }
+
+               __terminated = 1;
+               break;
+       case SIGUSR2:
+               __btd_toggle_debug();
+               break;
+       case SIGPIPE:
+               /* ignore */
+               break;
+       }
+
+       return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+       GIOChannel *channel;
+       guint source;
+       sigset_t mask;
+       int fd;
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+       sigaddset(&mask, SIGUSR2);
+       sigaddset(&mask, SIGPIPE);
+
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+               perror("Failed to set signal mask");
+               return 0;
+       }
+
+       fd = signalfd(-1, &mask, 0);
+       if (fd < 0) {
+               perror("Failed to create signal descriptor");
+               return 0;
+       }
+
+       channel = g_io_channel_unix_new(fd);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       source = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               signal_handler, NULL);
+
+       g_io_channel_unref(channel);
+
+       return source;
+}
+
+static gchar *option_debug = NULL;
+static gchar *option_plugin = NULL;
+static gchar *option_noplugin = NULL;
+static gboolean option_detach = TRUE;
+static gboolean option_version = FALSE;
+static gboolean option_udev = FALSE;
+
+static guint last_adapter_timeout = 0;
+
+static gboolean exit_timeout(gpointer data)
+{
+       g_main_loop_quit(event_loop);
+       last_adapter_timeout = 0;
+       return FALSE;
+}
+
+void btd_start_exit_timer(void)
+{
+       if (option_udev == FALSE)
+               return;
+
+       if (last_adapter_timeout > 0)
+               g_source_remove(last_adapter_timeout);
+
+       last_adapter_timeout = g_timeout_add_seconds(LAST_ADAPTER_EXIT_TIMEOUT,
+                                               exit_timeout, NULL);
+}
+
+void btd_stop_exit_timer(void)
+{
+       if (last_adapter_timeout == 0)
+               return;
+
+       g_source_remove(last_adapter_timeout);
+       last_adapter_timeout = 0;
+}
+
+static void disconnect_dbus(void)
+{
+       DBusConnection *conn = get_dbus_connection();
+
+       if (!conn || !dbus_connection_get_is_connected(conn))
+               return;
+
+       manager_cleanup(conn, "/");
+
+       set_dbus_connection(NULL);
+
+       dbus_connection_unref(conn);
+}
+
+static int connect_dbus(void)
+{
+       DBusConnection *conn;
+       DBusError err;
+
+       dbus_error_init(&err);
+
+       conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, BLUEZ_NAME, &err);
+       if (!conn) {
+               if (dbus_error_is_set(&err)) {
+                       g_printerr("D-Bus setup failed: %s\n", err.message);
+                       dbus_error_free(&err);
+                       return -EIO;
+               }
+               return -EALREADY;
+       }
+
+       if (!manager_init(conn, "/"))
+               return -EIO;
+
+       set_dbus_connection(conn);
+
+       return 0;
+}
+
+static gboolean parse_debug(const char *key, const char *value,
+                               gpointer user_data, GError **error)
+{
+       if (value)
+               option_debug = g_strdup(value);
+       else
+               option_debug = g_strdup("*");
+
+       return TRUE;
+}
+
+static GOptionEntry options[] = {
+       { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
+                               G_OPTION_ARG_CALLBACK, parse_debug,
+                               "Specify debug options to enable", "DEBUG" },
+       { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
+                               "Specify plugins to load", "NAME,..," },
+       { "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
+                               "Specify plugins not to load", "NAME,..." },
+       { "nodetach", 'n', G_OPTION_FLAG_REVERSE,
+                               G_OPTION_ARG_NONE, &option_detach,
+                               "Don't run as daemon in background" },
+       { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+                               "Show version information and exit" },
+       { "udev", 'u', 0, G_OPTION_ARG_NONE, &option_udev,
+                               "Run from udev mode of operation" },
+       { NULL },
+};
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+       GError *err = NULL;
+       uint16_t mtu = 0;
+       GKeyFile *config;
+       guint signal;
+
+       init_defaults();
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
+               if (err != NULL) {
+                       g_printerr("%s\n", err->message);
+                       g_error_free(err);
+               } else
+                       g_printerr("An unknown error occurred\n");
+               exit(1);
+       }
+
+       g_option_context_free(context);
+
+       if (option_version == TRUE) {
+               printf("%s\n", VERSION);
+               exit(0);
+       }
+
+       if (option_udev == TRUE) {
+               int err;
+
+               option_detach = TRUE;
+               err = connect_dbus();
+               if (err < 0) {
+                       if (err == -EALREADY)
+                               exit(0);
+                       exit(1);
+               }
+       }
+
+       if (option_detach == TRUE && option_udev == FALSE) {
+               if (daemon(0, 0)) {
+                       perror("Can't start daemon");
+                       exit(1);
+               }
+       }
+
+       umask(0077);
+
+       event_loop = g_main_loop_new(NULL, FALSE);
+
+       signal = setup_signalfd();
+
+       __btd_log_init(option_debug, option_detach);
+
+       config = load_config(CONFIGDIR "/main.conf");
+
+       parse_config(config);
+
+       agent_init();
+
+       if (option_udev == FALSE) {
+               if (connect_dbus() < 0) {
+                       error("Unable to get on D-Bus");
+                       exit(1);
+               }
+       } else {
+               if (daemon(0, 0)) {
+                       perror("Can't start daemon");
+                       exit(1);
+               }
+       }
+
+       start_sdp_server(mtu, SDP_SERVER_COMPAT);
+
+       /* Loading plugins has to be done after D-Bus has been setup since
+        * the plugins might wanna expose some paths on the bus. However the
+        * best order of how to init various subsystems of the Bluetooth
+        * daemon needs to be re-worked. */
+       plugin_init(config, option_plugin, option_noplugin);
+
+       if (adapter_ops_setup() < 0) {
+               error("adapter_ops_setup failed");
+               exit(1);
+       }
+
+       rfkill_init();
+
+       DBG("Entering main loop");
+
+       g_main_loop_run(event_loop);
+
+       g_source_remove(signal);
+
+       disconnect_dbus();
+
+       rfkill_exit();
+
+       plugin_cleanup();
+
+       stop_sdp_server();
+
+       agent_exit();
+
+       g_main_loop_unref(event_loop);
+
+       if (config)
+               g_key_file_free(config);
+
+       info("Exit");
+
+       __btd_log_cleanup();
+
+       return 0;
+}
diff --git a/src/main.conf b/src/main.conf
new file mode 100644 (file)
index 0000000..787ef4f
--- /dev/null
@@ -0,0 +1,64 @@
+[General]
+
+# List of plugins that should not be loaded on bluetoothd startup
+#DisablePlugins = network,input
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered.
+Class = 0x000100
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+PairableTimeout = 0
+
+# Use some other page timeout than the controller default one
+# which is 16384 (10 seconds).
+PageTimeout = 8192
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels.
+AutoConnectTimeout = 60
+
+# What value should be assumed for the adapter Powered property when
+# SetProperty(Powered, ...) hasn't been called yet. Defaults to true
+InitiallyPowered = true
+
+# Remember the previously stored Powered state when initializing adapters
+RememberPowered = true
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to true.
+ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+DebugKeys = false
+
+# Enable the GATT functionality. Default is false
+EnableGatt = false
diff --git a/src/manager.c b/src/manager.c
new file mode 100644 (file)
index 0000000..385354d
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include <gdbus.h>
+
+#include "glib-helper.h"
+#include "hcid.h"
+#include "dbus-common.h"
+#include "log.h"
+#include "adapter.h"
+#include "error.h"
+#include "manager.h"
+
+static char base_path[50] = "/org/bluez";
+
+static DBusConnection *connection = NULL;
+static int default_adapter_id = -1;
+static GSList *adapters = NULL;
+
+const char *manager_get_base_path(void)
+{
+       return base_path;
+}
+
+static DBusMessage *default_adapter(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       struct btd_adapter *adapter;
+       const gchar *path;
+
+       adapter = manager_find_adapter_by_id(default_adapter_id);
+       if (!adapter)
+               return btd_error_no_such_adapter(msg);
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       path = adapter_get_path(adapter);
+
+       dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *find_adapter(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       struct btd_adapter *adapter;
+       const char *pattern;
+       int dev_id;
+       const gchar *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+                                                       DBUS_TYPE_INVALID))
+               return NULL;
+
+       /* hci_devid() would make sense to use here, except it is
+        * restricted to devices which are up */
+       if (!strcmp(pattern, "any") || !strcmp(pattern, "00:00:00:00:00:00")) {
+               path = adapter_any_get_path();
+               if (path != NULL)
+                       goto done;
+               return btd_error_no_such_adapter(msg);
+       } else if (!strncmp(pattern, "hci", 3) && strlen(pattern) >= 4) {
+               dev_id = atoi(pattern + 3);
+               adapter = manager_find_adapter_by_id(dev_id);
+       } else {
+               bdaddr_t bdaddr;
+               str2ba(pattern, &bdaddr);
+               adapter = manager_find_adapter(&bdaddr);
+       }
+
+       if (!adapter)
+               return btd_error_no_such_adapter(msg);
+
+       path = adapter_get_path(adapter);
+
+done:
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+static DBusMessage *list_adapters(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter;
+       DBusMessageIter array_iter;
+       DBusMessage *reply;
+       GSList *l;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                               DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
+
+       for (l = adapters; l; l = l->next) {
+               struct btd_adapter *adapter = l->data;
+               const gchar *path = adapter_get_path(adapter);
+
+               dbus_message_iter_append_basic(&array_iter,
+                                       DBUS_TYPE_OBJECT_PATH, &path);
+       }
+
+       dbus_message_iter_close_container(&iter, &array_iter);
+
+       return reply;
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       GSList *list;
+       char **array;
+       int i;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       array = g_new0(char *, g_slist_length(adapters) + 1);
+       for (i = 0, list = adapters; list; list = list->next) {
+               struct btd_adapter *adapter = list->data;
+
+               array[i] = (char *) adapter_get_path(adapter);
+               i++;
+       }
+       dict_append_array(&dict, "Adapters", DBUS_TYPE_OBJECT_PATH, &array, i);
+       g_free(array);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static const GDBusMethodTable manager_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_METHOD("DefaultAdapter",
+                       NULL, GDBUS_ARGS({ "adapter", "o" }),
+                       default_adapter) },
+       { GDBUS_METHOD("FindAdapter",
+                       GDBUS_ARGS({ "pattern", "s" }),
+                       GDBUS_ARGS({ "adapter", "o" }),
+                       find_adapter) },
+       { GDBUS_ASYNC_METHOD("ListAdapters",
+                       NULL, GDBUS_ARGS({ "adapters", "ao" }),
+                       list_adapters) },
+       { }
+};
+
+static const GDBusSignalTable manager_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { GDBUS_SIGNAL("AdapterAdded",
+                       GDBUS_ARGS({ "adapter", "o" })) },
+       { GDBUS_SIGNAL("AdapterRemoved",
+                       GDBUS_ARGS({ "adapter", "o" })) },
+       { GDBUS_SIGNAL("DefaultAdapterChanged",
+                       GDBUS_ARGS({ "adapter", "o" })) },
+       { }
+};
+
+dbus_bool_t manager_init(DBusConnection *conn, const char *path)
+{
+       connection = conn;
+
+       snprintf(base_path, sizeof(base_path), "/org/bluez/%d", getpid());
+
+       return g_dbus_register_interface(conn, "/", MANAGER_INTERFACE,
+                                       manager_methods, manager_signals,
+                                       NULL, NULL, NULL);
+}
+
+static void manager_update_adapters(void)
+{
+       GSList *list;
+       char **array;
+       int i;
+
+       array = g_new0(char *, g_slist_length(adapters) + 1);
+       for (i = 0, list = adapters; list; list = list->next) {
+               struct btd_adapter *adapter = list->data;
+
+               array[i] = (char *) adapter_get_path(adapter);
+               i++;
+       }
+
+       emit_array_property_changed(connection, "/",
+                                       MANAGER_INTERFACE, "Adapters",
+                                       DBUS_TYPE_OBJECT_PATH, &array, i);
+
+       g_free(array);
+}
+
+static void manager_set_default_adapter(int id)
+{
+       struct btd_adapter *adapter;
+       const gchar *path;
+
+       default_adapter_id = id;
+
+       adapter = manager_find_adapter_by_id(id);
+       if (!adapter)
+               return;
+
+       path = adapter_get_path(adapter);
+
+       g_dbus_emit_signal(connection, "/",
+                       MANAGER_INTERFACE,
+                       "DefaultAdapterChanged",
+                       DBUS_TYPE_OBJECT_PATH, &path,
+                       DBUS_TYPE_INVALID);
+}
+
+struct btd_adapter *manager_get_default_adapter(void)
+{
+       return manager_find_adapter_by_id(default_adapter_id);
+}
+
+static void manager_remove_adapter(struct btd_adapter *adapter)
+{
+       uint16_t dev_id = adapter_get_dev_id(adapter);
+       const gchar *path = adapter_get_path(adapter);
+
+       adapters = g_slist_remove(adapters, adapter);
+
+       manager_update_adapters();
+
+       if (default_adapter_id == dev_id || default_adapter_id < 0) {
+               int new_default = hci_get_route(NULL);
+
+               manager_set_default_adapter(new_default);
+       }
+
+       g_dbus_emit_signal(connection, "/",
+                       MANAGER_INTERFACE, "AdapterRemoved",
+                       DBUS_TYPE_OBJECT_PATH, &path,
+                       DBUS_TYPE_INVALID);
+
+       adapter_remove(adapter);
+       btd_adapter_unref(adapter);
+
+       if (adapters == NULL)
+               btd_start_exit_timer();
+}
+
+void manager_cleanup(DBusConnection *conn, const char *path)
+{
+       while (adapters) {
+               struct btd_adapter *adapter = adapters->data;
+
+               adapters = g_slist_remove(adapters, adapter);
+               adapter_remove(adapter);
+               btd_adapter_unref(adapter);
+       }
+
+       btd_start_exit_timer();
+
+       g_dbus_unregister_interface(conn, "/", MANAGER_INTERFACE);
+}
+
+static gint adapter_id_cmp(gconstpointer a, gconstpointer b)
+{
+       struct btd_adapter *adapter = (struct btd_adapter *) a;
+       uint16_t id = GPOINTER_TO_UINT(b);
+       uint16_t dev_id = adapter_get_dev_id(adapter);
+
+       return dev_id == id ? 0 : -1;
+}
+
+static gint adapter_cmp(gconstpointer a, gconstpointer b)
+{
+       struct btd_adapter *adapter = (struct btd_adapter *) a;
+       const bdaddr_t *bdaddr = b;
+       bdaddr_t src;
+
+       adapter_get_address(adapter, &src);
+
+       return bacmp(&src, bdaddr);
+}
+
+struct btd_adapter *manager_find_adapter(const bdaddr_t *sba)
+{
+       GSList *match;
+
+       match = g_slist_find_custom(adapters, sba, adapter_cmp);
+       if (!match)
+               return NULL;
+
+       return match->data;
+}
+
+struct btd_adapter *manager_find_adapter_by_id(int id)
+{
+       GSList *match;
+
+       match = g_slist_find_custom(adapters, GINT_TO_POINTER(id),
+                                                       adapter_id_cmp);
+       if (!match)
+               return NULL;
+
+       return match->data;
+}
+
+void manager_foreach_adapter(adapter_cb func, gpointer user_data)
+{
+       g_slist_foreach(adapters, (GFunc) func, user_data);
+}
+
+GSList *manager_get_adapters(void)
+{
+       return adapters;
+}
+
+void manager_add_adapter(const char *path)
+{
+       g_dbus_emit_signal(connection, "/",
+                               MANAGER_INTERFACE, "AdapterAdded",
+                               DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+
+       manager_update_adapters();
+
+       btd_stop_exit_timer();
+}
+
+struct btd_adapter *btd_manager_register_adapter(int id, gboolean up)
+{
+       struct btd_adapter *adapter;
+       const char *path;
+
+       adapter = manager_find_adapter_by_id(id);
+       if (adapter) {
+               error("Unable to register adapter: hci%d already exist", id);
+               return NULL;
+       }
+
+       adapter = adapter_create(connection, id);
+       if (!adapter)
+               return NULL;
+
+       adapters = g_slist_append(adapters, adapter);
+
+       if (!adapter_init(adapter, up)) {
+               adapters = g_slist_remove(adapters, adapter);
+               btd_adapter_unref(adapter);
+               return NULL;
+       }
+
+       path = adapter_get_path(adapter);
+       g_dbus_emit_signal(connection, "/",
+                               MANAGER_INTERFACE, "AdapterAdded",
+                               DBUS_TYPE_OBJECT_PATH, &path,
+                               DBUS_TYPE_INVALID);
+
+       manager_update_adapters();
+
+       btd_stop_exit_timer();
+
+       if (default_adapter_id < 0)
+               manager_set_default_adapter(id);
+
+       if (main_opts.did_source)
+               btd_adapter_set_did(adapter, main_opts.did_vendor,
+                                               main_opts.did_product,
+                                               main_opts.did_version,
+                                               main_opts.did_source);
+
+       DBG("Adapter %s registered", path);
+
+       return btd_adapter_ref(adapter);
+}
+
+int btd_manager_unregister_adapter(int id)
+{
+       struct btd_adapter *adapter;
+       const gchar *path;
+
+       adapter = manager_find_adapter_by_id(id);
+       if (!adapter)
+               return -1;
+
+       path = adapter_get_path(adapter);
+
+       info("Unregister path: %s", path);
+
+       manager_remove_adapter(adapter);
+
+       return 0;
+}
diff --git a/src/manager.h b/src/manager.h
new file mode 100644 (file)
index 0000000..264cd25
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <bluetooth/bluetooth.h>
+#include <dbus/dbus.h>
+
+#define MANAGER_INTERFACE "org.bluez.Manager"
+
+typedef void (*adapter_cb) (struct btd_adapter *adapter, gpointer user_data);
+
+dbus_bool_t manager_init(DBusConnection *conn, const char *path);
+void manager_cleanup(DBusConnection *conn, const char *path);
+
+const char *manager_get_base_path(void);
+struct btd_adapter *manager_find_adapter(const bdaddr_t *sba);
+struct btd_adapter *manager_find_adapter_by_id(int id);
+struct btd_adapter *manager_get_default_adapter(void);
+void manager_foreach_adapter(adapter_cb func, gpointer user_data);
+GSList *manager_get_adapters(void);
+struct btd_adapter *btd_manager_register_adapter(int id, gboolean up);
+int btd_manager_unregister_adapter(int id);
+void manager_add_adapter(const char *path);
diff --git a/src/oob.c b/src/oob.c
new file mode 100644 (file)
index 0000000..75798fb
--- /dev/null
+++ b/src/oob.c
@@ -0,0 +1,41 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  ST-Ericsson SA
+ *
+ *  Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "adapter.h"
+#include "oob.h"
+
+static oob_read_cb_t local_oob_read_cb = NULL;
+
+void oob_register_cb(oob_read_cb_t cb)
+{
+       local_oob_read_cb = cb;
+}
+
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+                                                       uint8_t *randomizer)
+{
+       if (local_oob_read_cb)
+               local_oob_read_cb(adapter, hash, randomizer);
+}
diff --git a/src/oob.h b/src/oob.h
new file mode 100644 (file)
index 0000000..5805082
--- /dev/null
+++ b/src/oob.h
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  ST-Ericsson SA
+ *
+ *  Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef void (*oob_read_cb_t) (struct btd_adapter *adapter, uint8_t *hash,
+                                                       uint8_t *randomizer);
+
+void oob_register_cb(oob_read_cb_t cb);
+
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+                                                       uint8_t *randomizer);
diff --git a/src/org.bluez.service b/src/org.bluez.service
new file mode 100644 (file)
index 0000000..dd7ae8f
--- /dev/null
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=org.bluez
+Exec=/bin/false
+User=root
+SystemdService=dbus-org.bluez.service
diff --git a/src/oui.c b/src/oui.c
new file mode 100644 (file)
index 0000000..80bb3d3
--- /dev/null
+++ b/src/oui.c
@@ -0,0 +1,101 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include "oui.h"
+
+/* http://standards.ieee.org/regauth/oui/oui.txt */
+
+char *ouitocomp(const char *oui)
+{
+       struct stat st;
+       char *str, *map, *off, *end;
+       int fd;
+
+       fd = open(OUIFILE, O_RDONLY);
+       if (fd < 0)
+               return NULL;
+
+       if (fstat(fd, &st) < 0) {
+               close(fd);
+               return NULL;
+       }
+
+       str = malloc(128);
+       if (!str) {
+               close(fd);
+               return NULL;
+       }
+
+       memset(str, 0, 128);
+
+       map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+       if (!map || map == MAP_FAILED) {
+               free(str);
+               close(fd);
+               return NULL;
+       }
+
+       off = strstr(map, oui);
+       if (off) {
+               off += 18;
+               end = strpbrk(off, "\r\n");
+               strncpy(str, off, end - off);
+       } else {
+               free(str);
+               str = NULL;
+       }
+
+       munmap(map, st.st_size);
+
+       close(fd);
+
+       return str;
+}
+
+int oui2comp(const char *oui, char *comp, size_t size)
+{
+       char *tmp;
+
+       tmp = ouitocomp(oui);
+       if (!tmp)
+               return -1;
+
+       snprintf(comp, size, "%s", tmp);
+
+       free(tmp);
+
+       return 0;
+}
diff --git a/src/oui.h b/src/oui.h
new file mode 100644 (file)
index 0000000..ad66e56
--- /dev/null
+++ b/src/oui.h
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+char *ouitocomp(const char *oui);
+int oui2comp(const char *oui, char *comp, size_t size);
diff --git a/src/plugin.c b/src/plugin.c
new file mode 100644 (file)
index 0000000..2a86e68
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include <glib.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "hcid.h"
+#include "btio.h"
+
+static GSList *plugins = NULL;
+
+struct bluetooth_plugin {
+       void *handle;
+       gboolean active;
+       struct bluetooth_plugin_desc *desc;
+};
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+       const struct bluetooth_plugin *plugin1 = a;
+       const struct bluetooth_plugin *plugin2 = b;
+
+       return plugin2->desc->priority - plugin1->desc->priority;
+}
+
+static gboolean add_plugin(void *handle, struct bluetooth_plugin_desc *desc)
+{
+       struct bluetooth_plugin *plugin;
+
+       if (desc->init == NULL)
+               return FALSE;
+
+       if (g_str_equal(desc->version, VERSION) == FALSE) {
+               error("Version mismatch for %s", desc->name);
+               return FALSE;
+       }
+
+       DBG("Loading %s plugin", desc->name);
+
+       plugin = g_try_new0(struct bluetooth_plugin, 1);
+       if (plugin == NULL)
+               return FALSE;
+
+       plugin->handle = handle;
+       plugin->active = FALSE;
+       plugin->desc = desc;
+
+       __btd_enable_debug(desc->debug_start, desc->debug_stop);
+
+       plugins = g_slist_insert_sorted(plugins, plugin, compare_priority);
+
+       return TRUE;
+}
+
+static gboolean enable_plugin(const char *name, char **conf_disable,
+                                       char **cli_enable, char **cli_disable)
+{
+       if (conf_disable) {
+               for (; *conf_disable; conf_disable++)
+                       if (g_pattern_match_simple(*conf_disable, name))
+                               break;
+               if (*conf_disable) {
+                       info("Excluding (conf) %s", name);
+                       return FALSE;
+               }
+       }
+
+       if (cli_disable) {
+               for (; *cli_disable; cli_disable++)
+                       if (g_pattern_match_simple(*cli_disable, name))
+                               break;
+               if (*cli_disable) {
+                       info("Excluding (cli) %s", name);
+                       return FALSE;
+               }
+       }
+
+       if (cli_enable) {
+               for (; *cli_enable; cli_enable++)
+                       if (g_pattern_match_simple(*cli_enable, name))
+                               break;
+               if (!*cli_enable) {
+                       info("Ignoring (cli) %s", name);
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+#include "builtin.h"
+
+gboolean plugin_init(GKeyFile *config, const char *enable, const char *disable)
+{
+       GSList *list;
+       GDir *dir;
+       const gchar *file;
+       char **conf_disabled, **cli_disabled, **cli_enabled;
+       unsigned int i;
+
+       /* Make a call to BtIO API so its symbols got resolved before the
+        * plugins are loaded. */
+       bt_io_error_quark();
+
+       if (config)
+               conf_disabled = g_key_file_get_string_list(config, "General",
+                                                       "DisablePlugins",
+                                                       NULL, NULL);
+       else
+               conf_disabled = NULL;
+
+       if (enable)
+               cli_enabled = g_strsplit_set(enable, ", ", -1);
+       else
+               cli_enabled = NULL;
+
+       if (disable)
+               cli_disabled = g_strsplit_set(disable, ", ", -1);
+       else
+               cli_disabled = NULL;
+
+       DBG("Loading builtin plugins");
+
+       for (i = 0; __bluetooth_builtin[i]; i++) {
+               if (!enable_plugin(__bluetooth_builtin[i]->name, conf_disabled,
+                                               cli_enabled, cli_disabled))
+                       continue;
+
+               add_plugin(NULL,  __bluetooth_builtin[i]);
+       }
+
+       if (strlen(PLUGINDIR) == 0)
+               goto start;
+
+       DBG("Loading plugins %s", PLUGINDIR);
+
+       dir = g_dir_open(PLUGINDIR, 0, NULL);
+       if (!dir)
+               goto start;
+
+       while ((file = g_dir_read_name(dir)) != NULL) {
+               struct bluetooth_plugin_desc *desc;
+               void *handle;
+               gchar *filename;
+
+               if (g_str_has_prefix(file, "lib") == TRUE ||
+                               g_str_has_suffix(file, ".so") == FALSE)
+                       continue;
+
+               filename = g_build_filename(PLUGINDIR, file, NULL);
+
+               handle = dlopen(filename, RTLD_NOW);
+               if (handle == NULL) {
+                       error("Can't load plugin %s: %s", filename,
+                                                               dlerror());
+                       g_free(filename);
+                       continue;
+               }
+
+               g_free(filename);
+
+               desc = dlsym(handle, "bluetooth_plugin_desc");
+               if (desc == NULL) {
+                       error("Can't load plugin description: %s", dlerror());
+                       dlclose(handle);
+                       continue;
+               }
+
+               if (!enable_plugin(desc->name, conf_disabled,
+                                               cli_enabled, cli_disabled)) {
+                       dlclose(handle);
+                       continue;
+               }
+
+               if (add_plugin(handle, desc) == FALSE)
+                       dlclose(handle);
+       }
+
+       g_dir_close(dir);
+
+start:
+       for (list = plugins; list; list = list->next) {
+               struct bluetooth_plugin *plugin = list->data;
+
+               if (plugin->desc->init() < 0) {
+                       error("Failed to init %s plugin", plugin->desc->name);
+                       continue;
+               }
+
+               plugin->active = TRUE;
+       }
+
+       g_strfreev(conf_disabled);
+       g_strfreev(cli_enabled);
+       g_strfreev(cli_disabled);
+
+       return TRUE;
+}
+
+void plugin_cleanup(void)
+{
+       GSList *list;
+
+       DBG("Cleanup plugins");
+
+       for (list = plugins; list; list = list->next) {
+               struct bluetooth_plugin *plugin = list->data;
+
+               if (plugin->active == TRUE && plugin->desc->exit)
+                       plugin->desc->exit();
+
+               if (plugin->handle != NULL)
+                       dlclose(plugin->handle);
+
+               g_free(plugin);
+       }
+
+       g_slist_free(plugins);
+}
diff --git a/src/plugin.h b/src/plugin.h
new file mode 100644 (file)
index 0000000..89c7b85
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#define BLUETOOTH_PLUGIN_PRIORITY_LOW      -100
+#define BLUETOOTH_PLUGIN_PRIORITY_DEFAULT     0
+#define BLUETOOTH_PLUGIN_PRIORITY_HIGH      100
+
+struct bluetooth_plugin_desc {
+       const char *name;
+       const char *version;
+       int priority;
+       int (*init) (void);
+       void (*exit) (void);
+       void *debug_start;
+       void *debug_stop;
+};
+
+#ifdef BLUETOOTH_PLUGIN_BUILTIN
+#define BLUETOOTH_PLUGIN_DEFINE(name, version, priority, init, exit) \
+               struct bluetooth_plugin_desc __bluetooth_builtin_ ## name = { \
+                       #name, version, priority, init, exit \
+               };
+#else
+#define BLUETOOTH_PLUGIN_DEFINE(name, version, priority, init, exit) \
+               extern struct btd_debug_desc __start___debug[] \
+                               __attribute__ ((weak, visibility("hidden"))); \
+               extern struct btd_debug_desc __stop___debug[] \
+                               __attribute__ ((weak, visibility("hidden"))); \
+               extern struct bluetooth_plugin_desc bluetooth_plugin_desc \
+                               __attribute__ ((visibility("default"))); \
+               struct bluetooth_plugin_desc bluetooth_plugin_desc = { \
+                       #name, version, priority, init, exit, \
+                       __start___debug, __stop___debug \
+               };
+#endif
diff --git a/src/ppoll.h b/src/ppoll.h
new file mode 100644 (file)
index 0000000..7d09d44
--- /dev/null
@@ -0,0 +1,16 @@
+#ifdef ppoll
+#undef ppoll
+#endif
+
+#define ppoll compat_ppoll
+
+static inline int compat_ppoll(struct pollfd *fds, nfds_t nfds,
+               const struct timespec *timeout, const sigset_t *sigmask)
+{
+       if (timeout == NULL)
+               return poll(fds, nfds, -1);
+       else if (timeout->tv_sec == 0)
+               return poll(fds, nfds, 500);
+       else
+               return poll(fds, nfds, timeout->tv_sec * 1000);
+}
diff --git a/src/rfkill.c b/src/rfkill.c
new file mode 100644 (file)
index 0000000..b40c6e7
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "adapter.h"
+#include "manager.h"
+#include "hcid.h"
+
+enum rfkill_type {
+       RFKILL_TYPE_ALL = 0,
+       RFKILL_TYPE_WLAN,
+       RFKILL_TYPE_BLUETOOTH,
+       RFKILL_TYPE_UWB,
+       RFKILL_TYPE_WIMAX,
+       RFKILL_TYPE_WWAN,
+};
+
+enum rfkill_operation {
+       RFKILL_OP_ADD = 0,
+       RFKILL_OP_DEL,
+       RFKILL_OP_CHANGE,
+       RFKILL_OP_CHANGE_ALL,
+};
+
+struct rfkill_event {
+       uint32_t idx;
+       uint8_t  type;
+       uint8_t  op;
+       uint8_t  soft;
+       uint8_t  hard;
+};
+
+static gboolean rfkill_event(GIOChannel *chan,
+                               GIOCondition cond, gpointer data)
+{
+       unsigned char buf[32];
+       struct rfkill_event *event = (void *) buf;
+       struct btd_adapter *adapter;
+       char sysname[PATH_MAX];
+       ssize_t len;
+       int fd, id;
+
+       if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+               return FALSE;
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       memset(buf, 0, sizeof(buf));
+
+       len = read(fd, buf, sizeof(buf));
+       if (len < 0) {
+               if (errno == EAGAIN)
+                       return TRUE;
+               return FALSE;
+       }
+
+       if (len != sizeof(struct rfkill_event))
+               return TRUE;
+
+       DBG("RFKILL event idx %u type %u op %u soft %u hard %u",
+                                       event->idx, event->type, event->op,
+                                               event->soft, event->hard);
+
+       if (event->soft || event->hard)
+               return TRUE;
+
+       if (event->op != RFKILL_OP_CHANGE)
+               return TRUE;
+
+       if (event->type != RFKILL_TYPE_BLUETOOTH &&
+                                       event->type != RFKILL_TYPE_ALL)
+               return TRUE;
+
+       snprintf(sysname, sizeof(sysname) - 1,
+                       "/sys/class/rfkill/rfkill%u/name", event->idx);
+
+       fd = open(sysname, O_RDONLY);
+       if (fd < 0)
+               return TRUE;
+
+       memset(sysname, 0, sizeof(sysname));
+
+       if (read(fd, sysname, sizeof(sysname)) < 4) {
+               close(fd);
+               return TRUE;
+       }
+
+       close(fd);
+
+       if (g_str_has_prefix(sysname, "hci") == FALSE)
+               return TRUE;
+
+       id = atoi(sysname + 3);
+       if (id < 0)
+               return TRUE;
+
+       adapter = manager_find_adapter_by_id(id);
+       if (!adapter)
+               return TRUE;
+
+       DBG("RFKILL unblock for hci%d", id);
+
+       btd_adapter_restore_powered(adapter);
+
+       return TRUE;
+}
+
+static GIOChannel *channel = NULL;
+
+void rfkill_init(void)
+{
+       int fd;
+
+       if (!main_opts.remember_powered)
+               return;
+
+       fd = open("/dev/rfkill", O_RDWR);
+       if (fd < 0) {
+               error("Failed to open RFKILL control device");
+               return;
+       }
+
+       channel = g_io_channel_unix_new(fd);
+       g_io_channel_set_close_on_unref(channel, TRUE);
+
+       g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+                                                       rfkill_event, NULL);
+}
+
+void rfkill_exit(void)
+{
+       if (!channel)
+               return;
+
+       g_io_channel_shutdown(channel, TRUE, NULL);
+       g_io_channel_unref(channel);
+
+       channel = NULL;
+}
diff --git a/src/sdp-client.c b/src/sdp-client.c
new file mode 100644 (file)
index 0000000..55e59c2
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include "btio.h"
+#include "sdp-client.h"
+
+/* Number of seconds to keep a sdp_session_t in the cache */
+#define CACHE_TIMEOUT 2
+
+struct cached_sdp_session {
+       bdaddr_t src;
+       bdaddr_t dst;
+       sdp_session_t *session;
+       guint timer;
+};
+
+static GSList *cached_sdp_sessions = NULL;
+
+static gboolean cached_session_expired(gpointer user_data)
+{
+       struct cached_sdp_session *cached = user_data;
+
+       cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, cached);
+
+       sdp_close(cached->session);
+
+       g_free(cached);
+
+       return FALSE;
+}
+
+static sdp_session_t *get_cached_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       GSList *l;
+
+       for (l = cached_sdp_sessions; l != NULL; l = l->next) {
+               struct cached_sdp_session *c = l->data;
+               sdp_session_t *session;
+
+               if (bacmp(&c->src, src) || bacmp(&c->dst, dst))
+                       continue;
+
+               g_source_remove(c->timer);
+
+               session = c->session;
+
+               cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, c);
+               g_free(c);
+
+               return session;
+       }
+
+       return NULL;
+}
+
+static sdp_session_t *get_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       sdp_session_t *session;
+
+       session = get_cached_sdp_session(src, dst);
+       if (session)
+               return session;
+
+       return sdp_connect(src, dst, SDP_NON_BLOCKING);
+}
+
+static void cache_sdp_session(bdaddr_t *src, bdaddr_t *dst,
+                                               sdp_session_t *session)
+{
+       struct cached_sdp_session *cached;
+
+       cached = g_new0(struct cached_sdp_session, 1);
+
+       bacpy(&cached->src, src);
+       bacpy(&cached->dst, dst);
+
+       cached->session = session;
+
+       cached_sdp_sessions = g_slist_append(cached_sdp_sessions, cached);
+
+       cached->timer = g_timeout_add_seconds(CACHE_TIMEOUT,
+                                               cached_session_expired,
+                                               cached);
+}
+
+struct search_context {
+       bdaddr_t                src;
+       bdaddr_t                dst;
+       sdp_session_t           *session;
+       bt_callback_t           cb;
+       bt_destroy_t            destroy;
+       gpointer                user_data;
+       uuid_t                  uuid;
+       guint                   io_id;
+};
+
+static GSList *context_list = NULL;
+
+static void search_context_cleanup(struct search_context *ctxt)
+{
+       context_list = g_slist_remove(context_list, ctxt);
+
+       if (ctxt->destroy)
+               ctxt->destroy(ctxt->user_data);
+
+       g_free(ctxt);
+}
+
+static void search_completed_cb(uint8_t type, uint16_t status,
+                       uint8_t *rsp, size_t size, void *user_data)
+{
+       struct search_context *ctxt = user_data;
+       sdp_list_t *recs = NULL;
+       int scanned, seqlen = 0, bytesleft = size;
+       uint8_t dataType;
+       int err = 0;
+
+       if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       scanned = sdp_extract_seqtype(rsp, bytesleft, &dataType, &seqlen);
+       if (!scanned || !seqlen)
+               goto done;
+
+       rsp += scanned;
+       bytesleft -= scanned;
+       do {
+               sdp_record_t *rec;
+               int recsize;
+
+               recsize = 0;
+               rec = sdp_extract_pdu(rsp, bytesleft, &recsize);
+               if (!rec)
+                       break;
+
+               if (!recsize) {
+                       sdp_record_free(rec);
+                       break;
+               }
+
+               scanned += recsize;
+               rsp += recsize;
+               bytesleft -= recsize;
+
+               recs = sdp_list_append(recs, rec);
+       } while (scanned < (ssize_t) size && bytesleft > 0);
+
+done:
+       cache_sdp_session(&ctxt->src, &ctxt->dst, ctxt->session);
+
+       if (ctxt->cb)
+               ctxt->cb(recs, err, ctxt->user_data);
+
+       if (recs)
+               sdp_list_free(recs, (sdp_free_func_t) sdp_record_free);
+
+       search_context_cleanup(ctxt);
+}
+
+static gboolean search_process_cb(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct search_context *ctxt = user_data;
+       int err = 0;
+
+       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+               err = EIO;
+               goto failed;
+       }
+
+       if (sdp_process(ctxt->session) < 0)
+               goto failed;
+
+       return TRUE;
+
+failed:
+       if (err) {
+               sdp_close(ctxt->session);
+               ctxt->session = NULL;
+
+               if (ctxt->cb)
+                       ctxt->cb(NULL, err, ctxt->user_data);
+
+               search_context_cleanup(ctxt);
+       }
+
+       return FALSE;
+}
+
+static gboolean connect_watch(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct search_context *ctxt = user_data;
+       sdp_list_t *search, *attrids;
+       uint32_t range = 0x0000ffff;
+       socklen_t len;
+       int sk, err, sk_err = 0;
+
+       sk = g_io_channel_unix_get_fd(chan);
+       ctxt->io_id = 0;
+
+       len = sizeof(sk_err);
+       if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+               err = -errno;
+       else
+               err = -sk_err;
+
+       if (err != 0)
+               goto failed;
+
+       if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) {
+               err = -EIO;
+               goto failed;
+       }
+
+       search = sdp_list_append(NULL, &ctxt->uuid);
+       attrids = sdp_list_append(NULL, &range);
+       if (sdp_service_search_attr_async(ctxt->session,
+                               search, SDP_ATTR_REQ_RANGE, attrids) < 0) {
+               sdp_list_free(attrids, NULL);
+               sdp_list_free(search, NULL);
+               err = -EIO;
+               goto failed;
+       }
+
+       sdp_list_free(attrids, NULL);
+       sdp_list_free(search, NULL);
+
+       /* Set callback responsible for update the internal SDP transaction */
+       ctxt->io_id = g_io_add_watch(chan,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               search_process_cb, ctxt);
+       return FALSE;
+
+failed:
+       sdp_close(ctxt->session);
+       ctxt->session = NULL;
+
+       if (ctxt->cb)
+               ctxt->cb(NULL, err, ctxt->user_data);
+
+       search_context_cleanup(ctxt);
+
+       return FALSE;
+}
+
+static int create_search_context(struct search_context **ctxt,
+                                       const bdaddr_t *src,
+                                       const bdaddr_t *dst,
+                                       uuid_t *uuid)
+{
+       sdp_session_t *s;
+       GIOChannel *chan;
+
+       if (!ctxt)
+               return -EINVAL;
+
+       s = get_sdp_session(src, dst);
+       if (!s)
+               return -errno;
+
+       *ctxt = g_try_malloc0(sizeof(struct search_context));
+       if (!*ctxt) {
+               sdp_close(s);
+               return -ENOMEM;
+       }
+
+       bacpy(&(*ctxt)->src, src);
+       bacpy(&(*ctxt)->dst, dst);
+       (*ctxt)->session = s;
+       (*ctxt)->uuid = *uuid;
+
+       chan = g_io_channel_unix_new(sdp_get_socket(s));
+       (*ctxt)->io_id = g_io_add_watch(chan,
+                               G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               connect_watch, *ctxt);
+       g_io_channel_unref(chan);
+
+       return 0;
+}
+
+int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
+                       uuid_t *uuid, bt_callback_t cb, void *user_data,
+                       bt_destroy_t destroy)
+{
+       struct search_context *ctxt = NULL;
+       int err;
+
+       if (!cb)
+               return -EINVAL;
+
+       err = create_search_context(&ctxt, src, dst, uuid);
+       if (err < 0)
+               return err;
+
+       ctxt->cb        = cb;
+       ctxt->destroy   = destroy;
+       ctxt->user_data = user_data;
+
+       context_list = g_slist_append(context_list, ctxt);
+
+       return 0;
+}
+
+static gint find_by_bdaddr(gconstpointer data, gconstpointer user_data)
+{
+       const struct search_context *ctxt = data, *search = user_data;
+       int ret;
+
+       ret = bacmp(&ctxt->src, &search->src);
+       if (ret != 0)
+               return ret;
+
+       return bacmp(&ctxt->dst, &search->dst);
+}
+
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       struct search_context match, *ctxt;
+       GSList *l;
+
+       memset(&match, 0, sizeof(match));
+       bacpy(&match.src, src);
+       bacpy(&match.dst, dst);
+
+       /* Ongoing SDP Discovery */
+       l = g_slist_find_custom(context_list, &match, find_by_bdaddr);
+       if (l == NULL)
+               return -ENOENT;
+
+       ctxt = l->data;
+
+       if (!ctxt->session)
+               return -ENOTCONN;
+
+       if (ctxt->io_id)
+               g_source_remove(ctxt->io_id);
+
+       if (ctxt->session)
+               sdp_close(ctxt->session);
+
+       search_context_cleanup(ctxt);
+
+       return 0;
+}
+
+void bt_clear_cached_session(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       sdp_session_t *session;
+
+       session = get_cached_sdp_session(src, dst);
+       if (session)
+               sdp_close(session);
+}
diff --git a/src/sdp-client.h b/src/sdp-client.h
new file mode 100644 (file)
index 0000000..9191594
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data);
+typedef void (*bt_destroy_t) (gpointer user_data);
+
+int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
+                       uuid_t *uuid, bt_callback_t cb, void *user_data,
+                       bt_destroy_t destroy);
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst);
+void bt_clear_cached_session(const bdaddr_t *src, const bdaddr_t *dst);
diff --git a/src/sdp-xml.c b/src/sdp-xml.c
new file mode 100644 (file)
index 0000000..d7b2aef
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "sdp-xml.h"
+
+#define STRBUFSIZE 1024
+#define MAXINDENT 64
+
+static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,
+               void *data, void (*appender)(void *, const char *))
+{
+       int i, hex;
+       char buf[STRBUFSIZE];
+       char indent[MAXINDENT];
+
+       if (!value)
+               return;
+
+       if (indent_level >= MAXINDENT)
+               indent_level = MAXINDENT - 2;
+
+       for (i = 0; i < indent_level; i++)
+               indent[i] = '\t';
+
+       indent[i] = '\0';
+       buf[STRBUFSIZE - 1] = '\0';
+
+       switch (value->dtd) {
+       case SDP_DATA_NIL:
+               appender(data, indent);
+               appender(data, "<nil/>\n");
+               break;
+
+       case SDP_BOOL:
+               appender(data, indent);
+               appender(data, "<boolean value=\"");
+               appender(data, value->val.uint8 ? "true" : "false");
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_UINT8:
+               appender(data, indent);
+               appender(data, "<uint8 value=\"");
+               snprintf(buf, STRBUFSIZE - 1, "0x%02x", value->val.uint8);
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_UINT16:
+               appender(data, indent);
+               appender(data, "<uint16 value=\"");
+               snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uint16);
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_UINT32:
+               appender(data, indent);
+               appender(data, "<uint32 value=\"");
+               snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uint32);
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_UINT64:
+               appender(data, indent);
+               appender(data, "<uint64 value=\"");
+               snprintf(buf, STRBUFSIZE - 1, "0x%016jx", value->val.uint64);
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_UINT128:
+               appender(data, indent);
+               appender(data, "<uint128 value=\"");
+
+               for (i = 0; i < 16; i++) {
+                       sprintf(&buf[i * 2], "%02x",
+                               (unsigned char) value->val.uint128.data[i]);
+               }
+
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_INT8:
+               appender(data, indent);
+               appender(data, "<int8 value=\"");
+               snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int8);
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_INT16:
+               appender(data, indent);
+               appender(data, "<int16 value=\"");
+               snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int16);
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_INT32:
+               appender(data, indent);
+               appender(data, "<int32 value=\"");
+               snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int32);
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_INT64:
+               appender(data, indent);
+               appender(data, "<int64 value=\"");
+               snprintf(buf, STRBUFSIZE - 1, "%jd", value->val.int64);
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_INT128:
+               appender(data, indent);
+               appender(data, "<int128 value=\"");
+
+               for (i = 0; i < 16; i++) {
+                       sprintf(&buf[i * 2], "%02x",
+                               (unsigned char) value->val.int128.data[i]);
+               }
+               appender(data, buf);
+
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_UUID16:
+               appender(data, indent);
+               appender(data, "<uuid value=\"");
+               snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uuid.value.uuid16);
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_UUID32:
+               appender(data, indent);
+               appender(data, "<uuid value=\"");
+               snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uuid.value.uuid32);
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_UUID128:
+               appender(data, indent);
+               appender(data, "<uuid value=\"");
+
+               snprintf(buf, STRBUFSIZE - 1,
+                        "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[0],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[1],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[2],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[3],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[4],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[5],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[6],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[7],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[8],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[9],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[10],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[11],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[12],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[13],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[14],
+                        (unsigned char) value->val.uuid.value.
+                        uuid128.data[15]);
+
+               appender(data, buf);
+               appender(data, "\" />\n");
+               break;
+
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+       case SDP_TEXT_STR32:
+       {
+               int num_chars_to_escape = 0;
+               int length = value->unitSize - 1;
+               char *strBuf = 0;
+
+               hex = 0;
+
+               for (i = 0; i < length; i++) {
+                       if (!isprint(value->val.str[i]) &&
+                                       value->val.str[i] != '\0') {
+                               hex = 1;
+                               break;
+                       }
+
+                       /* XML is evil, must do this... */
+                       if ((value->val.str[i] == '<') ||
+                                       (value->val.str[i] == '>') ||
+                                       (value->val.str[i] == '"') ||
+                                       (value->val.str[i] == '&'))
+                               num_chars_to_escape++;
+               }
+
+               appender(data, indent);
+
+               appender(data, "<text ");
+
+               if (hex) {
+                       appender(data, "encoding=\"hex\" ");
+                       strBuf = malloc(sizeof(char)
+                                                * ((value->unitSize-1) * 2 + 1));
+
+                       /* Unit Size seems to include the size for dtd
+                          It is thus off by 1
+                          This is safe for Normal strings, but not
+                          hex encoded data */
+                       for (i = 0; i < (value->unitSize-1); i++)
+                               sprintf(&strBuf[i*sizeof(char)*2],
+                                       "%02x",
+                                       (unsigned char) value->val.str[i]);
+
+                       strBuf[(value->unitSize-1) * 2] = '\0';
+               }
+               else {
+                       int j;
+                       /* escape the XML disallowed chars */
+                       strBuf = malloc(sizeof(char) *
+                                       (value->unitSize + 1 + num_chars_to_escape * 4));
+                       for (i = 0, j = 0; i < length; i++) {
+                               if (value->val.str[i] == '&') {
+                                       strBuf[j++] = '&';
+                                       strBuf[j++] = 'a';
+                                       strBuf[j++] = 'm';
+                                       strBuf[j++] = 'p';
+                               }
+                               else if (value->val.str[i] == '<') {
+                                       strBuf[j++] = '&';
+                                       strBuf[j++] = 'l';
+                                       strBuf[j++] = 't';
+                               }
+                               else if (value->val.str[i] == '>') {
+                                       strBuf[j++] = '&';
+                                       strBuf[j++] = 'g';
+                                       strBuf[j++] = 't';
+                               }
+                               else if (value->val.str[i] == '"') {
+                                       strBuf[j++] = '&';
+                                       strBuf[j++] = 'q';
+                                       strBuf[j++] = 'u';
+                                       strBuf[j++] = 'o';
+                                       strBuf[j++] = 't';
+                               }
+                               else if (value->val.str[i] == '\0') {
+                                       strBuf[j++] = ' ';
+                               } else {
+                                       strBuf[j++] = value->val.str[i];
+                               }
+                       }
+
+                       strBuf[j] = '\0';
+               }
+
+               appender(data, "value=\"");
+               appender(data, strBuf);
+               appender(data, "\" />\n");
+               free(strBuf);
+               break;
+       }
+
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_URL_STR32:
+       {
+               char *strBuf;
+
+               appender(data, indent);
+               appender(data, "<url value=\"");
+               strBuf = strndup(value->val.str, value->unitSize - 1);
+               appender(data, strBuf);
+               free(strBuf);
+               appender(data, "\" />\n");
+               break;
+       }
+
+       case SDP_SEQ8:
+       case SDP_SEQ16:
+       case SDP_SEQ32:
+               appender(data, indent);
+               appender(data, "<sequence>\n");
+
+               convert_raw_data_to_xml(value->val.dataseq,
+                                       indent_level + 1, data, appender);
+
+               appender(data, indent);
+               appender(data, "</sequence>\n");
+
+               break;
+
+       case SDP_ALT8:
+       case SDP_ALT16:
+       case SDP_ALT32:
+               appender(data, indent);
+
+               appender(data, "<alternate>\n");
+
+               convert_raw_data_to_xml(value->val.dataseq,
+                                       indent_level + 1, data, appender);
+               appender(data, indent);
+
+               appender(data, "</alternate>\n");
+
+               break;
+       }
+
+       convert_raw_data_to_xml(value->next, indent_level, data, appender);
+}
+
+struct conversion_data {
+       void *data;
+       void (*appender)(void *data, const char *);
+};
+
+static void convert_raw_attr_to_xml_func(void *val, void *data)
+{
+       struct conversion_data *cd = data;
+       sdp_data_t *value = val;
+       char buf[STRBUFSIZE];
+
+       buf[STRBUFSIZE - 1] = '\0';
+       snprintf(buf, STRBUFSIZE - 1, "\t<attribute id=\"0x%04x\">\n",
+                value->attrId);
+       cd->appender(cd->data, buf);
+
+       if (data)
+               convert_raw_data_to_xml(value, 2, cd->data, cd->appender);
+       else
+               cd->appender(cd->data, "\t\tNULL\n");
+
+       cd->appender(cd->data, "\t</attribute>\n");
+}
+
+/*
+ * Will convert the sdp record to XML.  The appender and data can be used
+ * to control where to output the record (e.g. file or a data buffer).  The
+ * appender will be called repeatedly with data and the character buffer
+ * (containing parts of the generated XML) to append.
+ */
+void convert_sdp_record_to_xml(sdp_record_t *rec,
+                       void *data, void (*appender)(void *, const char *))
+{
+       struct conversion_data cd;
+
+       cd.data = data;
+       cd.appender = appender;
+
+       if (rec && rec->attrlist) {
+               appender(data, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\n");
+               appender(data, "<record>\n");
+               sdp_list_foreach(rec->attrlist,
+                                convert_raw_attr_to_xml_func, &cd);
+               appender(data, "</record>\n");
+       }
+}
+
+static sdp_data_t *sdp_xml_parse_uuid128(const char *data)
+{
+       uint128_t val;
+       unsigned int i, j;
+
+       char buf[3];
+
+       memset(&val, 0, sizeof(val));
+
+       buf[2] = '\0';
+
+       for (j = 0, i = 0; i < strlen(data);) {
+               if (data[i] == '-') {
+                       i++;
+                       continue;
+               }
+
+               buf[0] = data[i];
+               buf[1] = data[i + 1];
+
+               val.data[j++] = strtoul(buf, 0, 16);
+               i += 2;
+       }
+
+       return sdp_data_alloc(SDP_UUID128, &val);
+}
+
+sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record)
+{
+       sdp_data_t *ret;
+       char *endptr;
+       uint32_t val;
+       uint16_t val2;
+       int len;
+
+       len = strlen(data);
+
+       if (len == 36) {
+               ret = sdp_xml_parse_uuid128(data);
+               goto result;
+       }
+
+       val = strtoll(data, &endptr, 16);
+
+       /* Couldn't parse */
+       if (*endptr != '\0')
+               return NULL;
+
+       if (val > USHRT_MAX) {
+               ret = sdp_data_alloc(SDP_UUID32, &val);
+               goto result;
+       }
+
+       val2 = val;
+
+       ret = sdp_data_alloc(SDP_UUID16, &val2);
+
+result:
+       if (record && ret)
+               sdp_pattern_add_uuid(record, &ret->val.uuid);
+
+       return ret;
+}
+
+sdp_data_t *sdp_xml_parse_int(const char * data, uint8_t dtd)
+{
+       char *endptr;
+       sdp_data_t *ret = NULL;
+
+       switch (dtd) {
+       case SDP_BOOL:
+       {
+               uint8_t val = 0;
+
+               if (!strcmp("true", data)) {
+                       val = 1;
+               }
+
+               else if (!strcmp("false", data)) {
+                       val = 0;
+               }
+               else {
+                       return NULL;
+               }
+
+               ret = sdp_data_alloc(dtd, &val);
+               break;
+       }
+
+       case SDP_INT8:
+       {
+               int8_t val = strtoul(data, &endptr, 0);
+
+               /* Failed to parse */
+               if ((endptr != data) && (*endptr != '\0'))
+                       return NULL;
+
+               ret = sdp_data_alloc(dtd, &val);
+               break;
+       }
+
+       case SDP_UINT8:
+       {
+               uint8_t val = strtoul(data, &endptr, 0);
+
+               /* Failed to parse */
+               if ((endptr != data) && (*endptr != '\0'))
+                       return NULL;
+
+               ret = sdp_data_alloc(dtd, &val);
+               break;
+       }
+
+       case SDP_INT16:
+       {
+               int16_t val = strtoul(data, &endptr, 0);
+
+               /* Failed to parse */
+               if ((endptr != data) && (*endptr != '\0'))
+                       return NULL;
+
+               ret = sdp_data_alloc(dtd, &val);
+               break;
+       }
+
+       case SDP_UINT16:
+       {
+               uint16_t val = strtoul(data, &endptr, 0);
+
+               /* Failed to parse */
+               if ((endptr != data) && (*endptr != '\0'))
+                       return NULL;
+
+               ret = sdp_data_alloc(dtd, &val);
+               break;
+       }
+
+       case SDP_INT32:
+       {
+               int32_t val = strtoul(data, &endptr, 0);
+
+               /* Failed to parse */
+               if ((endptr != data) && (*endptr != '\0'))
+                       return NULL;
+
+               ret = sdp_data_alloc(dtd, &val);
+               break;
+       }
+
+       case SDP_UINT32:
+       {
+               uint32_t val = strtoul(data, &endptr, 0);
+
+               /* Failed to parse */
+               if ((endptr != data) && (*endptr != '\0'))
+                       return NULL;
+
+               ret = sdp_data_alloc(dtd, &val);
+               break;
+       }
+
+       case SDP_INT64:
+       {
+               int64_t val = strtoull(data, &endptr, 0);
+
+               /* Failed to parse */
+               if ((endptr != data) && (*endptr != '\0'))
+                       return NULL;
+
+               ret = sdp_data_alloc(dtd, &val);
+               break;
+       }
+
+       case SDP_UINT64:
+       {
+               uint64_t val = strtoull(data, &endptr, 0);
+
+               /* Failed to parse */
+               if ((endptr != data) && (*endptr != '\0'))
+                       return NULL;
+
+               ret = sdp_data_alloc(dtd, &val);
+               break;
+       }
+
+       case SDP_INT128:
+       case SDP_UINT128:
+       {
+               uint128_t val;
+               int i = 0;
+               char buf[3];
+
+               buf[2] = '\0';
+
+               for (; i < 32; i += 2) {
+                       buf[0] = data[i];
+                       buf[1] = data[i + 1];
+
+                       val.data[i >> 1] = strtoul(buf, 0, 16);
+               }
+
+               ret = sdp_data_alloc(dtd, &val);
+               break;
+       }
+
+       };
+
+       return ret;
+}
+
+static char *sdp_xml_parse_string_decode(const char *data, char encoding, uint32_t *length)
+{
+       int len = strlen(data);
+       char *text;
+
+       if (encoding == SDP_XML_ENCODING_NORMAL) {
+               text = strdup(data);
+               *length = len;
+       } else {
+               char buf[3], *decoded;
+               int i;
+
+               decoded = malloc((len >> 1) + 1);
+
+               /* Ensure the string is a power of 2 */
+               len = (len >> 1) << 1;
+
+               buf[2] = '\0';
+
+               for (i = 0; i < len; i += 2) {
+                       buf[0] = data[i];
+                       buf[1] = data[i + 1];
+
+                       decoded[i >> 1] = strtoul(buf, 0, 16);
+               }
+
+               decoded[len >> 1] = '\0';
+               text = decoded;
+               *length = len >> 1;
+       }
+
+       return text;
+}
+
+sdp_data_t *sdp_xml_parse_url(const char *data)
+{
+       uint8_t dtd = SDP_URL_STR8;
+       char *url;
+       uint32_t length;
+       sdp_data_t *ret;
+
+       url = sdp_xml_parse_string_decode(data,
+                               SDP_XML_ENCODING_NORMAL, &length);
+
+       if (length > UCHAR_MAX)
+               dtd = SDP_URL_STR16;
+
+       ret = sdp_data_alloc_with_length(dtd, url, length);
+
+       free(url);
+
+       return ret;
+}
+
+sdp_data_t *sdp_xml_parse_text(const char *data, char encoding)
+{
+       uint8_t dtd = SDP_TEXT_STR8;
+       char *text;
+       uint32_t length;
+       sdp_data_t *ret;
+
+       text = sdp_xml_parse_string_decode(data, encoding, &length);
+
+       if (length > UCHAR_MAX)
+               dtd = SDP_TEXT_STR16;
+
+       ret = sdp_data_alloc_with_length(dtd, text, length);
+
+       free(text);
+
+       return ret;
+}
+
+sdp_data_t *sdp_xml_parse_nil(const char *data)
+{
+       return sdp_data_alloc(SDP_DATA_NIL, 0);
+}
+
+#define DEFAULT_XML_DATA_SIZE 1024
+
+struct sdp_xml_data *sdp_xml_data_alloc(void)
+{
+       struct sdp_xml_data *elem;
+
+       elem = malloc(sizeof(struct sdp_xml_data));
+       if (!elem)
+               return NULL;
+
+       memset(elem, 0, sizeof(struct sdp_xml_data));
+
+       /* Null terminate the text */
+       elem->size = DEFAULT_XML_DATA_SIZE;
+       elem->text = malloc(DEFAULT_XML_DATA_SIZE);
+       elem->text[0] = '\0';
+
+       return elem;
+}
+
+void sdp_xml_data_free(struct sdp_xml_data *elem)
+{
+       if (elem->data)
+               sdp_data_free(elem->data);
+
+       free(elem->name);
+       free(elem->text);
+       free(elem);
+}
+
+struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem)
+{
+       char *newbuf;
+
+       newbuf = malloc(elem->size * 2);
+       if (!newbuf)
+               return NULL;
+
+       memcpy(newbuf, elem->text, elem->size);
+       elem->size *= 2;
+       free(elem->text);
+
+       elem->text = newbuf;
+
+       return elem;
+}
+
+sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem,
+                                                       sdp_record_t *record)
+{
+       const char *data = elem->text;
+
+       if (!strcmp(el, "boolean"))
+               return sdp_xml_parse_int(data, SDP_BOOL);
+       else if (!strcmp(el, "uint8"))
+               return sdp_xml_parse_int(data, SDP_UINT8);
+       else if (!strcmp(el, "uint16"))
+               return sdp_xml_parse_int(data, SDP_UINT16);
+       else if (!strcmp(el, "uint32"))
+               return sdp_xml_parse_int(data, SDP_UINT32);
+       else if (!strcmp(el, "uint64"))
+               return sdp_xml_parse_int(data, SDP_UINT64);
+       else if (!strcmp(el, "uint128"))
+               return sdp_xml_parse_int(data, SDP_UINT128);
+       else if (!strcmp(el, "int8"))
+               return sdp_xml_parse_int(data, SDP_INT8);
+       else if (!strcmp(el, "int16"))
+               return sdp_xml_parse_int(data, SDP_INT16);
+       else if (!strcmp(el, "int32"))
+               return sdp_xml_parse_int(data, SDP_INT32);
+       else if (!strcmp(el, "int64"))
+               return sdp_xml_parse_int(data, SDP_INT64);
+       else if (!strcmp(el, "int128"))
+               return sdp_xml_parse_int(data, SDP_INT128);
+       else if (!strcmp(el, "uuid"))
+               return sdp_xml_parse_uuid(data, record);
+       else if (!strcmp(el, "url"))
+               return sdp_xml_parse_url(data);
+       else if (!strcmp(el, "text"))
+               return sdp_xml_parse_text(data, elem->type);
+       else if (!strcmp(el, "nil"))
+               return sdp_xml_parse_nil(data);
+
+       return NULL;
+}
diff --git a/src/sdp-xml.h b/src/sdp-xml.h
new file mode 100644 (file)
index 0000000..db7db30
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef __SDP_XML_H
+#define __SDP_XML_H
+
+#include <bluetooth/sdp.h>
+
+#define SDP_XML_ENCODING_NORMAL        0
+#define SDP_XML_ENCODING_HEX   1
+
+void convert_sdp_record_to_xml(sdp_record_t *rec,
+               void *user_data, void (*append_func) (void *, const char *));
+
+sdp_data_t *sdp_xml_parse_nil(const char *data);
+sdp_data_t *sdp_xml_parse_text(const char *data, char encoding);
+sdp_data_t *sdp_xml_parse_url(const char *data);
+sdp_data_t *sdp_xml_parse_int(const char *data, uint8_t dtd);
+sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record);
+
+struct sdp_xml_data {
+       char *text;                     /* Pointer to the current buffer */
+       int size;                       /* Size of the current buffer */
+       sdp_data_t *data;               /* The current item being built */
+       struct sdp_xml_data *next;      /* Next item on the stack */
+       char type;                      /* 0 = Text or Hexadecimal */
+       char *name;                     /* Name, optional in the dtd */
+       /* TODO: What is it used for? */
+};
+
+struct sdp_xml_data *sdp_xml_data_alloc(void);
+void sdp_xml_data_free(struct sdp_xml_data *elem);
+struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem);
+
+sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem,
+                                                       sdp_record_t *record);
+
+#endif /* __SDP_XML_H */
diff --git a/src/sdpd-database.c b/src/sdpd-database.c
new file mode 100644 (file)
index 0000000..dd492bf
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "sdpd.h"
+#include "log.h"
+#include "adapter.h"
+#include "manager.h"
+
+static sdp_list_t *service_db;
+static sdp_list_t *access_db;
+
+typedef struct {
+       uint32_t handle;
+       bdaddr_t device;
+} sdp_access_t;
+
+/*
+ * Ordering function called when inserting a service record.
+ * The service repository is a linked list in sorted order
+ * and the service record handle is the sort key
+ */
+int record_sort(const void *r1, const void *r2)
+{
+       const sdp_record_t *rec1 = r1;
+       const sdp_record_t *rec2 = r2;
+
+       if (!rec1 || !rec2) {
+               error("NULL RECORD LIST FATAL");
+               return -1;
+       }
+
+       return rec1->handle - rec2->handle;
+}
+
+static int access_sort(const void *r1, const void *r2)
+{
+       const sdp_access_t *rec1 = r1;
+       const sdp_access_t *rec2 = r2;
+
+       if (!rec1 || !rec2) {
+               error("NULL RECORD LIST FATAL");
+               return -1;
+       }
+
+       return rec1->handle - rec2->handle;
+}
+
+static void access_free(void *p)
+{
+       free(p);
+}
+
+/*
+ * Reset the service repository by deleting its contents
+ */
+void sdp_svcdb_reset(void)
+{
+       sdp_list_free(service_db, (sdp_free_func_t) sdp_record_free);
+       sdp_list_free(access_db, access_free);
+}
+
+typedef struct _indexed {
+       int sock;
+       sdp_record_t *record;
+} sdp_indexed_t;
+
+static sdp_list_t *socket_index;
+
+/*
+ * collect all services registered over this socket
+ */
+void sdp_svcdb_collect_all(int sock)
+{
+       sdp_list_t *p, *q;
+
+       for (p = socket_index, q = 0; p; ) {
+               sdp_indexed_t *item = p->data;
+               if (item->sock == sock) {
+                       sdp_list_t *next = p->next;
+                       sdp_record_remove(item->record->handle);
+                       sdp_record_free(item->record);
+                       free(item);
+                       if (q)
+                               q->next = next;
+                       else
+                               socket_index = next;
+                       free(p);
+                       p = next;
+               } else if (item->sock > sock)
+                       return;
+               else {
+                       q = p;
+                       p = p->next;
+               }
+       }
+}
+
+void sdp_svcdb_collect(sdp_record_t *rec)
+{
+       sdp_list_t *p, *q;
+
+       for (p = socket_index, q = 0; p; q = p, p = p->next) {
+               sdp_indexed_t *item = p->data;
+               if (rec == item->record) {
+                       free(item);
+                       if (q)
+                               q->next = p->next;
+                       else
+                               socket_index = p->next;
+                       free(p);
+                       return;
+               }
+       }
+}
+
+static int compare_indices(const void *i1, const void *i2)
+{
+       const sdp_indexed_t *s1 = i1;
+       const sdp_indexed_t *s2 = i2;
+       return s1->sock - s2->sock;
+}
+
+void sdp_svcdb_set_collectable(sdp_record_t *record, int sock)
+{
+       sdp_indexed_t *item = malloc(sizeof(sdp_indexed_t));
+       item->sock = sock;
+       item->record = record;
+       socket_index = sdp_list_insert_sorted(socket_index, item, compare_indices);
+}
+
+/*
+ * Add a service record to the repository
+ */
+void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec)
+{
+       struct btd_adapter *adapter;
+       sdp_access_t *dev;
+
+       SDPDBG("Adding rec : 0x%lx", (long) rec);
+       SDPDBG("with handle : 0x%x", rec->handle);
+
+       service_db = sdp_list_insert_sorted(service_db, rec, record_sort);
+
+       dev = malloc(sizeof(*dev));
+       if (!dev)
+               return;
+
+       bacpy(&dev->device, device);
+       dev->handle = rec->handle;
+
+       access_db = sdp_list_insert_sorted(access_db, dev, access_sort);
+
+       if (bacmp(device, BDADDR_ANY) == 0) {
+               manager_foreach_adapter(adapter_service_insert, rec);
+               return;
+       }
+
+       adapter = manager_find_adapter(device);
+       if (adapter)
+               adapter_service_insert(adapter, rec);
+}
+
+static sdp_list_t *record_locate(uint32_t handle)
+{
+       if (service_db) {
+               sdp_list_t *p;
+               sdp_record_t r;
+
+               r.handle = handle;
+               p = sdp_list_find(service_db, &r, record_sort);
+               return p;
+       }
+
+       SDPDBG("Could not find svcRec for : 0x%x", handle);
+       return NULL;
+}
+
+static sdp_list_t *access_locate(uint32_t handle)
+{
+       if (access_db) {
+               sdp_list_t *p;
+               sdp_access_t a;
+
+               a.handle = handle;
+               p = sdp_list_find(access_db, &a, access_sort);
+               return p;
+       }
+
+       SDPDBG("Could not find access data for : 0x%x", handle);
+       return NULL;
+}
+
+/*
+ * Given a service record handle, find the record associated with it.
+ */
+sdp_record_t *sdp_record_find(uint32_t handle)
+{
+       sdp_list_t *p = record_locate(handle);
+
+       if (!p) {
+               SDPDBG("Couldn't find record for : 0x%x", handle);
+               return 0;
+       }
+
+       return p->data;
+}
+
+/*
+ * Given a service record handle, remove its record from the repository
+ */
+int sdp_record_remove(uint32_t handle)
+{
+       sdp_list_t *p = record_locate(handle);
+       sdp_record_t *r;
+       sdp_access_t *a;
+
+       if (!p) {
+               error("Remove : Couldn't find record for : 0x%x", handle);
+               return -1;
+       }
+
+       r = p->data;
+       if (r)
+               service_db = sdp_list_remove(service_db, r);
+
+       p = access_locate(handle);
+       if (p == NULL || p->data == NULL)
+               return 0;
+
+       a = p->data;
+
+       if (bacmp(&a->device, BDADDR_ANY) != 0) {
+               struct btd_adapter *adapter = manager_find_adapter(&a->device);
+               if (adapter)
+                       adapter_service_remove(adapter, r);
+       } else
+               manager_foreach_adapter(adapter_service_remove, r);
+
+       access_db = sdp_list_remove(access_db, a);
+       access_free(a);
+
+       return 0;
+}
+
+/*
+ * Return a pointer to the linked list containing the records in sorted order
+ */
+sdp_list_t *sdp_get_record_list(void)
+{
+       return service_db;
+}
+
+sdp_list_t *sdp_get_access_list(void)
+{
+       return access_db;
+}
+
+int sdp_check_access(uint32_t handle, bdaddr_t *device)
+{
+       sdp_list_t *p = access_locate(handle);
+       sdp_access_t *a;
+
+       if (!p)
+               return 1;
+
+       a = p->data;
+       if (!a)
+               return 1;
+
+       if (bacmp(&a->device, device) &&
+                       bacmp(&a->device, BDADDR_ANY) &&
+                       bacmp(device, BDADDR_ANY))
+               return 0;
+
+       return 1;
+}
+
+uint32_t sdp_next_handle(void)
+{
+       uint32_t handle = 0x10000;
+
+       while (sdp_record_find(handle))
+               handle++;
+
+       return handle;
+}
+
+void sdp_init_services_list(bdaddr_t *device)
+{
+       sdp_list_t *p;
+
+       DBG("");
+
+       for (p = access_db; p != NULL; p = p->next) {
+               sdp_access_t *access = p->data;
+               sdp_record_t *rec;
+
+               if (bacmp(BDADDR_ANY, &access->device))
+                       continue;
+
+               rec = sdp_record_find(access->handle);
+               if (rec == NULL)
+                       continue;
+
+               SDPDBG("adding record with handle %x", access->handle);
+
+               manager_foreach_adapter(adapter_service_insert, rec);
+       }
+}
diff --git a/src/sdpd-request.c b/src/sdpd-request.c
new file mode 100644 (file)
index 0000000..6a903c6
--- /dev/null
@@ -0,0 +1,1093 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.h>
+
+#include "sdpd.h"
+#include "log.h"
+
+typedef struct {
+       uint32_t timestamp;
+       union {
+               uint16_t maxBytesSent;
+               uint16_t lastIndexSent;
+       } cStateValue;
+} sdp_cont_state_t;
+
+#define SDP_CONT_STATE_SIZE (sizeof(uint8_t) + sizeof(sdp_cont_state_t))
+
+#define MIN(x, y) ((x) < (y)) ? (x): (y)
+
+typedef struct _sdp_cstate_list sdp_cstate_list_t;
+
+struct _sdp_cstate_list {
+       sdp_cstate_list_t *next;
+       uint32_t timestamp;
+       sdp_buf_t buf;
+};
+
+static sdp_cstate_list_t *cstates;
+
+/* FIXME: should probably remove it when it's found */
+static sdp_buf_t *sdp_get_cached_rsp(sdp_cont_state_t *cstate)
+{
+       sdp_cstate_list_t *p;
+
+       for (p = cstates; p; p = p->next)
+               if (p->timestamp == cstate->timestamp)
+                       return &p->buf;
+       return 0;
+}
+
+static uint32_t sdp_cstate_alloc_buf(sdp_buf_t *buf)
+{
+       sdp_cstate_list_t *cstate = malloc(sizeof(sdp_cstate_list_t));
+       uint8_t *data = malloc(buf->data_size);
+
+       memcpy(data, buf->data, buf->data_size);
+       memset((char *)cstate, 0, sizeof(sdp_cstate_list_t));
+       cstate->buf.data = data;
+       cstate->buf.data_size = buf->data_size;
+       cstate->buf.buf_size = buf->data_size;
+       cstate->timestamp = sdp_get_time();
+       cstate->next = cstates;
+       cstates = cstate;
+       return cstate->timestamp;
+}
+
+/* Additional values for checking datatype (not in spec) */
+#define SDP_TYPE_UUID  0xfe
+#define SDP_TYPE_ATTRID        0xff
+
+struct attrid {
+       uint8_t dtd;
+       union {
+               uint16_t uint16;
+               uint32_t uint32;
+       };
+};
+
+/*
+ * Generic data element sequence extractor. Builds
+ * a list whose elements are those found in the
+ * sequence. The data type of elements found in the
+ * sequence is returned in the reference pDataType
+ */
+static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *pDataType, uint8_t expectedType)
+{
+       uint8_t seqType;
+       int scanned, data_size = 0;
+       short numberOfElements = 0;
+       int seqlen = 0;
+       sdp_list_t *pSeq = NULL;
+       uint8_t dataType;
+       int status = 0;
+       const uint8_t *p;
+       size_t bufsize;
+
+       scanned = sdp_extract_seqtype(buf, len, &seqType, &data_size);
+
+       SDPDBG("Seq type : %d", seqType);
+       if (!scanned || (seqType != SDP_SEQ8 && seqType != SDP_SEQ16)) {
+               error("Unknown seq type");
+               return -1;
+       }
+       p = buf + scanned;
+       bufsize = len - scanned;
+
+       SDPDBG("Data size : %d", data_size);
+
+       for (;;) {
+               char *pElem = NULL;
+               int localSeqLength = 0;
+
+               if (bufsize < sizeof(uint8_t)) {
+                       SDPDBG("->Unexpected end of buffer");
+                       goto failed;
+               }
+
+               dataType = *p;
+
+               SDPDBG("Data type: 0x%02x", dataType);
+
+               if (expectedType == SDP_TYPE_UUID) {
+                       if (dataType != SDP_UUID16 && dataType != SDP_UUID32 && dataType != SDP_UUID128) {
+                               SDPDBG("->Unexpected Data type (expected UUID_ANY)");
+                               goto failed;
+                       }
+               } else if (expectedType == SDP_TYPE_ATTRID &&
+                               (dataType != SDP_UINT16 && dataType != SDP_UINT32)) {
+                       SDPDBG("->Unexpected Data type (expected 0x%02x or 0x%02x)",
+                                                               SDP_UINT16, SDP_UINT32);
+                       goto failed;
+               } else if (expectedType != SDP_TYPE_ATTRID && dataType != expectedType) {
+                       SDPDBG("->Unexpected Data type (expected 0x%02x)", expectedType);
+                       goto failed;
+               }
+
+               switch (dataType) {
+               case SDP_UINT16:
+                       p += sizeof(uint8_t);
+                       seqlen += sizeof(uint8_t);
+                       bufsize -= sizeof(uint8_t);
+                       if (bufsize < sizeof(uint16_t)) {
+                               SDPDBG("->Unexpected end of buffer");
+                               goto failed;
+                       }
+
+                       if (expectedType == SDP_TYPE_ATTRID) {
+                               struct attrid *aid;
+                               aid = malloc(sizeof(struct attrid));
+                               aid->dtd = dataType;
+                               bt_put_unaligned(ntohs(bt_get_unaligned((uint16_t *)p)), (uint16_t *)&aid->uint16);
+                               pElem = (char *) aid;
+                       } else {
+                               pElem = malloc(sizeof(uint16_t));
+                               bt_put_unaligned(ntohs(bt_get_unaligned((uint16_t *)p)), (uint16_t *)pElem);
+                       }
+                       p += sizeof(uint16_t);
+                       seqlen += sizeof(uint16_t);
+                       bufsize -= sizeof(uint16_t);
+                       break;
+               case SDP_UINT32:
+                       p += sizeof(uint8_t);
+                       seqlen += sizeof(uint8_t);
+                       bufsize -= sizeof(uint8_t);
+                       if (bufsize < (int)sizeof(uint32_t)) {
+                               SDPDBG("->Unexpected end of buffer");
+                               goto failed;
+                       }
+
+                       if (expectedType == SDP_TYPE_ATTRID) {
+                               struct attrid *aid;
+                               aid = malloc(sizeof(struct attrid));
+                               aid->dtd = dataType;
+                               bt_put_unaligned(ntohl(bt_get_unaligned((uint32_t *)p)), (uint32_t *)&aid->uint32);
+                               pElem = (char *) aid;
+                       } else {
+                               pElem = malloc(sizeof(uint32_t));
+                               bt_put_unaligned(ntohl(bt_get_unaligned((uint32_t *)p)), (uint32_t *)pElem);
+                       }
+                       p += sizeof(uint32_t);
+                       seqlen += sizeof(uint32_t);
+                       bufsize -= sizeof(uint32_t);
+                       break;
+               case SDP_UUID16:
+               case SDP_UUID32:
+               case SDP_UUID128:
+                       pElem = malloc(sizeof(uuid_t));
+                       status = sdp_uuid_extract(p, bufsize, (uuid_t *) pElem, &localSeqLength);
+                       if (status < 0) {
+                               free(pElem);
+                               goto failed;
+                       }
+                       seqlen += localSeqLength;
+                       p += localSeqLength;
+                       bufsize -= localSeqLength;
+                       break;
+               default:
+                       return -1;
+               }
+               if (status == 0) {
+                       pSeq = sdp_list_append(pSeq, pElem);
+                       numberOfElements++;
+                       SDPDBG("No of elements : %d", numberOfElements);
+
+                       if (seqlen == data_size)
+                               break;
+                       else if (seqlen > data_size || seqlen > len)
+                               goto failed;
+               } else
+                       free(pElem);
+       }
+       *svcReqSeq = pSeq;
+       scanned += seqlen;
+       *pDataType = dataType;
+       return scanned;
+
+failed:
+       sdp_list_free(pSeq, free);
+       return -1;
+}
+
+static int sdp_set_cstate_pdu(sdp_buf_t *buf, sdp_cont_state_t *cstate)
+{
+       uint8_t *pdata = buf->data + buf->data_size;
+       int length = 0;
+
+       if (cstate) {
+               SDPDBG("Non null sdp_cstate_t id : 0x%x", cstate->timestamp);
+               *pdata = sizeof(sdp_cont_state_t);
+               pdata += sizeof(uint8_t);
+               length += sizeof(uint8_t);
+               memcpy(pdata, cstate, sizeof(sdp_cont_state_t));
+               length += sizeof(sdp_cont_state_t);
+       } else {
+               /* set "null" continuation state */
+               *pdata = 0;
+               length += sizeof(uint8_t);
+       }
+       buf->data_size += length;
+       return length;
+}
+
+static int sdp_cstate_get(uint8_t *buffer, size_t len,
+                                               sdp_cont_state_t **cstate)
+{
+       uint8_t cStateSize = *buffer;
+
+       SDPDBG("Continuation State size : %d", cStateSize);
+
+       if (cStateSize == 0) {
+               *cstate = NULL;
+               return 0;
+       }
+
+       buffer++;
+       len--;
+
+       if (len < sizeof(sdp_cont_state_t))
+               return -EINVAL;
+
+       /*
+        * Check if continuation state exists, if yes attempt
+        * to get response remainder from cache, else send error
+        */
+
+       *cstate = malloc(sizeof(sdp_cont_state_t));
+       if (!(*cstate))
+               return -ENOMEM;
+
+       memcpy(*cstate, buffer, sizeof(sdp_cont_state_t));
+
+       SDPDBG("Cstate TS : 0x%x", (*cstate)->timestamp);
+       SDPDBG("Bytes sent : %d", (*cstate)->cStateValue.maxBytesSent);
+
+       return 0;
+}
+
+/*
+ * The matching process is defined as "each and every UUID
+ * specified in the "search pattern" must be present in the
+ * "target pattern". Here "search pattern" is the set of UUIDs
+ * specified by the service discovery client and "target pattern"
+ * is the set of UUIDs present in a service record.
+ *
+ * Return 1 if each and every UUID in the search
+ * pattern exists in the target pattern, 0 if the
+ * match succeeds and -1 on error.
+ */
+static int sdp_match_uuid(sdp_list_t *search, sdp_list_t *pattern)
+{
+       /*
+        * The target is a sorted list, so we need not look
+        * at all elements to confirm existence of an element
+        * from the search pattern
+        */
+       int patlen = sdp_list_len(pattern);
+
+       if (patlen < sdp_list_len(search))
+               return -1;
+       for (; search; search = search->next) {
+               uuid_t *uuid128;
+               void *data = search->data;
+               sdp_list_t *list;
+               if (data == NULL)
+                       return -1;
+
+               /* create 128-bit form of the search UUID */
+               uuid128 = sdp_uuid_to_uuid128((uuid_t *)data);
+               list = sdp_list_find(pattern, uuid128, sdp_uuid128_cmp);
+               bt_free(uuid128);
+               if (!list)
+                       return 0;
+       }
+       return 1;
+}
+
+/*
+ * Service search request PDU. This method extracts the search pattern
+ * (a sequence of UUIDs) and calls the matching function
+ * to find matching services
+ */
+static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
+{
+       int status = 0, i, plen, mlen, mtu, scanned;
+       sdp_list_t *pattern = NULL;
+       uint16_t expected, actual, rsp_count = 0;
+       uint8_t dtd;
+       sdp_cont_state_t *cstate = NULL;
+       uint8_t *pCacheBuffer = NULL;
+       int handleSize = 0;
+       uint32_t cStateId = 0;
+       short *pTotalRecordCount, *pCurrentRecordCount;
+       uint8_t *pdata = req->buf + sizeof(sdp_pdu_hdr_t);
+       size_t data_left = req->len - sizeof(sdp_pdu_hdr_t);
+
+       scanned = extract_des(pdata, data_left, &pattern, &dtd, SDP_TYPE_UUID);
+
+       if (scanned == -1) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+       pdata += scanned;
+       data_left -= scanned;
+
+       plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
+       mlen = scanned + sizeof(uint16_t) + 1;
+       /* ensure we don't read past buffer */
+       if (plen < mlen || plen != mlen + *(uint8_t *)(pdata+sizeof(uint16_t))) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       if (data_left < sizeof(uint16_t)) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       expected = ntohs(bt_get_unaligned((uint16_t *)pdata));
+
+       SDPDBG("Expected count: %d", expected);
+       SDPDBG("Bytes scanned : %d", scanned);
+
+       pdata += sizeof(uint16_t);
+       data_left -= sizeof(uint16_t);
+
+       /*
+        * Check if continuation state exists, if yes attempt
+        * to get rsp remainder from cache, else send error
+        */
+       if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       mtu = req->mtu - sizeof(sdp_pdu_hdr_t) - sizeof(uint16_t) - sizeof(uint16_t) - SDP_CONT_STATE_SIZE;
+       actual = MIN(expected, mtu >> 2);
+
+       /* make space in the rsp buffer for total and current record counts */
+       pdata = buf->data;
+
+       /* total service record count = 0 */
+       pTotalRecordCount = (short *)pdata;
+       bt_put_unaligned(0, (uint16_t *)pdata);
+       pdata += sizeof(uint16_t);
+       buf->data_size += sizeof(uint16_t);
+
+       /* current service record count = 0 */
+       pCurrentRecordCount = (short *)pdata;
+       bt_put_unaligned(0, (uint16_t *)pdata);
+       pdata += sizeof(uint16_t);
+       buf->data_size += sizeof(uint16_t);
+
+       if (cstate == NULL) {
+               /* for every record in the DB, do a pattern search */
+               sdp_list_t *list = sdp_get_record_list();
+
+               handleSize = 0;
+               for (; list && rsp_count < expected; list = list->next) {
+                       sdp_record_t *rec = list->data;
+
+                       SDPDBG("Checking svcRec : 0x%x", rec->handle);
+
+                       if (sdp_match_uuid(pattern, rec->pattern) > 0 &&
+                                       sdp_check_access(rec->handle, &req->device)) {
+                               rsp_count++;
+                               bt_put_unaligned(htonl(rec->handle), (uint32_t *)pdata);
+                               pdata += sizeof(uint32_t);
+                               handleSize += sizeof(uint32_t);
+                       }
+               }
+
+               SDPDBG("Match count: %d", rsp_count);
+
+               buf->data_size += handleSize;
+               bt_put_unaligned(htons(rsp_count), (uint16_t *)pTotalRecordCount);
+               bt_put_unaligned(htons(rsp_count), (uint16_t *)pCurrentRecordCount);
+
+               if (rsp_count > actual) {
+                       /* cache the rsp and generate a continuation state */
+                       cStateId = sdp_cstate_alloc_buf(buf);
+                       /*
+                        * subtract handleSize since we now send only
+                        * a subset of handles
+                        */
+                       buf->data_size -= handleSize;
+               } else {
+                       /* NULL continuation state */
+                       sdp_set_cstate_pdu(buf, NULL);
+               }
+       }
+
+       /* under both the conditions below, the rsp buffer is not built yet */
+       if (cstate || cStateId > 0) {
+               short lastIndex = 0;
+
+               if (cstate) {
+                       /*
+                        * Get the previous sdp_cont_state_t and obtain
+                        * the cached rsp
+                        */
+                       sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
+                       if (pCache) {
+                               pCacheBuffer = pCache->data;
+                               /* get the rsp_count from the cached buffer */
+                               rsp_count = ntohs(bt_get_unaligned((uint16_t *)pCacheBuffer));
+
+                               /* get index of the last sdp_record_t sent */
+                               lastIndex = cstate->cStateValue.lastIndexSent;
+                       } else {
+                               status = SDP_INVALID_CSTATE;
+                               goto done;
+                       }
+               } else {
+                       pCacheBuffer = buf->data;
+                       lastIndex = 0;
+               }
+
+               /*
+                * Set the local buffer pointer to after the
+                * current record count and increment the cached
+                * buffer pointer to beyond the counters
+                */
+               pdata = (uint8_t *) pCurrentRecordCount + sizeof(uint16_t);
+
+               /* increment beyond the totalCount and the currentCount */
+               pCacheBuffer += 2 * sizeof(uint16_t);
+
+               if (cstate) {
+                       handleSize = 0;
+                       for (i = lastIndex; (i - lastIndex) < actual && i < rsp_count; i++) {
+                               bt_put_unaligned(bt_get_unaligned((uint32_t *)(pCacheBuffer + i * sizeof(uint32_t))), (uint32_t *)pdata);
+                               pdata += sizeof(uint32_t);
+                               handleSize += sizeof(uint32_t);
+                       }
+               } else {
+                       handleSize = actual << 2;
+                       i = actual;
+               }
+
+               buf->data_size += handleSize;
+               bt_put_unaligned(htons(rsp_count), (uint16_t *)pTotalRecordCount);
+               bt_put_unaligned(htons(i - lastIndex), (uint16_t *)pCurrentRecordCount);
+
+               if (i == rsp_count) {
+                       /* set "null" continuationState */
+                       sdp_set_cstate_pdu(buf, NULL);
+               } else {
+                       /*
+                        * there's more: set lastIndexSent to
+                        * the new value and move on
+                        */
+                       sdp_cont_state_t newState;
+
+                       SDPDBG("Setting non-NULL sdp_cstate_t");
+
+                       if (cstate)
+                               memcpy(&newState, cstate, sizeof(sdp_cont_state_t));
+                       else {
+                               memset(&newState, 0, sizeof(sdp_cont_state_t));
+                               newState.timestamp = cStateId;
+                       }
+                       newState.cStateValue.lastIndexSent = i;
+                       sdp_set_cstate_pdu(buf, &newState);
+               }
+       }
+
+done:
+       free(cstate);
+       if (pattern)
+               sdp_list_free(pattern, free);
+
+       return status;
+}
+
+/*
+ * Extract attribute identifiers from the request PDU.
+ * Clients could request a subset of attributes (by id)
+ * from a service record, instead of the whole set. The
+ * requested identifiers are present in the PDU form of
+ * the request
+ */
+static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf)
+{
+       sdp_buf_t pdu;
+
+       if (!rec)
+               return SDP_INVALID_RECORD_HANDLE;
+
+       if (seq == NULL) {
+               SDPDBG("Attribute sequence is NULL");
+               return 0;
+       }
+
+       SDPDBG("Entries in attr seq : %d", sdp_list_len(seq));
+
+       sdp_gen_record_pdu(rec, &pdu);
+
+       for (; seq; seq = seq->next) {
+               struct attrid *aid = seq->data;
+
+               SDPDBG("AttrDataType : %d", aid->dtd);
+
+               if (aid->dtd == SDP_UINT16) {
+                       uint16_t attr = bt_get_unaligned((uint16_t *)&aid->uint16);
+                       sdp_data_t *a = sdp_data_get(rec, attr);
+                       if (a)
+                               sdp_append_to_pdu(buf, a);
+               } else if (aid->dtd == SDP_UINT32) {
+                       uint32_t range = bt_get_unaligned((uint32_t *)&aid->uint32);
+                       uint16_t attr;
+                       uint16_t low = (0xffff0000 & range) >> 16;
+                       uint16_t high = 0x0000ffff & range;
+                       sdp_data_t *data;
+
+                       SDPDBG("attr range : 0x%x", range);
+                       SDPDBG("Low id : 0x%x", low);
+                       SDPDBG("High id : 0x%x", high);
+
+                       if (low == 0x0000 && high == 0xffff && pdu.data_size <= buf->buf_size) {
+                               /* copy it */
+                               memcpy(buf->data, pdu.data, pdu.data_size);
+                               buf->data_size = pdu.data_size;
+                               break;
+                       }
+                       /* (else) sub-range of attributes */
+                       for (attr = low; attr < high; attr++) {
+                               data = sdp_data_get(rec, attr);
+                               if (data)
+                                       sdp_append_to_pdu(buf, data);
+                       }
+                       data = sdp_data_get(rec, high);
+                       if (data)
+                               sdp_append_to_pdu(buf, data);
+               } else {
+                       error("Unexpected data type : 0x%x", aid->dtd);
+                       error("Expect uint16_t or uint32_t");
+                       free(pdu.data);
+                       return SDP_INVALID_SYNTAX;
+               }
+       }
+
+       free(pdu.data);
+
+       return 0;
+}
+
+/*
+ * A request for the attributes of a service record.
+ * First check if the service record (specified by
+ * service record handle) exists, then call the attribute
+ * streaming function
+ */
+static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
+{
+       sdp_cont_state_t *cstate = NULL;
+       uint8_t *pResponse = NULL;
+       short cstate_size = 0;
+       sdp_list_t *seq = NULL;
+       uint8_t dtd = 0;
+       int scanned = 0;
+       unsigned int max_rsp_size;
+       int status = 0, plen, mlen;
+       uint8_t *pdata = req->buf + sizeof(sdp_pdu_hdr_t);
+       size_t data_left = req->len - sizeof(sdp_pdu_hdr_t);
+       uint32_t handle;
+
+       if (data_left < sizeof(uint32_t)) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       handle = ntohl(bt_get_unaligned((uint32_t *)pdata));
+
+       pdata += sizeof(uint32_t);
+       data_left -= sizeof(uint32_t);
+
+       if (data_left < sizeof(uint16_t)) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       max_rsp_size = ntohs(bt_get_unaligned((uint16_t *)pdata));
+
+       pdata += sizeof(uint16_t);
+       data_left -= sizeof(uint16_t);
+
+       if (data_left < sizeof(sdp_pdu_hdr_t)) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       /* extract the attribute list */
+       scanned = extract_des(pdata, data_left, &seq, &dtd, SDP_TYPE_ATTRID);
+       if (scanned == -1) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+       pdata += scanned;
+       data_left -= scanned;
+
+       plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
+       mlen = scanned + sizeof(uint32_t) + sizeof(uint16_t) + 1;
+       /* ensure we don't read past buffer */
+       if (plen < mlen || plen != mlen + *(uint8_t *)pdata) {
+               status = SDP_INVALID_PDU_SIZE;
+               goto done;
+       }
+
+       /*
+        * if continuation state exists, attempt
+        * to get rsp remainder from cache, else send error
+        */
+       if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       SDPDBG("SvcRecHandle : 0x%x", handle);
+       SDPDBG("max_rsp_size : %d", max_rsp_size);
+
+       /*
+        * Check that max_rsp_size is within valid range
+        * a minimum size of 0x0007 has to be used for data field
+        */
+       if (max_rsp_size < 0x0007) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       /*
+        * Calculate Attribute size according to MTU
+        * We can send only (MTU - sizeof(sdp_pdu_hdr_t) - sizeof(sdp_cont_state_t))
+        */
+       max_rsp_size = MIN(max_rsp_size, req->mtu - sizeof(sdp_pdu_hdr_t) -
+                       sizeof(uint32_t) - SDP_CONT_STATE_SIZE - sizeof(uint16_t));
+
+       /* pull header for AttributeList byte count */
+       buf->data += sizeof(uint16_t);
+       buf->buf_size -= sizeof(uint16_t);
+
+       if (cstate) {
+               sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
+
+               SDPDBG("Obtained cached rsp : %p", pCache);
+
+               if (pCache) {
+                       short sent = MIN(max_rsp_size, pCache->data_size - cstate->cStateValue.maxBytesSent);
+                       pResponse = pCache->data;
+                       memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent);
+                       buf->data_size += sent;
+                       cstate->cStateValue.maxBytesSent += sent;
+
+                       SDPDBG("Response size : %d sending now : %d bytes sent so far : %d",
+                               pCache->data_size, sent, cstate->cStateValue.maxBytesSent);
+                       if (cstate->cStateValue.maxBytesSent == pCache->data_size)
+                               cstate_size = sdp_set_cstate_pdu(buf, NULL);
+                       else
+                               cstate_size = sdp_set_cstate_pdu(buf, cstate);
+               } else {
+                       status = SDP_INVALID_CSTATE;
+                       error("NULL cache buffer and non-NULL continuation state");
+               }
+       } else {
+               sdp_record_t *rec = sdp_record_find(handle);
+               status = extract_attrs(rec, seq, buf);
+               if (buf->data_size > max_rsp_size) {
+                       sdp_cont_state_t newState;
+
+                       memset((char *)&newState, 0, sizeof(sdp_cont_state_t));
+                       newState.timestamp = sdp_cstate_alloc_buf(buf);
+                       /*
+                        * Reset the buffer size to the maximum expected and
+                        * set the sdp_cont_state_t
+                        */
+                       SDPDBG("Creating continuation state of size : %d", buf->data_size);
+                       buf->data_size = max_rsp_size;
+                       newState.cStateValue.maxBytesSent = max_rsp_size;
+                       cstate_size = sdp_set_cstate_pdu(buf, &newState);
+               } else {
+                       if (buf->data_size == 0)
+                               sdp_append_to_buf(buf, NULL, 0);
+                       cstate_size = sdp_set_cstate_pdu(buf, NULL);
+               }
+       }
+
+       /* push header */
+       buf->data -= sizeof(uint16_t);
+       buf->buf_size += sizeof(uint16_t);
+
+done:
+       free(cstate);
+       if (seq)
+               sdp_list_free(seq, free);
+       if (status)
+               return status;
+
+       /* set attribute list byte count */
+       bt_put_unaligned(htons(buf->data_size - cstate_size), (uint16_t *)buf->data);
+       buf->data_size += sizeof(uint16_t);
+       return 0;
+}
+
+/*
+ * combined service search and attribute extraction
+ */
+static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
+{
+       int status = 0, plen, totscanned;
+       uint8_t *pdata, *pResponse = NULL;
+       unsigned int max;
+       int scanned, rsp_count = 0;
+       sdp_list_t *pattern = NULL, *seq = NULL, *svcList;
+       sdp_cont_state_t *cstate = NULL;
+       short cstate_size = 0;
+       uint8_t dtd = 0;
+       sdp_buf_t tmpbuf;
+       size_t data_left;
+
+       tmpbuf.data = NULL;
+       pdata = req->buf + sizeof(sdp_pdu_hdr_t);
+       data_left = req->len - sizeof(sdp_pdu_hdr_t);
+       scanned = extract_des(pdata, data_left, &pattern, &dtd, SDP_TYPE_UUID);
+       if (scanned == -1) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+       totscanned = scanned;
+
+       SDPDBG("Bytes scanned: %d", scanned);
+
+       pdata += scanned;
+       data_left -= scanned;
+
+       if (data_left < sizeof(uint16_t)) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       max = ntohs(bt_get_unaligned((uint16_t *)pdata));
+
+       pdata += sizeof(uint16_t);
+       data_left -= sizeof(uint16_t);
+
+       SDPDBG("Max Attr expected: %d", max);
+
+       if (data_left < sizeof(sdp_pdu_hdr_t)) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       /* extract the attribute list */
+       scanned = extract_des(pdata, data_left, &seq, &dtd, SDP_TYPE_ATTRID);
+       if (scanned == -1) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       pdata += scanned;
+       data_left -= scanned;
+
+       totscanned += scanned + sizeof(uint16_t) + 1;
+
+       plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
+       if (plen < totscanned || plen != totscanned + *(uint8_t *)pdata) {
+               status = SDP_INVALID_PDU_SIZE;
+               goto done;
+       }
+
+       /*
+        * if continuation state exists attempt
+        * to get rsp remainder from cache, else send error
+        */
+       if (sdp_cstate_get(pdata, data_left, &cstate) < 0) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       svcList = sdp_get_record_list();
+
+       tmpbuf.data = malloc(USHRT_MAX);
+       tmpbuf.data_size = 0;
+       tmpbuf.buf_size = USHRT_MAX;
+       memset(tmpbuf.data, 0, USHRT_MAX);
+
+       /*
+        * Calculate Attribute size according to MTU
+        * We can send only (MTU - sizeof(sdp_pdu_hdr_t) - sizeof(sdp_cont_state_t))
+        */
+       max = MIN(max, req->mtu - sizeof(sdp_pdu_hdr_t) - SDP_CONT_STATE_SIZE - sizeof(uint16_t));
+
+       /* pull header for AttributeList byte count */
+       buf->data += sizeof(uint16_t);
+       buf->buf_size -= sizeof(uint16_t);
+
+       if (cstate == NULL) {
+               /* no continuation state -> create new response */
+               sdp_list_t *p;
+               for (p = svcList; p; p = p->next) {
+                       sdp_record_t *rec = p->data;
+                       if (sdp_match_uuid(pattern, rec->pattern) > 0 &&
+                                       sdp_check_access(rec->handle, &req->device)) {
+                               rsp_count++;
+                               status = extract_attrs(rec, seq, &tmpbuf);
+
+                               SDPDBG("Response count : %d", rsp_count);
+                               SDPDBG("Local PDU size : %d", tmpbuf.data_size);
+                               if (status) {
+                                       SDPDBG("Extract attr from record returns err");
+                                       break;
+                               }
+                               if (buf->data_size + tmpbuf.data_size < buf->buf_size) {
+                                       /* to be sure no relocations */
+                                       sdp_append_to_buf(buf, tmpbuf.data, tmpbuf.data_size);
+                                       tmpbuf.data_size = 0;
+                                       memset(tmpbuf.data, 0, USHRT_MAX);
+                               } else {
+                                       error("Relocation needed");
+                                       break;
+                               }
+                               SDPDBG("Net PDU size : %d", buf->data_size);
+                       }
+               }
+               if (buf->data_size > max) {
+                       sdp_cont_state_t newState;
+
+                       memset((char *)&newState, 0, sizeof(sdp_cont_state_t));
+                       newState.timestamp = sdp_cstate_alloc_buf(buf);
+                       /*
+                        * Reset the buffer size to the maximum expected and
+                        * set the sdp_cont_state_t
+                        */
+                       buf->data_size = max;
+                       newState.cStateValue.maxBytesSent = max;
+                       cstate_size = sdp_set_cstate_pdu(buf, &newState);
+               } else
+                       cstate_size = sdp_set_cstate_pdu(buf, NULL);
+       } else {
+               /* continuation State exists -> get from cache */
+               sdp_buf_t *pCache = sdp_get_cached_rsp(cstate);
+               if (pCache) {
+                       uint16_t sent = MIN(max, pCache->data_size - cstate->cStateValue.maxBytesSent);
+                       pResponse = pCache->data;
+                       memcpy(buf->data, pResponse + cstate->cStateValue.maxBytesSent, sent);
+                       buf->data_size += sent;
+                       cstate->cStateValue.maxBytesSent += sent;
+                       if (cstate->cStateValue.maxBytesSent == pCache->data_size)
+                               cstate_size = sdp_set_cstate_pdu(buf, NULL);
+                       else
+                               cstate_size = sdp_set_cstate_pdu(buf, cstate);
+               } else {
+                       status = SDP_INVALID_CSTATE;
+                       SDPDBG("Non-null continuation state, but null cache buffer");
+               }
+       }
+
+       if (!rsp_count && !cstate) {
+               /* found nothing */
+               buf->data_size = 0;
+               sdp_append_to_buf(buf, tmpbuf.data, tmpbuf.data_size);
+               sdp_set_cstate_pdu(buf, NULL);
+       }
+
+       /* push header */
+       buf->data -= sizeof(uint16_t);
+       buf->buf_size += sizeof(uint16_t);
+
+       if (!status) {
+               /* set attribute list byte count */
+               bt_put_unaligned(htons(buf->data_size - cstate_size), (uint16_t *)buf->data);
+               buf->data_size += sizeof(uint16_t);
+       }
+
+done:
+       free(cstate);
+       free(tmpbuf.data);
+       if (pattern)
+               sdp_list_free(pattern, free);
+       if (seq)
+               sdp_list_free(seq, free);
+       return status;
+}
+
+/*
+ * Top level request processor. Calls the appropriate processing
+ * function based on request type. Handles service registration
+ * client requests also.
+ */
+static void process_request(sdp_req_t *req)
+{
+       sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *)req->buf;
+       sdp_pdu_hdr_t *rsphdr;
+       sdp_buf_t rsp;
+       uint8_t *buf = malloc(USHRT_MAX);
+       int status = SDP_INVALID_SYNTAX;
+
+       memset(buf, 0, USHRT_MAX);
+       rsp.data = buf + sizeof(sdp_pdu_hdr_t);
+       rsp.data_size = 0;
+       rsp.buf_size = USHRT_MAX - sizeof(sdp_pdu_hdr_t);
+       rsphdr = (sdp_pdu_hdr_t *)buf;
+
+       if (ntohs(reqhdr->plen) != req->len - sizeof(sdp_pdu_hdr_t)) {
+               status = SDP_INVALID_PDU_SIZE;
+               goto send_rsp;
+       }
+       switch (reqhdr->pdu_id) {
+       case SDP_SVC_SEARCH_REQ:
+               SDPDBG("Got a svc srch req");
+               status = service_search_req(req, &rsp);
+               rsphdr->pdu_id = SDP_SVC_SEARCH_RSP;
+               break;
+       case SDP_SVC_ATTR_REQ:
+               SDPDBG("Got a svc attr req");
+               status = service_attr_req(req, &rsp);
+               rsphdr->pdu_id = SDP_SVC_ATTR_RSP;
+               break;
+       case SDP_SVC_SEARCH_ATTR_REQ:
+               SDPDBG("Got a svc srch attr req");
+               status = service_search_attr_req(req, &rsp);
+               rsphdr->pdu_id = SDP_SVC_SEARCH_ATTR_RSP;
+               break;
+       /* Following requests are allowed only for local connections */
+       case SDP_SVC_REGISTER_REQ:
+               SDPDBG("Service register request");
+               if (req->local) {
+                       status = service_register_req(req, &rsp);
+                       rsphdr->pdu_id = SDP_SVC_REGISTER_RSP;
+               }
+               break;
+       case SDP_SVC_UPDATE_REQ:
+               SDPDBG("Service update request");
+               if (req->local) {
+                       status = service_update_req(req, &rsp);
+                       rsphdr->pdu_id = SDP_SVC_UPDATE_RSP;
+               }
+               break;
+       case SDP_SVC_REMOVE_REQ:
+               SDPDBG("Service removal request");
+               if (req->local) {
+                       status = service_remove_req(req, &rsp);
+                       rsphdr->pdu_id = SDP_SVC_REMOVE_RSP;
+               }
+               break;
+       default:
+               error("Unknown PDU ID : 0x%x received", reqhdr->pdu_id);
+               status = SDP_INVALID_SYNTAX;
+               break;
+       }
+
+send_rsp:
+       if (status) {
+               rsphdr->pdu_id = SDP_ERROR_RSP;
+               bt_put_unaligned(htons(status), (uint16_t *)rsp.data);
+               rsp.data_size = sizeof(uint16_t);
+       }
+
+       SDPDBG("Sending rsp. status %d", status);
+
+       rsphdr->tid  = reqhdr->tid;
+       rsphdr->plen = htons(rsp.data_size);
+
+       /* point back to the real buffer start and set the real rsp length */
+       rsp.data_size += sizeof(sdp_pdu_hdr_t);
+       rsp.data = buf;
+
+       /* stream the rsp PDU */
+       if (send(req->sock, rsp.data, rsp.data_size, 0) < 0)
+               error("send: %s (%d)", strerror(errno), errno);
+
+       SDPDBG("Bytes Sent : %d", rsp.data_size);
+
+       free(rsp.data);
+       free(req->buf);
+}
+
+void handle_request(int sk, uint8_t *data, int len)
+{
+       struct sockaddr_l2 sa;
+       socklen_t size;
+       sdp_req_t req;
+
+       size = sizeof(sa);
+       if (getpeername(sk, (struct sockaddr *) &sa, &size) < 0) {
+               error("getpeername: %s", strerror(errno));
+               return;
+       }
+
+       if (sa.l2_family == AF_BLUETOOTH) {
+               struct l2cap_options lo;
+
+               memset(&lo, 0, sizeof(lo));
+               size = sizeof(lo);
+
+               if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &lo, &size) < 0) {
+                       error("getsockopt: %s", strerror(errno));
+                       return;
+               }
+
+               bacpy(&req.bdaddr, &sa.l2_bdaddr);
+               req.mtu = lo.omtu;
+               req.local = 0;
+               memset(&sa, 0, sizeof(sa));
+               size = sizeof(sa);
+
+               if (getsockname(sk, (struct sockaddr *) &sa, &size) < 0) {
+                       error("getsockname: %s", strerror(errno));
+                       return;
+               }
+
+               bacpy(&req.device, &sa.l2_bdaddr);
+       } else {
+               bacpy(&req.device, BDADDR_ANY);
+               bacpy(&req.bdaddr, BDADDR_LOCAL);
+               req.mtu = 2048;
+               req.local = 1;
+       }
+
+       req.sock = sk;
+       req.buf  = data;
+       req.len  = len;
+
+       process_request(&req);
+}
diff --git a/src/sdpd-server.c b/src/sdpd-server.c
new file mode 100644 (file)
index 0000000..1d9509e
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <sys/un.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "hcid.h"
+#include "log.h"
+#include "sdpd.h"
+
+static guint l2cap_id = 0, unix_id = 0;
+
+static int l2cap_sock, unix_sock;
+
+/*
+ * SDP server initialization on startup includes creating the
+ * l2cap and unix sockets over which discovery and registration clients
+ * access us respectively
+ */
+static int init_server(uint16_t mtu, int master, int compat)
+{
+       struct l2cap_options opts;
+       struct sockaddr_l2 l2addr;
+       struct sockaddr_un unaddr;
+       socklen_t optlen;
+
+       /* Register the public browse group root */
+       register_public_browse_group();
+
+       /* Register the SDP server's service record */
+       register_server_service();
+
+       /* Create L2CAP socket */
+       l2cap_sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+       if (l2cap_sock < 0) {
+               error("opening L2CAP socket: %s", strerror(errno));
+               return -1;
+       }
+
+       memset(&l2addr, 0, sizeof(l2addr));
+       l2addr.l2_family = AF_BLUETOOTH;
+       bacpy(&l2addr.l2_bdaddr, BDADDR_ANY);
+       l2addr.l2_psm = htobs(SDP_PSM);
+
+       if (bind(l2cap_sock, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
+               error("binding L2CAP socket: %s", strerror(errno));
+               return -1;
+       }
+
+       if (master) {
+               int opt = L2CAP_LM_MASTER;
+               if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+                       error("setsockopt: %s", strerror(errno));
+                       return -1;
+               }
+       }
+
+       if (mtu > 0) {
+               memset(&opts, 0, sizeof(opts));
+               optlen = sizeof(opts);
+
+               if (getsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+                       error("getsockopt: %s", strerror(errno));
+                       return -1;
+               }
+
+               opts.omtu = mtu;
+               opts.imtu = mtu;
+
+               if (setsockopt(l2cap_sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+                       error("setsockopt: %s", strerror(errno));
+                       return -1;
+               }
+       }
+
+       if (listen(l2cap_sock, 5) < 0) {
+               error("listen: %s", strerror(errno));
+               return -1;
+       }
+
+       if (!compat) {
+               unix_sock = -1;
+               return 0;
+       }
+
+       /* Create local Unix socket */
+       unix_sock = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (unix_sock < 0) {
+               error("opening UNIX socket: %s", strerror(errno));
+               return -1;
+       }
+
+       memset(&unaddr, 0, sizeof(unaddr));
+       unaddr.sun_family = AF_UNIX;
+       strcpy(unaddr.sun_path, SDP_UNIX_PATH);
+
+       unlink(unaddr.sun_path);
+
+       if (bind(unix_sock, (struct sockaddr *) &unaddr, sizeof(unaddr)) < 0) {
+               error("binding UNIX socket: %s", strerror(errno));
+               return -1;
+       }
+
+       if (listen(unix_sock, 5) < 0) {
+               error("listen UNIX socket: %s", strerror(errno));
+               return -1;
+       }
+
+       chmod(SDP_UNIX_PATH, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+
+       return 0;
+}
+
+static gboolean io_session_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       sdp_pdu_hdr_t hdr;
+       uint8_t *buf;
+       int sk, len, size;
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       sk = g_io_channel_unix_get_fd(chan);
+
+       if (cond & (G_IO_HUP | G_IO_ERR)) {
+               sdp_svcdb_collect_all(sk);
+               return FALSE;
+       }
+
+       len = recv(sk, &hdr, sizeof(sdp_pdu_hdr_t), MSG_PEEK);
+       if (len <= 0) {
+               sdp_svcdb_collect_all(sk);
+               return FALSE;
+       }
+
+       size = sizeof(sdp_pdu_hdr_t) + ntohs(hdr.plen);
+       buf = malloc(size);
+       if (!buf)
+               return TRUE;
+
+       len = recv(sk, buf, size, 0);
+       if (len <= 0) {
+               sdp_svcdb_collect_all(sk);
+               free(buf);
+               return FALSE;
+       }
+
+       handle_request(sk, buf, len);
+
+       return TRUE;
+}
+
+static gboolean io_accept_event(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       GIOChannel *io;
+       int nsk;
+
+       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+               return FALSE;
+
+       if (data == &l2cap_sock) {
+               struct sockaddr_l2 addr;
+               socklen_t len = sizeof(addr);
+
+               nsk = accept(l2cap_sock, (struct sockaddr *) &addr, &len);
+       } else if (data == &unix_sock) {
+               struct sockaddr_un addr;
+               socklen_t len = sizeof(addr);
+
+               nsk = accept(unix_sock, (struct sockaddr *) &addr, &len);
+       } else
+               return FALSE;
+
+       if (nsk < 0) {
+               error("Can't accept connection: %s", strerror(errno));
+               return TRUE;
+       }
+
+       io = g_io_channel_unix_new(nsk);
+       g_io_channel_set_close_on_unref(io, TRUE);
+
+       g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       io_session_event, data);
+
+       g_io_channel_unref(io);
+
+       return TRUE;
+}
+
+int start_sdp_server(uint16_t mtu, uint32_t flags)
+{
+       int compat = flags & SDP_SERVER_COMPAT;
+       int master = flags & SDP_SERVER_MASTER;
+       GIOChannel *io;
+
+       info("Starting SDP server");
+
+       if (init_server(mtu, master, compat) < 0) {
+               error("Server initialization failed");
+               return -1;
+       }
+
+       if (main_opts.did_source > 0)
+               register_device_id();
+
+       io = g_io_channel_unix_new(l2cap_sock);
+       g_io_channel_set_close_on_unref(io, TRUE);
+
+       l2cap_id = g_io_add_watch(io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       io_accept_event, &l2cap_sock);
+       g_io_channel_unref(io);
+
+       if (compat && unix_sock > fileno(stderr)) {
+               io = g_io_channel_unix_new(unix_sock);
+               g_io_channel_set_close_on_unref(io, TRUE);
+
+               unix_id = g_io_add_watch(io,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       io_accept_event, &unix_sock);
+               g_io_channel_unref(io);
+       }
+
+       return 0;
+}
+
+void stop_sdp_server(void)
+{
+       info("Stopping SDP server");
+
+       sdp_svcdb_reset();
+
+       if (unix_id > 0)
+               g_source_remove(unix_id);
+
+       if (l2cap_id > 0)
+               g_source_remove(l2cap_id);
+
+       l2cap_id = unix_id = 0;
+       l2cap_sock = unix_sock = -1;
+}
diff --git a/src/sdpd-service.c b/src/sdpd-service.c
new file mode 100644 (file)
index 0000000..39e05ab
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "hcid.h"
+#include "sdpd.h"
+#include "log.h"
+#include "adapter.h"
+#include "manager.h"
+
+static sdp_record_t *server = NULL;
+
+/*
+ * List of version numbers supported by the SDP server.
+ * Add to this list when newer versions are supported.
+ */
+static sdp_version_t sdpVnumArray[1] = {
+       { 1, 0 }
+};
+static const int sdpServerVnumEntries = 1;
+
+/*
+ * A simple function which returns the time of day in
+ * seconds. Used for updating the service db state
+ * attribute of the service record of the SDP server
+ */
+uint32_t sdp_get_time(void)
+{
+       /*
+        * To handle failure in gettimeofday, so an old
+        * value is returned and service does not fail
+        */
+       static struct timeval tm;
+
+       gettimeofday(&tm, NULL);
+       return (uint32_t) tm.tv_sec;
+}
+
+/*
+ * The service database state is an attribute of the service record
+ * of the SDP server itself. This attribute is guaranteed to
+ * change if any of the contents of the service repository
+ * changes. This function updates the timestamp of value of
+ * the svcDBState attribute
+ * Set the SDP server DB. Simply a timestamp which is the marker
+ * when the DB was modified.
+ */
+static void update_db_timestamp(void)
+{
+       uint32_t dbts = sdp_get_time();
+       sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &dbts);
+       sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d);
+}
+
+void register_public_browse_group(void)
+{
+       sdp_list_t *browselist;
+       uuid_t bgscid, pbgid;
+       sdp_data_t *sdpdata;
+       sdp_record_t *browse = sdp_record_alloc();
+
+       browse->handle = SDP_SERVER_RECORD_HANDLE + 1;
+
+       sdp_record_add(BDADDR_ANY, browse);
+       sdpdata = sdp_data_alloc(SDP_UINT32, &browse->handle);
+       sdp_attr_add(browse, SDP_ATTR_RECORD_HANDLE, sdpdata);
+
+       sdp_uuid16_create(&bgscid, BROWSE_GRP_DESC_SVCLASS_ID);
+       browselist = sdp_list_append(0, &bgscid);
+       sdp_set_service_classes(browse, browselist);
+       sdp_list_free(browselist, 0);
+
+       sdp_uuid16_create(&pbgid, PUBLIC_BROWSE_GROUP);
+       sdp_attr_add_new(browse, SDP_ATTR_GROUP_ID,
+                               SDP_UUID16, &pbgid.value.uuid16);
+}
+
+/*
+ * The SDP server must present its own service record to
+ * the service repository. This can be accessed by service
+ * discovery clients. This method constructs a service record
+ * and stores it in the repository
+ */
+void register_server_service(void)
+{
+       sdp_list_t *classIDList;
+       uuid_t classID;
+       void **versions, **versionDTDs;
+       uint8_t dtd;
+       sdp_data_t *pData;
+       int i;
+
+       server = sdp_record_alloc();
+       server->pattern = NULL;
+
+       /* Force the record to be SDP_SERVER_RECORD_HANDLE */
+       server->handle = SDP_SERVER_RECORD_HANDLE;
+
+       sdp_record_add(BDADDR_ANY, server);
+       sdp_attr_add(server, SDP_ATTR_RECORD_HANDLE,
+                               sdp_data_alloc(SDP_UINT32, &server->handle));
+
+       sdp_uuid16_create(&classID, SDP_SERVER_SVCLASS_ID);
+       classIDList = sdp_list_append(0, &classID);
+       sdp_set_service_classes(server, classIDList);
+       sdp_list_free(classIDList, 0);
+
+       /*
+        * Set the version numbers supported, these are passed as arguments
+        * to the server on command line. Now defaults to 1.0
+        * Build the version number sequence first
+        */
+       versions = malloc(sdpServerVnumEntries * sizeof(void *));
+       versionDTDs = malloc(sdpServerVnumEntries * sizeof(void *));
+       dtd = SDP_UINT16;
+       for (i = 0; i < sdpServerVnumEntries; i++) {
+               uint16_t *version = malloc(sizeof(uint16_t));
+               *version = sdpVnumArray[i].major;
+               *version = (*version << 8);
+               *version |= sdpVnumArray[i].minor;
+               versions[i] = version;
+               versionDTDs[i] = &dtd;
+       }
+       pData = sdp_seq_alloc(versionDTDs, versions, sdpServerVnumEntries);
+       for (i = 0; i < sdpServerVnumEntries; i++)
+               free(versions[i]);
+       free(versions);
+       free(versionDTDs);
+       sdp_attr_add(server, SDP_ATTR_VERSION_NUM_LIST, pData);
+
+       update_db_timestamp();
+}
+
+void register_device_id(void)
+{
+       const uint16_t spec = 0x0103;
+       const uint8_t primary = 1;
+       sdp_list_t *class_list, *group_list, *profile_list;
+       uuid_t class_uuid, group_uuid;
+       sdp_data_t *sdp_data, *primary_data, *source_data;
+       sdp_data_t *spec_data, *vendor_data, *product_data, *version_data;
+       sdp_profile_desc_t profile;
+       sdp_record_t *record = sdp_record_alloc();
+
+       info("Adding device id record for %04x:%04x:%04x:%04x",
+                               main_opts.did_source, main_opts.did_vendor,
+                               main_opts.did_product, main_opts.did_version);
+
+       record->handle = sdp_next_handle();
+
+       sdp_record_add(BDADDR_ANY, record);
+       sdp_data = sdp_data_alloc(SDP_UINT32, &record->handle);
+       sdp_attr_add(record, SDP_ATTR_RECORD_HANDLE, sdp_data);
+
+       sdp_uuid16_create(&class_uuid, PNP_INFO_SVCLASS_ID);
+       class_list = sdp_list_append(0, &class_uuid);
+       sdp_set_service_classes(record, class_list);
+       sdp_list_free(class_list, NULL);
+
+       sdp_uuid16_create(&group_uuid, PUBLIC_BROWSE_GROUP);
+       group_list = sdp_list_append(NULL, &group_uuid);
+       sdp_set_browse_groups(record, group_list);
+       sdp_list_free(group_list, NULL);
+
+       sdp_uuid16_create(&profile.uuid, PNP_INFO_PROFILE_ID);
+       profile.version = spec;
+       profile_list = sdp_list_append(NULL, &profile);
+       sdp_set_profile_descs(record, profile_list);
+       sdp_list_free(profile_list, NULL);
+
+       spec_data = sdp_data_alloc(SDP_UINT16, &spec);
+       sdp_attr_add(record, 0x0200, spec_data);
+
+       vendor_data = sdp_data_alloc(SDP_UINT16, &main_opts.did_vendor);
+       sdp_attr_add(record, 0x0201, vendor_data);
+
+       product_data = sdp_data_alloc(SDP_UINT16, &main_opts.did_product);
+       sdp_attr_add(record, 0x0202, product_data);
+
+       version_data = sdp_data_alloc(SDP_UINT16, &main_opts.did_version);
+       sdp_attr_add(record, 0x0203, version_data);
+
+       primary_data = sdp_data_alloc(SDP_BOOL, &primary);
+       sdp_attr_add(record, 0x0204, primary_data);
+
+       source_data = sdp_data_alloc(SDP_UINT16, &main_opts.did_source);
+       sdp_attr_add(record, 0x0205, source_data);
+
+       update_db_timestamp();
+}
+
+int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec)
+{
+       sdp_data_t *data;
+       sdp_list_t *pattern;
+
+       if (rec->handle == 0xffffffff) {
+               rec->handle = sdp_next_handle();
+               if (rec->handle < 0x10000)
+                       return -ENOSPC;
+       } else {
+               if (sdp_record_find(rec->handle))
+                       return -EEXIST;
+       }
+
+       DBG("Adding record with handle 0x%05x", rec->handle);
+
+       sdp_record_add(src, rec);
+
+       data = sdp_data_alloc(SDP_UINT32, &rec->handle);
+       sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data);
+
+       if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) {
+               uuid_t uuid;
+               sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
+               sdp_pattern_add_uuid(rec, &uuid);
+       }
+
+       for (pattern = rec->pattern; pattern; pattern = pattern->next) {
+               char uuid[32];
+
+               if (pattern->data == NULL)
+                       continue;
+
+               sdp_uuid2strn((uuid_t *) pattern->data, uuid, sizeof(uuid));
+               DBG("Record pattern UUID %s", uuid);
+       }
+
+       update_db_timestamp();
+
+       return 0;
+}
+
+int remove_record_from_server(uint32_t handle)
+{
+       sdp_record_t *rec;
+
+       /* Refuse to remove the server's own record */
+       if (handle == SDP_SERVER_RECORD_HANDLE)
+               return -EINVAL;
+
+       DBG("Removing record with handle 0x%05x", handle);
+
+       rec = sdp_record_find(handle);
+       if (!rec)
+               return -ENOENT;
+
+       if (sdp_record_remove(handle) == 0)
+               update_db_timestamp();
+
+       sdp_record_free(rec);
+
+       return 0;
+}
+
+/* FIXME: refactor for server-side */
+static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p,
+                                       unsigned int bufsize,
+                                       uint32_t handleExpected, int *scanned)
+{
+       int extractStatus = -1, localExtractedLength = 0;
+       uint8_t dtd;
+       int seqlen = 0;
+       sdp_record_t *rec = NULL;
+       uint16_t attrId, lookAheadAttrId;
+       sdp_data_t *pAttr = NULL;
+       uint32_t handle = 0xffffffff;
+
+       *scanned = sdp_extract_seqtype(p, bufsize, &dtd, &seqlen);
+       p += *scanned;
+       bufsize -= *scanned;
+
+       if (bufsize < sizeof(uint8_t) + sizeof(uint8_t)) {
+               SDPDBG("Unexpected end of packet");
+               return NULL;
+       }
+
+       lookAheadAttrId = ntohs(bt_get_unaligned((uint16_t *) (p + sizeof(uint8_t))));
+
+       SDPDBG("Look ahead attr id : %d", lookAheadAttrId);
+
+       if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) {
+               if (bufsize < (sizeof(uint8_t) * 2) +
+                                       sizeof(uint16_t) + sizeof(uint32_t)) {
+                       SDPDBG("Unexpected end of packet");
+                       return NULL;
+               }
+               handle = ntohl(bt_get_unaligned((uint32_t *) (p +
+                               sizeof(uint8_t) + sizeof(uint16_t) +
+                               sizeof(uint8_t))));
+               SDPDBG("SvcRecHandle : 0x%x", handle);
+               rec = sdp_record_find(handle);
+       } else if (handleExpected != 0xffffffff)
+               rec = sdp_record_find(handleExpected);
+
+       if (!rec) {
+               rec = sdp_record_alloc();
+               rec->attrlist = NULL;
+               if (lookAheadAttrId == SDP_ATTR_RECORD_HANDLE) {
+                       rec->handle = handle;
+                       sdp_record_add(device, rec);
+               } else if (handleExpected != 0xffffffff) {
+                       rec->handle = handleExpected;
+                       sdp_record_add(device, rec);
+               }
+       } else {
+               sdp_list_free(rec->attrlist, (sdp_free_func_t) sdp_data_free);
+               rec->attrlist = NULL;
+       }
+
+       while (localExtractedLength < seqlen) {
+               int attrSize = sizeof(uint8_t);
+               int attrValueLength = 0;
+
+               if (bufsize < attrSize + sizeof(uint16_t)) {
+                       SDPDBG("Unexpected end of packet: Terminating extraction of attributes");
+                       break;
+               }
+
+               SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d",
+                                                       seqlen, localExtractedLength);
+               dtd = *(uint8_t *) p;
+
+               attrId = ntohs(bt_get_unaligned((uint16_t *) (p + attrSize)));
+               attrSize += sizeof(uint16_t);
+
+               SDPDBG("DTD of attrId : %d Attr id : 0x%x", dtd, attrId);
+
+               pAttr = sdp_extract_attr(p + attrSize, bufsize - attrSize,
+                                                       &attrValueLength, rec);
+
+               SDPDBG("Attr id : 0x%x attrValueLength : %d", attrId, attrValueLength);
+
+               attrSize += attrValueLength;
+               if (pAttr == NULL) {
+                       SDPDBG("Terminating extraction of attributes");
+                       break;
+               }
+               localExtractedLength += attrSize;
+               p += attrSize;
+               bufsize -= attrSize;
+               sdp_attr_replace(rec, attrId, pAttr);
+               extractStatus = 0;
+               SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d",
+                                       seqlen, localExtractedLength);
+       }
+
+       if (extractStatus == 0) {
+               SDPDBG("Successful extracting of Svc Rec attributes");
+#ifdef SDP_DEBUG
+               sdp_print_service_attr(rec->attrlist);
+#endif
+               *scanned += seqlen;
+       }
+       return rec;
+}
+
+/*
+ * Add the newly created service record to the service repository
+ */
+int service_register_req(sdp_req_t *req, sdp_buf_t *rsp)
+{
+       int scanned = 0;
+       sdp_data_t *handle;
+       uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
+       int bufsize = req->len - sizeof(sdp_pdu_hdr_t);
+       sdp_record_t *rec;
+
+       req->flags = *p++;
+       if (req->flags & SDP_DEVICE_RECORD) {
+               bacpy(&req->device, (bdaddr_t *) p);
+               p += sizeof(bdaddr_t);
+               bufsize -= sizeof(bdaddr_t);
+       }
+
+       /* save image of PDU: we need it when clients request this attribute */
+       rec = extract_pdu_server(&req->device, p, bufsize, 0xffffffff, &scanned);
+       if (!rec)
+               goto invalid;
+
+       if (rec->handle == 0xffffffff) {
+               rec->handle = sdp_next_handle();
+               if (rec->handle < 0x10000) {
+                       sdp_record_free(rec);
+                       goto invalid;
+               }
+       } else {
+               if (sdp_record_find(rec->handle)) {
+                       /* extract_pdu_server will add the record handle
+                        * if it is missing. So instead of failing, skip
+                        * the record adding to avoid duplication. */
+                       goto success;
+               }
+       }
+
+       sdp_record_add(&req->device, rec);
+       if (!(req->flags & SDP_RECORD_PERSIST))
+               sdp_svcdb_set_collectable(rec, req->sock);
+
+       handle = sdp_data_alloc(SDP_UINT32, &rec->handle);
+       sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, handle);
+
+success:
+       /* if the browse group descriptor is NULL,
+        * ensure that the record belongs to the ROOT group */
+       if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) {
+               uuid_t uuid;
+               sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
+               sdp_pattern_add_uuid(rec, &uuid);
+       }
+
+       update_db_timestamp();
+
+       /* Build a rsp buffer */
+       bt_put_unaligned(htonl(rec->handle), (uint32_t *) rsp->data);
+       rsp->data_size = sizeof(uint32_t);
+
+       return 0;
+
+invalid:
+       bt_put_unaligned(htons(SDP_INVALID_SYNTAX), (uint16_t *) rsp->data);
+       rsp->data_size = sizeof(uint16_t);
+
+       return -1;
+}
+
+/*
+ * Update a service record
+ */
+int service_update_req(sdp_req_t *req, sdp_buf_t *rsp)
+{
+       sdp_record_t *orec, *nrec;
+       int status = 0, scanned = 0;
+       uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
+       int bufsize = req->len - sizeof(sdp_pdu_hdr_t);
+       uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p));
+
+       SDPDBG("Svc Rec Handle: 0x%x", handle);
+
+       p += sizeof(uint32_t);
+       bufsize -= sizeof(uint32_t);
+
+       orec = sdp_record_find(handle);
+
+       SDPDBG("SvcRecOld: %p", orec);
+
+       if (!orec) {
+               status = SDP_INVALID_RECORD_HANDLE;
+               goto done;
+       }
+
+       nrec = extract_pdu_server(BDADDR_ANY, p, bufsize, handle, &scanned);
+       if (!nrec) {
+               status = SDP_INVALID_SYNTAX;
+               goto done;
+       }
+
+       assert(nrec == orec);
+
+       update_db_timestamp();
+
+done:
+       p = rsp->data;
+       bt_put_unaligned(htons(status), (uint16_t *) p);
+       rsp->data_size = sizeof(uint16_t);
+       return status;
+}
+
+/*
+ * Remove a registered service record
+ */
+int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp)
+{
+       uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t);
+       uint32_t handle = ntohl(bt_get_unaligned((uint32_t *) p));
+       sdp_record_t *rec;
+       int status = 0;
+
+       /* extract service record handle */
+
+       rec = sdp_record_find(handle);
+       if (rec) {
+               sdp_svcdb_collect(rec);
+               status = sdp_record_remove(handle);
+               sdp_record_free(rec);
+               if (status == 0)
+                       update_db_timestamp();
+       } else {
+               status = SDP_INVALID_RECORD_HANDLE;
+               SDPDBG("Could not find record : 0x%x", handle);
+       }
+
+       p = rsp->data;
+       bt_put_unaligned(htons(status), (uint16_t *) p);
+       rsp->data_size = sizeof(uint16_t);
+
+       return status;
+}
diff --git a/src/sdpd.h b/src/sdpd.h
new file mode 100644 (file)
index 0000000..83d2b03
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+
+#ifdef SDP_DEBUG
+#include <syslog.h>
+#define SDPDBG(fmt, arg...) syslog(LOG_DEBUG, "%s: " fmt "\n", __func__ , ## arg)
+#else
+#define SDPDBG(fmt...)
+#endif
+
+typedef struct request {
+       bdaddr_t device;
+       bdaddr_t bdaddr;
+       int      local;
+       int      sock;
+       int      mtu;
+       int      flags;
+       uint8_t  *buf;
+       int      len;
+} sdp_req_t;
+
+void handle_request(int sk, uint8_t *data, int len);
+
+int service_register_req(sdp_req_t *req, sdp_buf_t *rsp);
+int service_update_req(sdp_req_t *req, sdp_buf_t *rsp);
+int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp);
+
+void register_public_browse_group(void);
+void register_server_service(void);
+void register_device_id(void);
+
+int record_sort(const void *r1, const void *r2);
+void sdp_svcdb_reset(void);
+void sdp_svcdb_collect_all(int sock);
+void sdp_svcdb_set_collectable(sdp_record_t *rec, int sock);
+void sdp_svcdb_collect(sdp_record_t *rec);
+sdp_record_t *sdp_record_find(uint32_t handle);
+void sdp_record_add(const bdaddr_t *device, sdp_record_t *rec);
+int sdp_record_remove(uint32_t handle);
+sdp_list_t *sdp_get_record_list(void);
+sdp_list_t *sdp_get_access_list(void);
+int sdp_check_access(uint32_t handle, bdaddr_t *device);
+uint32_t sdp_next_handle(void);
+
+uint32_t sdp_get_time(void);
+
+#define SDP_SERVER_COMPAT (1 << 0)
+#define SDP_SERVER_MASTER (1 << 1)
+
+int start_sdp_server(uint16_t mtu, uint32_t flags);
+void stop_sdp_server(void);
+
+int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec);
+int remove_record_from_server(uint32_t handle);
+
+void sdp_init_services_list(bdaddr_t *device);
diff --git a/src/storage.c b/src/storage.c
new file mode 100644 (file)
index 0000000..17e8001
--- /dev/null
@@ -0,0 +1,1396 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
+
+#include "textfile.h"
+#include "glib-helper.h"
+#include "storage.h"
+
+struct match {
+       GSList *keys;
+       char *pattern;
+};
+
+static inline int create_filename(char *buf, size_t size,
+                               const bdaddr_t *bdaddr, const char *name)
+{
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+
+       return create_name(buf, size, STORAGEDIR, addr, name);
+}
+
+int read_device_alias(const char *src, const char *dst, char *alias, size_t size)
+{
+       char filename[PATH_MAX + 1], *tmp;
+       int err;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "aliases");
+
+       tmp = textfile_get(filename, dst);
+       if (!tmp)
+               return -ENXIO;
+
+       err = snprintf(alias, size, "%s", tmp);
+
+       free(tmp);
+
+       return err < 0 ? -EIO : 0;
+}
+
+int write_device_alias(const char *src, const char *dst, const char *alias)
+{
+       char filename[PATH_MAX + 1];
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "aliases");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       return textfile_put(filename, dst, alias);
+}
+
+int write_discoverable_timeout(bdaddr_t *bdaddr, int timeout)
+{
+       char filename[PATH_MAX + 1], str[32];
+
+       snprintf(str, sizeof(str), "%d", timeout);
+
+       create_filename(filename, PATH_MAX, bdaddr, "config");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       return textfile_put(filename, "discovto", str);
+}
+
+int read_discoverable_timeout(const char *src, int *timeout)
+{
+       char filename[PATH_MAX + 1], *str;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+       str = textfile_get(filename, "discovto");
+       if (!str)
+               return -ENOENT;
+
+       if (sscanf(str, "%d", timeout) != 1) {
+               free(str);
+               return -ENOENT;
+       }
+
+       free(str);
+
+       return 0;
+}
+
+int write_pairable_timeout(bdaddr_t *bdaddr, int timeout)
+{
+       char filename[PATH_MAX + 1], str[32];
+
+       snprintf(str, sizeof(str), "%d", timeout);
+
+       create_filename(filename, PATH_MAX, bdaddr, "config");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       return textfile_put(filename, "pairto", str);
+}
+
+int read_pairable_timeout(const char *src, int *timeout)
+{
+       char filename[PATH_MAX + 1], *str;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+       str = textfile_get(filename, "pairto");
+       if (!str)
+               return -ENOENT;
+
+       if (sscanf(str, "%d", timeout) != 1) {
+               free(str);
+               return -ENOENT;
+       }
+
+       free(str);
+
+       return 0;
+}
+
+int write_device_mode(bdaddr_t *bdaddr, const char *mode)
+{
+       char filename[PATH_MAX + 1];
+
+       create_filename(filename, PATH_MAX, bdaddr, "config");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       if (strcmp(mode, "off") != 0)
+               textfile_put(filename, "onmode", mode);
+
+       return textfile_put(filename, "mode", mode);
+}
+
+int read_device_mode(const char *src, char *mode, int length)
+{
+       char filename[PATH_MAX + 1], *str;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+       str = textfile_get(filename, "mode");
+       if (!str)
+               return -ENOENT;
+
+       strncpy(mode, str, length);
+       mode[length - 1] = '\0';
+
+       free(str);
+
+       return 0;
+}
+
+int read_on_mode(const char *src, char *mode, int length)
+{
+       char filename[PATH_MAX + 1], *str;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "config");
+
+       str = textfile_get(filename, "onmode");
+       if (!str)
+               return -ENOENT;
+
+       strncpy(mode, str, length);
+       mode[length - 1] = '\0';
+
+       free(str);
+
+       return 0;
+}
+
+int write_local_name(bdaddr_t *bdaddr, const char *name)
+{
+       char filename[PATH_MAX + 1], str[249];
+       int i;
+
+       memset(str, 0, sizeof(str));
+       for (i = 0; i < 248 && name[i]; i++)
+               if ((unsigned char) name[i] < 32 || name[i] == 127)
+                       str[i] = '.';
+               else
+                       str[i] = name[i];
+
+       create_filename(filename, PATH_MAX, bdaddr, "config");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       return textfile_put(filename, "name", str);
+}
+
+int read_local_name(bdaddr_t *bdaddr, char *name)
+{
+       char filename[PATH_MAX + 1], *str;
+       int len;
+
+       create_filename(filename, PATH_MAX, bdaddr, "config");
+
+       str = textfile_get(filename, "name");
+       if (!str)
+               return -ENOENT;
+
+       len = strlen(str);
+       if (len > HCI_MAX_NAME_LENGTH)
+               str[HCI_MAX_NAME_LENGTH] = '\0';
+       strcpy(name, str);
+
+       free(str);
+
+       return 0;
+}
+
+int write_local_class(bdaddr_t *bdaddr, uint8_t *class)
+{
+       char filename[PATH_MAX + 1], str[9];
+
+       sprintf(str, "0x%2.2x%2.2x%2.2x", class[2], class[1], class[0]);
+
+       create_filename(filename, PATH_MAX, bdaddr, "config");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       return textfile_put(filename, "class", str);
+}
+
+int read_local_class(bdaddr_t *bdaddr, uint8_t *class)
+{
+       char filename[PATH_MAX + 1], tmp[3], *str;
+       int i;
+
+       create_filename(filename, PATH_MAX, bdaddr, "config");
+
+       str = textfile_get(filename, "class");
+       if (!str)
+               return -ENOENT;
+
+       memset(tmp, 0, sizeof(tmp));
+       for (i = 0; i < 3; i++) {
+               memcpy(tmp, str + (i * 2) + 2, 2);
+               class[2 - i] = (uint8_t) strtol(tmp, NULL, 16);
+       }
+
+       free(str);
+
+       return 0;
+}
+
+int read_remote_appearance(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                                       uint16_t *appearance)
+{
+       char filename[PATH_MAX + 1], key[20], *str;
+
+       create_filename(filename, PATH_MAX, local, "appearances");
+
+       ba2str(peer, key);
+       sprintf(&key[17], "#%hhu", bdaddr_type);
+
+       str = textfile_get(filename, key);
+       if (!str)
+               return -ENOENT;
+
+       if (sscanf(str, "%hx", appearance) != 1) {
+               free(str);
+               return -ENOENT;
+       }
+
+       free(str);
+
+       return 0;
+}
+
+int write_remote_appearance(bdaddr_t *local, bdaddr_t *peer,
+                               uint8_t bdaddr_type, uint16_t appearance)
+{
+       char filename[PATH_MAX + 1], key[20], str[7];
+
+       create_filename(filename, PATH_MAX, local, "appearances");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(peer, key);
+       sprintf(&key[17], "#%hhu", bdaddr_type);
+
+       sprintf(str, "0x%4.4x", appearance);
+
+       return textfile_put(filename, key, str);
+}
+
+int write_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class)
+{
+       char filename[PATH_MAX + 1], addr[18], str[9];
+
+       create_filename(filename, PATH_MAX, local, "classes");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(peer, addr);
+       sprintf(str, "0x%6.6x", class);
+
+       return textfile_put(filename, addr, str);
+}
+
+int read_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t *class)
+{
+       char filename[PATH_MAX + 1], addr[18], *str;
+
+       create_filename(filename, PATH_MAX, local, "classes");
+
+       ba2str(peer, addr);
+
+       str = textfile_get(filename, addr);
+       if (!str)
+               return -ENOENT;
+
+       if (sscanf(str, "%x", class) != 1) {
+               free(str);
+               return -ENOENT;
+       }
+
+       free(str);
+
+       return 0;
+}
+
+int write_device_name(bdaddr_t *local, bdaddr_t *peer, char *name)
+{
+       char filename[PATH_MAX + 1], addr[18], str[HCI_MAX_NAME_LENGTH + 1];
+       int i;
+
+       memset(str, 0, sizeof(str));
+       for (i = 0; i < HCI_MAX_NAME_LENGTH && name[i]; i++)
+               if ((unsigned char) name[i] < 32 || name[i] == 127)
+                       str[i] = '.';
+               else
+                       str[i] = name[i];
+
+       create_filename(filename, PATH_MAX, local, "names");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(peer, addr);
+       return textfile_put(filename, addr, str);
+}
+
+int read_device_name(const char *src, const char *dst, char *name)
+{
+       char filename[PATH_MAX + 1], *str;
+       int len;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "names");
+
+       str = textfile_get(filename, dst);
+       if (!str)
+               return -ENOENT;
+
+       len = strlen(str);
+       if (len > HCI_MAX_NAME_LENGTH)
+               str[HCI_MAX_NAME_LENGTH] = '\0';
+       strcpy(name, str);
+
+       free(str);
+
+       return 0;
+}
+
+int write_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data,
+                                                       uint8_t data_len)
+{
+       char filename[PATH_MAX + 1], addr[18], str[481];
+       int i;
+
+       memset(str, 0, sizeof(str));
+       for (i = 0; i < data_len; i++)
+               sprintf(str + (i * 2), "%2.2X", data[i]);
+
+       create_filename(filename, PATH_MAX, local, "eir");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(peer, addr);
+       return textfile_put(filename, addr, str);
+}
+
+int read_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data)
+{
+       char filename[PATH_MAX + 1], addr[18], *str;
+       int i;
+
+       create_filename(filename, PATH_MAX, local, "eir");
+
+       ba2str(peer, addr);
+
+       str = textfile_get(filename, addr);
+       if (!str)
+               return -ENOENT;
+
+       if (!data) {
+               free(str);
+               return 0;
+       }
+
+       if (strlen(str) < 480) {
+               free(str);
+               return -EIO;
+       }
+
+       for (i = 0; i < HCI_MAX_EIR_LENGTH; i++)
+               sscanf(str + (i * 2), "%02hhX", &data[i]);
+
+       free(str);
+
+       return 0;
+}
+
+int write_version_info(bdaddr_t *local, bdaddr_t *peer, uint16_t manufacturer,
+                                       uint8_t lmp_ver, uint16_t lmp_subver)
+{
+       char filename[PATH_MAX + 1], addr[18], str[16];
+
+       memset(str, 0, sizeof(str));
+       sprintf(str, "%d %d %d", manufacturer, lmp_ver, lmp_subver);
+
+       create_filename(filename, PATH_MAX, local, "manufacturers");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(peer, addr);
+       return textfile_put(filename, addr, str);
+}
+
+int write_features_info(bdaddr_t *local, bdaddr_t *peer,
+                               unsigned char *page1, unsigned char *page2)
+{
+       char filename[PATH_MAX + 1], addr[18];
+       char str[] = "0000000000000000 0000000000000000";
+       char *old_value;
+       int i;
+
+       ba2str(peer, addr);
+
+       create_filename(filename, PATH_MAX, local, "features");
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       old_value = textfile_get(filename, addr);
+
+       if (page1)
+               for (i = 0; i < 8; i++)
+                       sprintf(str + (i * 2), "%2.2X", page1[i]);
+       else if (old_value && strlen(old_value) >= 16)
+               strncpy(str, old_value, 16);
+
+       if (page2)
+               for (i = 0; i < 8; i++)
+                       sprintf(str + 17 + (i * 2), "%2.2X", page2[i]);
+       else if (old_value && strlen(old_value) >= 33)
+               strncpy(str + 17, old_value + 17, 16);
+
+       free(old_value);
+
+       return textfile_put(filename, addr, str);
+}
+
+static int decode_bytes(const char *str, unsigned char *bytes, size_t len)
+{
+       unsigned int i;
+
+       for (i = 0; i < len; i++) {
+               if (sscanf(str + (i * 2), "%02hhX", &bytes[i]) != 1)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+int read_remote_features(bdaddr_t *local, bdaddr_t *peer,
+                               unsigned char *page1, unsigned char *page2)
+{
+       char filename[PATH_MAX + 1], addr[18], *str;
+       size_t len;
+       int err;
+
+       if (page1 == NULL && page2 == NULL)
+               return -EINVAL;
+
+       create_filename(filename, PATH_MAX, local, "features");
+
+       ba2str(peer, addr);
+
+       str = textfile_get(filename, addr);
+       if (!str)
+               return -ENOENT;
+
+       len = strlen(str);
+
+       err = -ENOENT;
+
+       if (page1 && len >= 16)
+               err = decode_bytes(str, page1, 8);
+
+       if (page2 && len >= 33)
+               err = decode_bytes(str + 17, page2, 8);
+
+       free(str);
+
+       return err;
+}
+
+int write_lastseen_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm)
+{
+       char filename[PATH_MAX + 1], addr[18], str[24];
+
+       memset(str, 0, sizeof(str));
+       strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S %Z", tm);
+
+       create_filename(filename, PATH_MAX, local, "lastseen");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(peer, addr);
+       return textfile_put(filename, addr, str);
+}
+
+int write_lastused_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm)
+{
+       char filename[PATH_MAX + 1], addr[18], str[24];
+
+       memset(str, 0, sizeof(str));
+       strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S %Z", tm);
+
+       create_filename(filename, PATH_MAX, local, "lastused");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(peer, addr);
+       return textfile_put(filename, addr, str);
+}
+
+int write_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t type, int length)
+{
+       char filename[PATH_MAX + 1], addr[18], str[38];
+       int i;
+
+       memset(str, 0, sizeof(str));
+       for (i = 0; i < 16; i++)
+               sprintf(str + (i * 2), "%2.2X", key[i]);
+       sprintf(str + 32, " %d %d", type, length);
+
+       create_filename(filename, PATH_MAX, local, "linkkeys");
+
+       create_file(filename, S_IRUSR | S_IWUSR);
+
+       ba2str(peer, addr);
+
+       if (length < 0) {
+               char *tmp = textfile_get(filename, addr);
+               if (tmp) {
+                       if (strlen(tmp) > 34)
+                               memcpy(str + 34, tmp + 34, 3);
+                       free(tmp);
+               }
+       }
+
+       return textfile_put(filename, addr, str);
+}
+
+int read_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t *type)
+{
+       char filename[PATH_MAX + 1], addr[18], tmp[3], *str;
+       int i;
+
+       create_filename(filename, PATH_MAX, local, "linkkeys");
+
+       ba2str(peer, addr);
+       str = textfile_get(filename, addr);
+       if (!str)
+               return -ENOENT;
+
+       if (!key) {
+               free(str);
+               return 0;
+       }
+
+       memset(tmp, 0, sizeof(tmp));
+       for (i = 0; i < 16; i++) {
+               memcpy(tmp, str + (i * 2), 2);
+               key[i] = (uint8_t) strtol(tmp, NULL, 16);
+       }
+
+       if (type) {
+               memcpy(tmp, str + 33, 2);
+               *type = (uint8_t) strtol(tmp, NULL, 10);
+       }
+
+       free(str);
+
+       return 0;
+}
+
+ssize_t read_pin_code(bdaddr_t *local, bdaddr_t *peer, char *pin)
+{
+       char filename[PATH_MAX + 1], addr[18], *str;
+       ssize_t len;
+
+       create_filename(filename, PATH_MAX, local, "pincodes");
+
+       ba2str(peer, addr);
+       str = textfile_get(filename, addr);
+       if (!str)
+               return -ENOENT;
+
+       strncpy(pin, str, 16);
+       len = strlen(pin);
+
+       free(str);
+
+       return len;
+}
+
+static GSList *service_string_to_list(char *services)
+{
+       GSList *l = NULL;
+       char *start = services;
+       int i, finished = 0;
+
+       for (i = 0; !finished; i++) {
+               if (services[i] == '\0')
+                       finished = 1;
+
+               if (services[i] == ' ' || services[i] == '\0') {
+                       services[i] = '\0';
+                       l = g_slist_append(l, start);
+                       start = services + i + 1;
+               }
+       }
+
+       return l;
+}
+
+static char *service_list_to_string(GSList *services)
+{
+       char str[1024];
+       int len = 0;
+
+       if (!services)
+               return g_strdup("");
+
+       memset(str, 0, sizeof(str));
+
+       while (services) {
+               int ret;
+               char *ident = services->data;
+
+               ret = snprintf(str + len, sizeof(str) - len - 1, "%s%s",
+                               ident, services->next ? " " : "");
+
+               if (ret > 0)
+                       len += ret;
+
+               services = services->next;
+       }
+
+       return g_strdup(str);
+}
+
+int write_trust(const char *src, const char *addr, const char *service,
+               gboolean trust)
+{
+       char filename[PATH_MAX + 1], *str;
+       GSList *services = NULL, *match;
+       gboolean trusted;
+       int ret;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "trusts");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       str = textfile_caseget(filename, addr);
+       if (str)
+               services = service_string_to_list(str);
+
+       match = g_slist_find_custom(services, service, (GCompareFunc) strcmp);
+       trusted = match ? TRUE : FALSE;
+
+       /* If the old setting is the same as the requested one, we're done */
+       if (trusted == trust) {
+               g_slist_free(services);
+               free(str);
+               return 0;
+       }
+
+       if (trust)
+               services = g_slist_append(services, (void *) service);
+       else
+               services = g_slist_remove(services, match->data);
+
+       /* Remove the entry if the last trusted service was removed */
+       if (!trust && !services)
+               ret = textfile_casedel(filename, addr);
+       else {
+               char *new_str = service_list_to_string(services);
+               ret = textfile_caseput(filename, addr, new_str);
+               g_free(new_str);
+       }
+
+       g_slist_free(services);
+
+       free(str);
+
+       return ret;
+}
+
+gboolean read_trust(const bdaddr_t *local, const char *addr, const char *service)
+{
+       char filename[PATH_MAX + 1], *str;
+       GSList *services;
+       gboolean ret;
+
+       create_filename(filename, PATH_MAX, local, "trusts");
+
+       str = textfile_caseget(filename, addr);
+       if (!str)
+               return FALSE;
+
+       services = service_string_to_list(str);
+
+       if (g_slist_find_custom(services, service, (GCompareFunc) strcmp))
+               ret = TRUE;
+       else
+               ret = FALSE;
+
+       g_slist_free(services);
+       free(str);
+
+       return ret;
+}
+
+int write_device_profiles(bdaddr_t *src, bdaddr_t *dst, const char *profiles)
+{
+       char filename[PATH_MAX + 1], addr[18];
+
+       if (!profiles)
+               return -EINVAL;
+
+       create_filename(filename, PATH_MAX, src, "profiles");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(dst, addr);
+       return textfile_put(filename, addr, profiles);
+}
+
+int delete_entry(bdaddr_t *src, const char *storage, const char *key)
+{
+       char filename[PATH_MAX + 1];
+
+       create_filename(filename, PATH_MAX, src, storage);
+
+       return textfile_del(filename, key);
+}
+
+int store_record(const gchar *src, const gchar *dst, sdp_record_t *rec)
+{
+       char filename[PATH_MAX + 1], key[28];
+       sdp_buf_t buf;
+       int err, size, i;
+       char *str;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       snprintf(key, sizeof(key), "%17s#%08X", dst, rec->handle);
+
+       if (sdp_gen_record_pdu(rec, &buf) < 0)
+               return -1;
+
+       size = buf.data_size;
+
+       str = g_malloc0(size*2+1);
+
+       for (i = 0; i < size; i++)
+               sprintf(str + (i * 2), "%02X", buf.data[i]);
+
+       err = textfile_put(filename, key, str);
+
+       free(buf.data);
+       g_free(str);
+
+       return err;
+}
+
+sdp_record_t *record_from_string(const gchar *str)
+{
+       sdp_record_t *rec;
+       int size, i, len;
+       uint8_t *pdata;
+       char tmp[3];
+
+       size = strlen(str)/2;
+       pdata = g_malloc0(size);
+
+       tmp[2] = 0;
+       for (i = 0; i < size; i++) {
+               memcpy(tmp, str + (i * 2), 2);
+               pdata[i] = (uint8_t) strtol(tmp, NULL, 16);
+       }
+
+       rec = sdp_extract_pdu(pdata, size, &len);
+       g_free(pdata);
+
+       return rec;
+}
+
+
+sdp_record_t *fetch_record(const gchar *src, const gchar *dst,
+                                               const uint32_t handle)
+{
+       char filename[PATH_MAX + 1], key[28], *str;
+       sdp_record_t *rec;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp");
+
+       snprintf(key, sizeof(key), "%17s#%08X", dst, handle);
+
+       str = textfile_get(filename, key);
+       if (!str)
+               return NULL;
+
+       rec = record_from_string(str);
+       free(str);
+
+       return rec;
+}
+
+int delete_record(const gchar *src, const gchar *dst, const uint32_t handle)
+{
+       char filename[PATH_MAX + 1], key[28];
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp");
+
+       snprintf(key, sizeof(key), "%17s#%08X", dst, handle);
+
+       return textfile_del(filename, key);
+}
+
+struct record_list {
+       sdp_list_t *recs;
+       const gchar *addr;
+};
+
+static void create_stored_records_from_keys(char *key, char *value,
+                                                       void *user_data)
+{
+       struct record_list *rec_list = user_data;
+       const gchar *addr = rec_list->addr;
+       sdp_record_t *rec;
+
+       if (strncmp(key, addr, 17))
+               return;
+
+       rec = record_from_string(value);
+
+       rec_list->recs = sdp_list_append(rec_list->recs, rec);
+}
+
+void delete_all_records(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       sdp_list_t *records, *seq;
+       char srcaddr[18], dstaddr[18];
+
+       ba2str(src, srcaddr);
+       ba2str(dst, dstaddr);
+
+       records = read_records(src, dst);
+
+       for (seq = records; seq; seq = seq->next) {
+               sdp_record_t *rec = seq->data;
+               delete_record(srcaddr, dstaddr, rec->handle);
+       }
+
+       if (records)
+               sdp_list_free(records, (sdp_free_func_t) sdp_record_free);
+}
+
+sdp_list_t *read_records(const bdaddr_t *src, const bdaddr_t *dst)
+{
+       char filename[PATH_MAX + 1];
+       struct record_list rec_list;
+       char srcaddr[18], dstaddr[18];
+
+       ba2str(src, srcaddr);
+       ba2str(dst, dstaddr);
+
+       rec_list.addr = dstaddr;
+       rec_list.recs = NULL;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "sdp");
+       textfile_foreach(filename, create_stored_records_from_keys, &rec_list);
+
+       return rec_list.recs;
+}
+
+sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid)
+{
+       sdp_list_t *seq;
+
+       for (seq = recs; seq; seq = seq->next) {
+               sdp_record_t *rec = (sdp_record_t *) seq->data;
+               sdp_list_t *svcclass = NULL;
+               char *uuid_str;
+
+               if (sdp_get_service_classes(rec, &svcclass) < 0)
+                       continue;
+
+               /* Extract the uuid */
+               uuid_str = bt_uuid2string(svcclass->data);
+               if (!uuid_str)
+                       continue;
+
+               if (!strcasecmp(uuid_str, uuid)) {
+                       sdp_list_free(svcclass, free);
+                       free(uuid_str);
+                       return rec;
+               }
+
+               sdp_list_free(svcclass, free);
+               free(uuid_str);
+       }
+       return NULL;
+}
+
+int store_device_id(const gchar *src, const gchar *dst,
+                               const uint16_t source, const uint16_t vendor,
+                               const uint16_t product, const uint16_t version)
+{
+       char filename[PATH_MAX + 1], str[20];
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "did");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       snprintf(str, sizeof(str), "%04X %04X %04X %04X", source,
+                                               vendor, product, version);
+
+       return textfile_put(filename, dst, str);
+}
+
+static int read_device_id_from_did(const gchar *src, const gchar *dst,
+                                       uint16_t *source, uint16_t *vendor,
+                                       uint16_t *product, uint16_t *version)
+{
+       char filename[PATH_MAX + 1];
+       char *str, *vendor_str, *product_str, *version_str;
+
+       create_name(filename, PATH_MAX, STORAGEDIR, src, "did");
+
+       str = textfile_get(filename, dst);
+       if (!str)
+               return -ENOENT;
+
+       vendor_str = strchr(str, ' ');
+       if (!vendor_str) {
+               free(str);
+               return -ENOENT;
+       }
+       *(vendor_str++) = 0;
+
+       product_str = strchr(vendor_str, ' ');
+       if (!product_str) {
+               free(str);
+               return -ENOENT;
+       }
+       *(product_str++) = 0;
+
+       version_str = strchr(product_str, ' ');
+       if (!version_str) {
+               free(str);
+               return -ENOENT;
+       }
+       *(version_str++) = 0;
+
+       if (source)
+               *source = (uint16_t) strtol(str, NULL, 16);
+       if (vendor)
+               *vendor = (uint16_t) strtol(vendor_str, NULL, 16);
+       if (product)
+               *product = (uint16_t) strtol(product_str, NULL, 16);
+       if (version)
+               *version = (uint16_t) strtol(version_str, NULL, 16);
+
+       free(str);
+
+       return 0;
+}
+
+int read_device_id(const gchar *srcaddr, const gchar *dstaddr,
+                                       uint16_t *source, uint16_t *vendor,
+                                       uint16_t *product, uint16_t *version)
+{
+       uint16_t lsource, lvendor, lproduct, lversion;
+       sdp_list_t *recs;
+       sdp_record_t *rec;
+       bdaddr_t src, dst;
+       int err;
+
+       err = read_device_id_from_did(srcaddr, dstaddr, &lsource,
+                                               vendor, product, version);
+       if (!err) {
+               if (lsource == 0xffff)
+                       err = -ENOENT;
+
+               return err;
+       }
+
+       str2ba(srcaddr, &src);
+       str2ba(dstaddr, &dst);
+
+       recs = read_records(&src, &dst);
+       rec = find_record_in_list(recs, PNP_UUID);
+
+       if (rec) {
+               sdp_data_t *pdlist;
+
+               pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID_SOURCE);
+               lsource = pdlist ? pdlist->val.uint16 : 0x0000;
+
+               pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
+               lvendor = pdlist ? pdlist->val.uint16 : 0x0000;
+
+               pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
+               lproduct = pdlist ? pdlist->val.uint16 : 0x0000;
+
+               pdlist = sdp_data_get(rec, SDP_ATTR_VERSION);
+               lversion = pdlist ? pdlist->val.uint16 : 0x0000;
+
+               err = 0;
+       }
+
+       sdp_list_free(recs, (sdp_free_func_t)sdp_record_free);
+
+       if (err) {
+               /* FIXME: We should try EIR data if we have it, too */
+
+               /* If we don't have the data, we don't want to go through the
+                * above search every time. */
+               lsource = 0xffff;
+               lvendor = 0x0000;
+               lproduct = 0x0000;
+               lversion = 0x0000;
+       }
+
+       store_device_id(srcaddr, dstaddr, lsource, lvendor, lproduct, lversion);
+
+       if (err)
+               return err;
+
+       if (source)
+               *source = lsource;
+       if (vendor)
+               *vendor = lvendor;
+       if (product)
+               *product = lproduct;
+       if (version)
+               *version = lversion;
+
+       return 0;
+}
+
+int write_device_pairable(bdaddr_t *bdaddr, gboolean mode)
+{
+       char filename[PATH_MAX + 1];
+
+       create_filename(filename, PATH_MAX, bdaddr, "config");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       return textfile_put(filename, "pairable", mode ? "yes" : "no");
+}
+
+int read_device_pairable(bdaddr_t *bdaddr, gboolean *mode)
+{
+       char filename[PATH_MAX + 1], *str;
+
+       create_filename(filename, PATH_MAX, bdaddr, "config");
+
+       str = textfile_get(filename, "pairable");
+       if (!str)
+               return -ENOENT;
+
+       *mode = strcmp(str, "yes") == 0 ? TRUE : FALSE;
+
+       free(str);
+
+       return 0;
+}
+
+gboolean read_blocked(const bdaddr_t *local, const bdaddr_t *remote)
+{
+       char filename[PATH_MAX + 1], *str, addr[18];
+
+       create_filename(filename, PATH_MAX, local, "blocked");
+
+       ba2str(remote, addr);
+
+       str = textfile_caseget(filename, addr);
+       if (!str)
+               return FALSE;
+
+       free(str);
+
+       return TRUE;
+}
+
+int write_blocked(const bdaddr_t *local, const bdaddr_t *remote,
+                                                       gboolean blocked)
+{
+       char filename[PATH_MAX + 1], addr[18];
+
+       create_filename(filename, PATH_MAX, local, "blocked");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(remote, addr);
+
+       if (blocked == FALSE)
+               return textfile_casedel(filename, addr);
+
+       return textfile_caseput(filename, addr, "");
+}
+
+int write_device_services(const bdaddr_t *sba, const bdaddr_t *dba,
+                         uint8_t bdaddr_type, const char *services)
+{
+       char filename[PATH_MAX + 1], key[20];
+
+       create_filename(filename, PATH_MAX, sba, "primaries");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(dba, key);
+       sprintf(&key[17], "#%hhu", bdaddr_type);
+
+       return textfile_put(filename, key, services);
+}
+
+static void filter_keys(char *key, char *value, void *data)
+{
+       struct match *match = data;
+
+       if (strncasecmp(key, match->pattern, strlen(match->pattern)) == 0)
+               match->keys = g_slist_append(match->keys, g_strdup(key));
+}
+
+static void delete_by_pattern(const char *filename, char *pattern)
+{
+       struct match match;
+       GSList *l;
+       int err;
+
+       memset(&match, 0, sizeof(match));
+       match.pattern = pattern;
+
+       err = textfile_foreach(filename, filter_keys, &match);
+       if (err < 0)
+               goto done;
+
+       for (l = match.keys; l; l = l->next) {
+               const char *key = l->data;
+               textfile_del(filename, key);
+       }
+
+done:
+       g_slist_free_full(match.keys, g_free);
+}
+
+int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba,
+                                               uint8_t bdaddr_type)
+{
+       char filename[PATH_MAX + 1], key[20];
+
+       memset(key, 0, sizeof(key));
+
+       ba2str(dba, key);
+       sprintf(&key[17], "#%hhu", bdaddr_type);
+
+       /* Deleting all characteristics of a given key */
+       create_filename(filename, PATH_MAX, sba, "characteristics");
+       delete_by_pattern(filename, key);
+
+       /* Deleting all attributes values of a given key */
+       create_filename(filename, PATH_MAX, sba, "attributes");
+       delete_by_pattern(filename, key);
+
+       /* Deleting all CCC values of a given key */
+       create_filename(filename, PATH_MAX, sba, "ccc");
+       delete_by_pattern(filename, key);
+
+       create_filename(filename, PATH_MAX, sba, "primaries");
+
+       return textfile_del(filename, key);
+}
+
+char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba,
+                                                       uint8_t bdaddr_type)
+{
+       char filename[PATH_MAX + 1], key[20];
+
+       create_filename(filename, PATH_MAX, sba, "primaries");
+
+       ba2str(dba, key);
+       sprintf(&key[17], "#%hhu", bdaddr_type);
+
+       return textfile_caseget(filename, key);
+}
+
+int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+                                       uint8_t bdaddr_type, uint16_t handle,
+                                                             const char *chars)
+{
+       char filename[PATH_MAX + 1], addr[18], key[25];
+
+       create_filename(filename, PATH_MAX, sba, "characteristics");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(dba, addr);
+       snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+       return textfile_put(filename, key, chars);
+}
+
+char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+                                       uint8_t bdaddr_type, uint16_t handle)
+{
+       char filename[PATH_MAX + 1], addr[18], key[25];
+
+       create_filename(filename, PATH_MAX, sba, "characteristics");
+
+       ba2str(dba, addr);
+       snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+       return textfile_caseget(filename, key);
+}
+
+int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
+                               uint8_t bdaddr_type, uint16_t handle,
+                                                       const char *chars)
+{
+       char filename[PATH_MAX + 1], addr[18], key[25];
+
+       create_filename(filename, PATH_MAX, sba, "attributes");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(dba, addr);
+       snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+       return textfile_put(filename, key, chars);
+}
+
+int read_device_attributes(const bdaddr_t *sba, textfile_cb func, void *data)
+{
+       char filename[PATH_MAX + 1];
+
+       create_filename(filename, PATH_MAX, sba, "attributes");
+
+       return textfile_foreach(filename, func, data);
+}
+
+int read_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                       uint16_t handle, uint16_t *value)
+{
+       char filename[PATH_MAX + 1], addr[18], key[25];
+       char *str;
+       unsigned int config;
+       int err = 0;
+
+       create_filename(filename, PATH_MAX, local, "ccc");
+
+       ba2str(peer, addr);
+       snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+       str = textfile_caseget(filename, key);
+       if (str == NULL)
+               return -ENOENT;
+
+       if (sscanf(str, "%04X", &config) != 1)
+               err = -ENOENT;
+       else
+               *value = config;
+
+       free(str);
+
+       return err;
+}
+
+int write_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                       uint16_t handle, uint16_t value)
+{
+       char filename[PATH_MAX + 1], addr[18], key[25], config[5];
+
+       create_filename(filename, PATH_MAX, local, "ccc");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(peer, addr);
+       snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+       snprintf(config, sizeof(config), "%04X", value);
+
+       return textfile_put(filename, key, config);
+}
+
+void delete_device_ccc(bdaddr_t *local, bdaddr_t *peer)
+{
+       char filename[PATH_MAX + 1], addr[18];
+
+       ba2str(peer, addr);
+
+       /* Deleting all CCC values of a given address */
+       create_filename(filename, PATH_MAX, local, "ccc");
+       delete_by_pattern(filename, addr);
+}
+
+int write_longtermkeys(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                                               const char *key)
+{
+       char filename[PATH_MAX + 1], addr[20];
+
+       if (!key)
+               return -EINVAL;
+
+       create_filename(filename, PATH_MAX, local, "longtermkeys");
+
+       create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       ba2str(peer, addr);
+       sprintf(&addr[17], "#%hhu", bdaddr_type);
+
+       return textfile_put(filename, addr, key);
+}
+
+gboolean has_longtermkeys(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type)
+{
+       char filename[PATH_MAX + 1], key[20], *str;
+
+       create_filename(filename, PATH_MAX, local, "longtermkeys");
+
+       ba2str(peer, key);
+       sprintf(&key[17], "#%hhu", bdaddr_type);
+
+       str = textfile_caseget(filename, key);
+       if (str) {
+               free(str);
+               return TRUE;
+       }
+
+       return FALSE;
+}
diff --git a/src/storage.h b/src/storage.h
new file mode 100644 (file)
index 0000000..cc00e97
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "textfile.h"
+
+int read_device_alias(const char *src, const char *dst, char *alias, size_t size);
+int write_device_alias(const char *src, const char *dst, const char *alias);
+int write_discoverable_timeout(bdaddr_t *bdaddr, int timeout);
+int read_discoverable_timeout(const char *src, int *timeout);
+int write_pairable_timeout(bdaddr_t *bdaddr, int timeout);
+int read_pairable_timeout(const char *src, int *timeout);
+int write_device_mode(bdaddr_t *bdaddr, const char *mode);
+int read_device_mode(const char *src, char *mode, int length);
+int read_on_mode(const char *src, char *mode, int length);
+int write_local_name(bdaddr_t *bdaddr, const char *name);
+int read_local_name(bdaddr_t *bdaddr, char *name);
+int write_local_class(bdaddr_t *bdaddr, uint8_t *class);
+int read_local_class(bdaddr_t *bdaddr, uint8_t *class);
+int write_remote_appearance(bdaddr_t *local, bdaddr_t *peer,
+                               uint8_t bdaddr_type, uint16_t appearance);
+int read_remote_appearance(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                                       uint16_t *appearance);
+int write_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class);
+int read_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t *class);
+int write_device_name(bdaddr_t *local, bdaddr_t *peer, char *name);
+int read_device_name(const char *src, const char *dst, char *name);
+int write_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data,
+                                                       uint8_t data_len);
+int read_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data);
+int write_version_info(bdaddr_t *local, bdaddr_t *peer, uint16_t manufacturer, uint8_t lmp_ver, uint16_t lmp_subver);
+int write_features_info(bdaddr_t *local, bdaddr_t *peer, unsigned char *page1, unsigned char *page2);
+int read_remote_features(bdaddr_t *local, bdaddr_t *peer, unsigned char *page1, unsigned char *page2);
+int write_lastseen_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm);
+int write_lastused_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm);
+int write_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t type, int length);
+int read_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t *type);
+ssize_t read_pin_code(bdaddr_t *local, bdaddr_t *peer, char *pin);
+gboolean read_trust(const bdaddr_t *local, const char *addr, const char *service);
+int write_trust(const char *src, const char *addr, const char *service, gboolean trust);
+int write_device_profiles(bdaddr_t *src, bdaddr_t *dst, const char *profiles);
+int delete_entry(bdaddr_t *src, const char *storage, const char *key);
+int store_record(const gchar *src, const gchar *dst, sdp_record_t *rec);
+sdp_record_t *record_from_string(const gchar *str);
+sdp_record_t *fetch_record(const gchar *src, const gchar *dst, const uint32_t handle);
+int delete_record(const gchar *src, const gchar *dst, const uint32_t handle);
+void delete_all_records(const bdaddr_t *src, const bdaddr_t *dst);
+sdp_list_t *read_records(const bdaddr_t *src, const bdaddr_t *dst);
+sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid);
+int store_device_id(const gchar *src, const gchar *dst,
+                               const uint16_t source, const uint16_t vendor,
+                               const uint16_t product, const uint16_t version);
+int read_device_id(const gchar *src, const gchar *dst,
+                                       uint16_t *source, uint16_t *vendor,
+                                       uint16_t *product, uint16_t *version);
+int write_device_pairable(bdaddr_t *local, gboolean mode);
+int read_device_pairable(bdaddr_t *local, gboolean *mode);
+gboolean read_blocked(const bdaddr_t *local, const bdaddr_t *remote);
+int write_blocked(const bdaddr_t *local, const bdaddr_t *remote,
+                                                       gboolean blocked);
+int write_device_services(const bdaddr_t *sba, const bdaddr_t *dba,
+                               uint8_t bdaddr_type, const char *services);
+int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba,
+                                               uint8_t bdaddr_type);
+char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba,
+                                                       uint8_t bdaddr_type);
+int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+                                       uint8_t bdaddr_type, uint16_t handle,
+                                                            const char *chars);
+char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+                                       uint8_t bdaddr_type, uint16_t handle);
+int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
+                               uint8_t bdaddr_type, uint16_t handle,
+                                                       const char *chars);
+int read_device_attributes(const bdaddr_t *sba, textfile_cb func, void *data);
+int read_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                       uint16_t handle, uint16_t *value);
+int write_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                       uint16_t handle, uint16_t value);
+void delete_device_ccc(bdaddr_t *local, bdaddr_t *peer);
+int write_longtermkeys(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+                                                       const char *key);
+gboolean has_longtermkeys(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type);
diff --git a/src/textfile.c b/src/textfile.c
new file mode 100644 (file)
index 0000000..9d88fbc
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include "textfile.h"
+
+int create_dirs(const char *filename, const mode_t mode)
+{
+       struct stat st;
+       char dir[PATH_MAX + 1], *prev, *next;
+       int err;
+
+       err = stat(filename, &st);
+       if (!err && S_ISREG(st.st_mode))
+               return 0;
+
+       memset(dir, 0, PATH_MAX + 1);
+       strcat(dir, "/");
+
+       prev = strchr(filename, '/');
+
+       while (prev) {
+               next = strchr(prev + 1, '/');
+               if (!next)
+                       break;
+
+               if (next - prev == 1) {
+                       prev = next;
+                       continue;
+               }
+
+               strncat(dir, prev + 1, next - prev);
+               mkdir(dir, mode);
+
+               prev = next;
+       }
+
+       return 0;
+}
+
+int create_file(const char *filename, const mode_t mode)
+{
+       int fd;
+
+       umask(S_IWGRP | S_IWOTH);
+       create_dirs(filename, S_IRUSR | S_IWUSR | S_IXUSR |
+                                       S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+
+       fd = open(filename, O_RDWR | O_CREAT, mode);
+       if (fd < 0)
+               return fd;
+
+       close(fd);
+
+       return 0;
+}
+
+int create_name(char *buf, size_t size, const char *path, const char *address, const char *name)
+{
+       return snprintf(buf, size, "%s/%s/%s", path, address, name);
+}
+
+static inline char *find_key(char *map, size_t size, const char *key, size_t len, int icase)
+{
+       char *ptr = map;
+       size_t ptrlen = size;
+
+       while (ptrlen > len + 1) {
+               int cmp = (icase) ? strncasecmp(ptr, key, len) : strncmp(ptr, key, len);
+               if (cmp == 0) {
+                       if (ptr == map)
+                               return ptr;
+
+                       if ((*(ptr - 1) == '\r' || *(ptr - 1) == '\n') &&
+                                                       *(ptr + len) == ' ')
+                               return ptr;
+               }
+
+               if (icase) {
+                       char *p1 = memchr(ptr + 1, tolower(*key), ptrlen - 1);
+                       char *p2 = memchr(ptr + 1, toupper(*key), ptrlen - 1);
+
+                       if (!p1)
+                               ptr = p2;
+                       else if (!p2)
+                               ptr = p1;
+                       else
+                               ptr = (p1 < p2) ? p1 : p2;
+               } else
+                       ptr = memchr(ptr + 1, *key, ptrlen - 1);
+
+               if (!ptr)
+                       return NULL;
+
+               ptrlen = size - (ptr - map);
+       }
+
+       return NULL;
+}
+
+static inline int write_key_value(int fd, const char *key, const char *value)
+{
+       char *str;
+       size_t size;
+       int err = 0;
+
+       size = strlen(key) + strlen(value) + 2;
+
+       str = malloc(size + 1);
+       if (!str)
+               return ENOMEM;
+
+       sprintf(str, "%s %s\n", key, value);
+
+       if (write(fd, str, size) < 0)
+               err = -errno;
+
+       free(str);
+
+       return err;
+}
+
+static char *strnpbrk(const char *s, ssize_t len, const char *accept)
+{
+       const char *p = s;
+       const char *end;
+
+       end = s + len - 1;
+
+       while (p <= end && *p) {
+               const char *a = accept;
+
+               while (*a) {
+                       if (*p == *a)
+                               return (char *) p;
+                       a++;
+               }
+
+               p++;
+       }
+
+       return NULL;
+}
+
+static int write_key(const char *pathname, const char *key, const char *value, int icase)
+{
+       struct stat st;
+       char *map, *off, *end, *str;
+       off_t size;
+       size_t base;
+       int fd, len, err = 0;
+
+       fd = open(pathname, O_RDWR);
+       if (fd < 0)
+               return -errno;
+
+       if (flock(fd, LOCK_EX) < 0) {
+               err = -errno;
+               goto close;
+       }
+
+       if (fstat(fd, &st) < 0) {
+               err = -errno;
+               goto unlock;
+       }
+
+       size = st.st_size;
+
+       if (!size) {
+               if (value) {
+                       lseek(fd, size, SEEK_SET);
+                       err = write_key_value(fd, key, value);
+               }
+               goto unlock;
+       }
+
+       map = mmap(NULL, size, PROT_READ | PROT_WRITE,
+                                       MAP_PRIVATE | MAP_LOCKED, fd, 0);
+       if (!map || map == MAP_FAILED) {
+               err = -errno;
+               goto unlock;
+       }
+
+       len = strlen(key);
+       off = find_key(map, size, key, len, icase);
+       if (!off) {
+               munmap(map, size);
+               if (value) {
+                       lseek(fd, size, SEEK_SET);
+                       err = write_key_value(fd, key, value);
+               }
+               goto unlock;
+       }
+
+       base = off - map;
+
+       end = strnpbrk(off, size, "\r\n");
+       if (!end) {
+               err = -EILSEQ;
+               goto unmap;
+       }
+
+       if (value && ((ssize_t) strlen(value) == end - off - len - 1) &&
+                       !strncmp(off + len + 1, value, end - off - len - 1))
+               goto unmap;
+
+       len = strspn(end, "\r\n");
+       end += len;
+
+       len = size - (end - map);
+       if (!len) {
+               munmap(map, size);
+               if (ftruncate(fd, base) < 0) {
+                       err = -errno;
+                       goto unlock;
+               }
+               lseek(fd, base, SEEK_SET);
+               if (value)
+                       err = write_key_value(fd, key, value);
+
+               goto unlock;
+       }
+
+       if (len < 0 || len > size) {
+               err = -EILSEQ;
+               goto unmap;
+       }
+
+       str = malloc(len);
+       if (!str) {
+               err = -errno;
+               goto unmap;
+       }
+
+       memcpy(str, end, len);
+
+       munmap(map, size);
+       if (ftruncate(fd, base) < 0) {
+               err = -errno;
+               free(str);
+               goto unlock;
+       }
+       lseek(fd, base, SEEK_SET);
+       if (value)
+               err = write_key_value(fd, key, value);
+
+       if (write(fd, str, len) < 0)
+               err = -errno;
+
+       free(str);
+
+       goto unlock;
+
+unmap:
+       munmap(map, size);
+
+unlock:
+       flock(fd, LOCK_UN);
+
+close:
+       fdatasync(fd);
+
+       close(fd);
+       errno = -err;
+
+       return err;
+}
+
+static char *read_key(const char *pathname, const char *key, int icase)
+{
+       struct stat st;
+       char *map, *off, *end, *str = NULL;
+       off_t size; size_t len;
+       int fd, err = 0;
+
+       fd = open(pathname, O_RDONLY);
+       if (fd < 0)
+               return NULL;
+
+       if (flock(fd, LOCK_SH) < 0) {
+               err = -errno;
+               goto close;
+       }
+
+       if (fstat(fd, &st) < 0) {
+               err = -errno;
+               goto unlock;
+       }
+
+       size = st.st_size;
+
+       map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+       if (!map || map == MAP_FAILED) {
+               err = -errno;
+               goto unlock;
+       }
+
+       len = strlen(key);
+       off = find_key(map, size, key, len, icase);
+       if (!off) {
+               err = -EILSEQ;
+               goto unmap;
+       }
+
+       end = strnpbrk(off, size - (map - off), "\r\n");
+       if (!end) {
+               err = -EILSEQ;
+               goto unmap;
+       }
+
+       str = malloc(end - off - len);
+       if (!str) {
+               err = -EILSEQ;
+               goto unmap;
+       }
+
+       memset(str, 0, end - off - len);
+       strncpy(str, off + len + 1, end - off - len - 1);
+
+unmap:
+       munmap(map, size);
+
+unlock:
+       flock(fd, LOCK_UN);
+
+close:
+       close(fd);
+       errno = -err;
+
+       return str;
+}
+
+int textfile_put(const char *pathname, const char *key, const char *value)
+{
+       return write_key(pathname, key, value, 0);
+}
+
+int textfile_caseput(const char *pathname, const char *key, const char *value)
+{
+       return write_key(pathname, key, value, 1);
+}
+
+int textfile_del(const char *pathname, const char *key)
+{
+       return write_key(pathname, key, NULL, 0);
+}
+
+int textfile_casedel(const char *pathname, const char *key)
+{
+       return write_key(pathname, key, NULL, 1);
+}
+
+char *textfile_get(const char *pathname, const char *key)
+{
+       return read_key(pathname, key, 0);
+}
+
+char *textfile_caseget(const char *pathname, const char *key)
+{
+       return read_key(pathname, key, 1);
+}
+
+int textfile_foreach(const char *pathname, textfile_cb func, void *data)
+{
+       struct stat st;
+       char *map, *off, *end, *key, *value;
+       off_t size; size_t len;
+       int fd, err = 0;
+
+       fd = open(pathname, O_RDONLY);
+       if (fd < 0)
+               return -errno;
+
+       if (flock(fd, LOCK_SH) < 0) {
+               err = -errno;
+               goto close;
+       }
+
+       if (fstat(fd, &st) < 0) {
+               err = -errno;
+               goto unlock;
+       }
+
+       size = st.st_size;
+
+       map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+       if (!map || map == MAP_FAILED) {
+               err = -errno;
+               goto unlock;
+       }
+
+       off = map;
+
+       while (size - (off - map) > 0) {
+               end = strnpbrk(off, size - (off - map), " ");
+               if (!end) {
+                       err = -EILSEQ;
+                       break;
+               }
+
+               len = end - off;
+
+               key = malloc(len + 1);
+               if (!key) {
+                       err = -errno;
+                       break;
+               }
+
+               memset(key, 0, len + 1);
+               memcpy(key, off, len);
+
+               off = end + 1;
+
+               if (size - (off - map) < 0) {
+                       err = -EILSEQ;
+                       free(key);
+                       break;
+               }
+
+               end = strnpbrk(off, size - (off - map), "\r\n");
+               if (!end) {
+                       err = -EILSEQ;
+                       free(key);
+                       break;
+               }
+
+               len = end - off;
+
+               value = malloc(len + 1);
+               if (!value) {
+                       err = -errno;
+                       free(key);
+                       break;
+               }
+
+               memset(value, 0, len + 1);
+               memcpy(value, off, len);
+
+               func(key, value, data);
+
+               free(key);
+               free(value);
+
+               off = end + 1;
+       }
+
+       munmap(map, size);
+
+unlock:
+       flock(fd, LOCK_UN);
+
+close:
+       close(fd);
+       errno = -err;
+
+       return 0;
+}
diff --git a/src/textfile.h b/src/textfile.h
new file mode 100644 (file)
index 0000000..dc5fc2b
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __TEXTFILE_H
+#define __TEXTFILE_H
+
+int create_dirs(const char *filename, const mode_t mode);
+int create_file(const char *filename, const mode_t mode);
+int create_name(char *buf, size_t size, const char *path,
+                               const char *address, const char *name);
+
+int textfile_put(const char *pathname, const char *key, const char *value);
+int textfile_caseput(const char *pathname, const char *key, const char *value);
+int textfile_del(const char *pathname, const char *key);
+int textfile_casedel(const char *pathname, const char *key);
+char *textfile_get(const char *pathname, const char *key);
+char *textfile_caseget(const char *pathname, const char *key);
+
+typedef void (*textfile_cb) (char *key, char *value, void *data);
+
+int textfile_foreach(const char *pathname, textfile_cb func, void *data);
+
+#endif /* __TEXTFILE_H */
diff --git a/src/uinput.h b/src/uinput.h
new file mode 100644 (file)
index 0000000..20e0941
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __UINPUT_H
+#define __UINPUT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+/* Events */
+
+#define EV_SYN                 0x00
+#define EV_KEY                 0x01
+#define EV_REL                 0x02
+#define EV_ABS                 0x03
+#define EV_MSC                 0x04
+#define EV_LED                 0x11
+#define EV_SND                 0x12
+#define EV_REP                 0x14
+#define EV_FF                  0x15
+#define EV_PWR                 0x16
+#define EV_FF_STATUS           0x17
+#define EV_MAX                 0x1f
+
+/* Synchronization events */
+
+#define SYN_REPORT             0
+#define SYN_CONFIG             1
+
+/*
+ * Keys and buttons
+ *
+ * Most of the keys/buttons are modelled after USB HUT 1.12
+ * (see http://www.usb.org/developers/hidpage).
+ * Abbreviations in the comments:
+ * AC - Application Control
+ * AL - Application Launch Button
+ * SC - System Control
+ */
+
+#define KEY_RESERVED           0
+#define KEY_ESC                        1
+#define KEY_1                  2
+#define KEY_2                  3
+#define KEY_3                  4
+#define KEY_4                  5
+#define KEY_5                  6
+#define KEY_6                  7
+#define KEY_7                  8
+#define KEY_8                  9
+#define KEY_9                  10
+#define KEY_0                  11
+#define KEY_MINUS              12
+#define KEY_EQUAL              13
+#define KEY_BACKSPACE          14
+#define KEY_TAB                        15
+#define KEY_Q                  16
+#define KEY_W                  17
+#define KEY_E                  18
+#define KEY_R                  19
+#define KEY_T                  20
+#define KEY_Y                  21
+#define KEY_U                  22
+#define KEY_I                  23
+#define KEY_O                  24
+#define KEY_P                  25
+#define KEY_LEFTBRACE          26
+#define KEY_RIGHTBRACE         27
+#define KEY_ENTER              28
+#define KEY_LEFTCTRL           29
+#define KEY_A                  30
+#define KEY_S                  31
+#define KEY_D                  32
+#define KEY_F                  33
+#define KEY_G                  34
+#define KEY_H                  35
+#define KEY_J                  36
+#define KEY_K                  37
+#define KEY_L                  38
+#define KEY_SEMICOLON          39
+#define KEY_APOSTROPHE         40
+#define KEY_GRAVE              41
+#define KEY_LEFTSHIFT          42
+#define KEY_BACKSLASH          43
+#define KEY_Z                  44
+#define KEY_X                  45
+#define KEY_C                  46
+#define KEY_V                  47
+#define KEY_B                  48
+#define KEY_N                  49
+#define KEY_M                  50
+#define KEY_COMMA              51
+#define KEY_DOT                        52
+#define KEY_SLASH              53
+#define KEY_RIGHTSHIFT         54
+#define KEY_KPASTERISK         55
+#define KEY_LEFTALT            56
+#define KEY_SPACE              57
+#define KEY_CAPSLOCK           58
+#define KEY_F1                 59
+#define KEY_F2                 60
+#define KEY_F3                 61
+#define KEY_F4                 62
+#define KEY_F5                 63
+#define KEY_F6                 64
+#define KEY_F7                 65
+#define KEY_F8                 66
+#define KEY_F9                 67
+#define KEY_F10                        68
+#define KEY_NUMLOCK            69
+#define KEY_SCROLLLOCK         70
+#define KEY_KP7                        71
+#define KEY_KP8                        72
+#define KEY_KP9                        73
+#define KEY_KPMINUS            74
+#define KEY_KP4                        75
+#define KEY_KP5                        76
+#define KEY_KP6                        77
+#define KEY_KPPLUS             78
+#define KEY_KP1                        79
+#define KEY_KP2                        80
+#define KEY_KP3                        81
+#define KEY_KP0                        82
+#define KEY_KPDOT              83
+
+#define KEY_ZENKAKUHANKAKU     85
+#define KEY_102ND              86
+#define KEY_F11                        87
+#define KEY_F12                        88
+#define KEY_RO                 89
+#define KEY_KATAKANA           90
+#define KEY_HIRAGANA           91
+#define KEY_HENKAN             92
+#define KEY_KATAKANAHIRAGANA   93
+#define KEY_MUHENKAN           94
+#define KEY_KPJPCOMMA          95
+#define KEY_KPENTER            96
+#define KEY_RIGHTCTRL          97
+#define KEY_KPSLASH            98
+#define KEY_SYSRQ              99
+#define KEY_RIGHTALT           100
+#define KEY_LINEFEED           101
+#define KEY_HOME               102
+#define KEY_UP                 103
+#define KEY_PAGEUP             104
+#define KEY_LEFT               105
+#define KEY_RIGHT              106
+#define KEY_END                        107
+#define KEY_DOWN               108
+#define KEY_PAGEDOWN           109
+#define KEY_INSERT             110
+#define KEY_DELETE             111
+#define KEY_MACRO              112
+#define KEY_MUTE               113
+#define KEY_VOLUMEDOWN         114
+#define KEY_VOLUMEUP           115
+#define KEY_POWER              116     /* SC System Power Down */
+#define KEY_KPEQUAL            117
+#define KEY_KPPLUSMINUS                118
+#define KEY_PAUSE              119
+
+#define KEY_KPCOMMA            121
+#define KEY_HANGEUL            122
+#define KEY_HANGUEL            KEY_HANGEUL
+#define KEY_HANJA              123
+#define KEY_YEN                        124
+#define KEY_LEFTMETA           125
+#define KEY_RIGHTMETA          126
+#define KEY_COMPOSE            127
+
+#define KEY_STOP               128     /* AC Stop */
+#define KEY_AGAIN              129
+#define KEY_PROPS              130     /* AC Properties */
+#define KEY_UNDO               131     /* AC Undo */
+#define KEY_FRONT              132
+#define KEY_COPY               133     /* AC Copy */
+#define KEY_OPEN               134     /* AC Open */
+#define KEY_PASTE              135     /* AC Paste */
+#define KEY_FIND               136     /* AC Search */
+#define KEY_CUT                        137     /* AC Cut */
+#define KEY_HELP               138     /* AL Integrated Help Center */
+#define KEY_MENU               139     /* Menu (show menu) */
+#define KEY_CALC               140     /* AL Calculator */
+#define KEY_SETUP              141
+#define KEY_SLEEP              142     /* SC System Sleep */
+#define KEY_WAKEUP             143     /* System Wake Up */
+#define KEY_FILE               144     /* AL Local Machine Browser */
+#define KEY_SENDFILE           145
+#define KEY_DELETEFILE         146
+#define KEY_XFER               147
+#define KEY_PROG1              148
+#define KEY_PROG2              149
+#define KEY_WWW                        150     /* AL Internet Browser */
+#define KEY_MSDOS              151
+#define KEY_COFFEE             152     /* AL Terminal Lock/Screensaver */
+#define KEY_SCREENLOCK         KEY_COFFEE
+#define KEY_DIRECTION          153
+#define KEY_CYCLEWINDOWS       154
+#define KEY_MAIL               155
+#define KEY_BOOKMARKS          156     /* AC Bookmarks */
+#define KEY_COMPUTER           157
+#define KEY_BACK               158     /* AC Back */
+#define KEY_FORWARD            159     /* AC Forward */
+#define KEY_CLOSECD            160
+#define KEY_EJECTCD            161
+#define KEY_EJECTCLOSECD       162
+#define KEY_NEXTSONG           163
+#define KEY_PLAYPAUSE          164
+#define KEY_PREVIOUSSONG       165
+#define KEY_STOPCD             166
+#define KEY_RECORD             167
+#define KEY_REWIND             168
+#define KEY_PHONE              169     /* Media Select Telephone */
+#define KEY_ISO                        170
+#define KEY_CONFIG             171     /* AL Consumer Control Configuration */
+#define KEY_HOMEPAGE           172     /* AC Home */
+#define KEY_REFRESH            173     /* AC Refresh */
+#define KEY_EXIT               174     /* AC Exit */
+#define KEY_MOVE               175
+#define KEY_EDIT               176
+#define KEY_SCROLLUP           177
+#define KEY_SCROLLDOWN         178
+#define KEY_KPLEFTPAREN                179
+#define KEY_KPRIGHTPAREN       180
+#define KEY_NEW                        181     /* AC New */
+#define KEY_REDO               182     /* AC Redo/Repeat */
+
+#define KEY_F13                        183
+#define KEY_F14                        184
+#define KEY_F15                        185
+#define KEY_F16                        186
+#define KEY_F17                        187
+#define KEY_F18                        188
+#define KEY_F19                        189
+#define KEY_F20                        190
+#define KEY_F21                        191
+#define KEY_F22                        192
+#define KEY_F23                        193
+#define KEY_F24                        194
+
+#define KEY_PLAYCD             200
+#define KEY_PAUSECD            201
+#define KEY_PROG3              202
+#define KEY_PROG4              203
+#define KEY_SUSPEND            205
+#define KEY_CLOSE              206     /* AC Close */
+#define KEY_PLAY               207
+#define KEY_FASTFORWARD                208
+#define KEY_BASSBOOST          209
+#define KEY_PRINT              210     /* AC Print */
+#define KEY_HP                 211
+#define KEY_CAMERA             212
+#define KEY_SOUND              213
+#define KEY_QUESTION           214
+#define KEY_EMAIL              215
+#define KEY_CHAT               216
+#define KEY_SEARCH             217
+#define KEY_CONNECT            218
+#define KEY_FINANCE            219     /* AL Checkbook/Finance */
+#define KEY_SPORT              220
+#define KEY_SHOP               221
+#define KEY_ALTERASE           222
+#define KEY_CANCEL             223     /* AC Cancel */
+#define KEY_BRIGHTNESSDOWN     224
+#define KEY_BRIGHTNESSUP       225
+#define KEY_MEDIA              226
+
+#define KEY_SWITCHVIDEOMODE    227     /* Cycle between available video
+                                          outputs (Monitor/LCD/TV-out/etc) */
+#define KEY_KBDILLUMTOGGLE     228
+#define KEY_KBDILLUMDOWN       229
+#define KEY_KBDILLUMUP         230
+
+#define KEY_SEND               231     /* AC Send */
+#define KEY_REPLY              232     /* AC Reply */
+#define KEY_FORWARDMAIL                233     /* AC Forward Msg */
+#define KEY_SAVE               234     /* AC Save */
+#define KEY_DOCUMENTS          235
+
+#define KEY_BATTERY            236
+
+#define KEY_BLUETOOTH          237
+#define KEY_WLAN               238
+#define KEY_UWB                        239
+
+#define KEY_UNKNOWN            240
+
+#define KEY_VIDEO_NEXT         241     /* drive next video source */
+#define KEY_VIDEO_PREV         242     /* drive previous video source */
+#define KEY_BRIGHTNESS_CYCLE   243     /* brightness up, after max is min */
+#define KEY_BRIGHTNESS_ZERO    244     /* brightness off, use ambient */
+#define KEY_DISPLAY_OFF                245     /* display device to off state */
+
+#define KEY_WIMAX              246
+
+/* Range 248 - 255 is reserved for special needs of AT keyboard driver */
+
+#define BTN_MISC               0x100
+#define BTN_0                  0x100
+#define BTN_1                  0x101
+#define BTN_2                  0x102
+#define BTN_3                  0x103
+#define BTN_4                  0x104
+#define BTN_5                  0x105
+#define BTN_6                  0x106
+#define BTN_7                  0x107
+#define BTN_8                  0x108
+#define BTN_9                  0x109
+
+#define BTN_MOUSE              0x110
+#define BTN_LEFT               0x110
+#define BTN_RIGHT              0x111
+#define BTN_MIDDLE             0x112
+#define BTN_SIDE               0x113
+#define BTN_EXTRA              0x114
+#define BTN_FORWARD            0x115
+#define BTN_BACK               0x116
+#define BTN_TASK               0x117
+
+#define BTN_JOYSTICK           0x120
+#define BTN_TRIGGER            0x120
+#define BTN_THUMB              0x121
+#define BTN_THUMB2             0x122
+#define BTN_TOP                        0x123
+#define BTN_TOP2               0x124
+#define BTN_PINKIE             0x125
+#define BTN_BASE               0x126
+#define BTN_BASE2              0x127
+#define BTN_BASE3              0x128
+#define BTN_BASE4              0x129
+#define BTN_BASE5              0x12a
+#define BTN_BASE6              0x12b
+#define BTN_DEAD               0x12f
+
+#define BTN_GAMEPAD            0x130
+#define BTN_A                  0x130
+#define BTN_B                  0x131
+#define BTN_C                  0x132
+#define BTN_X                  0x133
+#define BTN_Y                  0x134
+#define BTN_Z                  0x135
+#define BTN_TL                 0x136
+#define BTN_TR                 0x137
+#define BTN_TL2                        0x138
+#define BTN_TR2                        0x139
+#define BTN_SELECT             0x13a
+#define BTN_START              0x13b
+#define BTN_MODE               0x13c
+#define BTN_THUMBL             0x13d
+#define BTN_THUMBR             0x13e
+
+#define BTN_DIGI               0x140
+#define BTN_TOOL_PEN           0x140
+#define BTN_TOOL_RUBBER                0x141
+#define BTN_TOOL_BRUSH         0x142
+#define BTN_TOOL_PENCIL                0x143
+#define BTN_TOOL_AIRBRUSH      0x144
+#define BTN_TOOL_FINGER                0x145
+#define BTN_TOOL_MOUSE         0x146
+#define BTN_TOOL_LENS          0x147
+#define BTN_TOUCH              0x14a
+#define BTN_STYLUS             0x14b
+#define BTN_STYLUS2            0x14c
+#define BTN_TOOL_DOUBLETAP     0x14d
+#define BTN_TOOL_TRIPLETAP     0x14e
+
+#define BTN_WHEEL              0x150
+#define BTN_GEAR_DOWN          0x150
+#define BTN_GEAR_UP            0x151
+
+#define KEY_OK                 0x160
+#define KEY_SELECT             0x161
+#define KEY_GOTO               0x162
+#define KEY_CLEAR              0x163
+#define KEY_POWER2             0x164
+#define KEY_OPTION             0x165
+#define KEY_INFO               0x166   /* AL OEM Features/Tips/Tutorial */
+#define KEY_TIME               0x167
+#define KEY_VENDOR             0x168
+#define KEY_ARCHIVE            0x169
+#define KEY_PROGRAM            0x16a   /* Media Select Program Guide */
+#define KEY_CHANNEL            0x16b
+#define KEY_FAVORITES          0x16c
+#define KEY_EPG                        0x16d
+#define KEY_PVR                        0x16e   /* Media Select Home */
+#define KEY_MHP                        0x16f
+#define KEY_LANGUAGE           0x170
+#define KEY_TITLE              0x171
+#define KEY_SUBTITLE           0x172
+#define KEY_ANGLE              0x173
+#define KEY_ZOOM               0x174
+#define KEY_MODE               0x175
+#define KEY_KEYBOARD           0x176
+#define KEY_SCREEN             0x177
+#define KEY_PC                 0x178   /* Media Select Computer */
+#define KEY_TV                 0x179   /* Media Select TV */
+#define KEY_TV2                        0x17a   /* Media Select Cable */
+#define KEY_VCR                        0x17b   /* Media Select VCR */
+#define KEY_VCR2               0x17c   /* VCR Plus */
+#define KEY_SAT                        0x17d   /* Media Select Satellite */
+#define KEY_SAT2               0x17e
+#define KEY_CD                 0x17f   /* Media Select CD */
+#define KEY_TAPE               0x180   /* Media Select Tape */
+#define KEY_RADIO              0x181
+#define KEY_TUNER              0x182   /* Media Select Tuner */
+#define KEY_PLAYER             0x183
+#define KEY_TEXT               0x184
+#define KEY_DVD                        0x185   /* Media Select DVD */
+#define KEY_AUX                        0x186
+#define KEY_MP3                        0x187
+#define KEY_AUDIO              0x188
+#define KEY_VIDEO              0x189
+#define KEY_DIRECTORY          0x18a
+#define KEY_LIST               0x18b
+#define KEY_MEMO               0x18c   /* Media Select Messages */
+#define KEY_CALENDAR           0x18d
+#define KEY_RED                        0x18e
+#define KEY_GREEN              0x18f
+#define KEY_YELLOW             0x190
+#define KEY_BLUE               0x191
+#define KEY_CHANNELUP          0x192   /* Channel Increment */
+#define KEY_CHANNELDOWN                0x193   /* Channel Decrement */
+#define KEY_FIRST              0x194
+#define KEY_LAST               0x195   /* Recall Last */
+#define KEY_AB                 0x196
+#define KEY_NEXT               0x197
+#define KEY_RESTART            0x198
+#define KEY_SLOW               0x199
+#define KEY_SHUFFLE            0x19a
+#define KEY_BREAK              0x19b
+#define KEY_PREVIOUS           0x19c
+#define KEY_DIGITS             0x19d
+#define KEY_TEEN               0x19e
+#define KEY_TWEN               0x19f
+#define KEY_VIDEOPHONE         0x1a0   /* Media Select Video Phone */
+#define KEY_GAMES              0x1a1   /* Media Select Games */
+#define KEY_ZOOMIN             0x1a2   /* AC Zoom In */
+#define KEY_ZOOMOUT            0x1a3   /* AC Zoom Out */
+#define KEY_ZOOMRESET          0x1a4   /* AC Zoom */
+#define KEY_WORDPROCESSOR      0x1a5   /* AL Word Processor */
+#define KEY_EDITOR             0x1a6   /* AL Text Editor */
+#define KEY_SPREADSHEET                0x1a7   /* AL Spreadsheet */
+#define KEY_GRAPHICSEDITOR     0x1a8   /* AL Graphics Editor */
+#define KEY_PRESENTATION       0x1a9   /* AL Presentation App */
+#define KEY_DATABASE           0x1aa   /* AL Database App */
+#define KEY_NEWS               0x1ab   /* AL Newsreader */
+#define KEY_VOICEMAIL          0x1ac   /* AL Voicemail */
+#define KEY_ADDRESSBOOK                0x1ad   /* AL Contacts/Address Book */
+#define KEY_MESSENGER          0x1ae   /* AL Instant Messaging */
+#define KEY_DISPLAYTOGGLE      0x1af   /* Turn display (LCD) on and off */
+#define KEY_SPELLCHECK         0x1b0   /* AL Spell Check */
+#define KEY_LOGOFF             0x1b1   /* AL Logoff */
+
+#define KEY_DOLLAR             0x1b2
+#define KEY_EURO               0x1b3
+
+#define KEY_FRAMEBACK          0x1b4   /* Consumer - transport controls */
+#define KEY_FRAMEFORWARD       0x1b5
+#define KEY_CONTEXT_MENU       0x1b6   /* GenDesc - system context menu */
+#define KEY_MEDIA_REPEAT       0x1b7   /* Consumer - transport control */
+
+#define KEY_DEL_EOL            0x1c0
+#define KEY_DEL_EOS            0x1c1
+#define KEY_INS_LINE           0x1c2
+#define KEY_DEL_LINE           0x1c3
+
+#define KEY_FN                 0x1d0
+#define KEY_FN_ESC             0x1d1
+#define KEY_FN_F1              0x1d2
+#define KEY_FN_F2              0x1d3
+#define KEY_FN_F3              0x1d4
+#define KEY_FN_F4              0x1d5
+#define KEY_FN_F5              0x1d6
+#define KEY_FN_F6              0x1d7
+#define KEY_FN_F7              0x1d8
+#define KEY_FN_F8              0x1d9
+#define KEY_FN_F9              0x1da
+#define KEY_FN_F10             0x1db
+#define KEY_FN_F11             0x1dc
+#define KEY_FN_F12             0x1dd
+#define KEY_FN_1               0x1de
+#define KEY_FN_2               0x1df
+#define KEY_FN_D               0x1e0
+#define KEY_FN_E               0x1e1
+#define KEY_FN_F               0x1e2
+#define KEY_FN_S               0x1e3
+#define KEY_FN_B               0x1e4
+
+#define KEY_BRL_DOT1           0x1f1
+#define KEY_BRL_DOT2           0x1f2
+#define KEY_BRL_DOT3           0x1f3
+#define KEY_BRL_DOT4           0x1f4
+#define KEY_BRL_DOT5           0x1f5
+#define KEY_BRL_DOT6           0x1f6
+#define KEY_BRL_DOT7           0x1f7
+#define KEY_BRL_DOT8           0x1f8
+#define KEY_BRL_DOT9           0x1f9
+#define KEY_BRL_DOT10          0x1fa
+
+/* We avoid low common keys in module aliases so they don't get huge. */
+#define KEY_MIN_INTERESTING    KEY_MUTE
+#define KEY_MAX                        0x1ff
+#define KEY_CNT                        (KEY_MAX+1)
+
+/*
+ * Relative axes
+ */
+
+#define REL_X                  0x00
+#define REL_Y                  0x01
+#define REL_Z                  0x02
+#define REL_RX                 0x03
+#define REL_RY                 0x04
+#define REL_RZ                 0x05
+#define REL_HWHEEL             0x06
+#define REL_DIAL               0x07
+#define REL_WHEEL              0x08
+#define REL_MISC               0x09
+#define REL_MAX                        0x0f
+#define REL_CNT                        (REL_MAX+1)
+
+/*
+ * Absolute axes
+ */
+
+#define ABS_X                  0x00
+#define ABS_Y                  0x01
+#define ABS_Z                  0x02
+#define ABS_RX                 0x03
+#define ABS_RY                 0x04
+#define ABS_RZ                 0x05
+#define ABS_THROTTLE           0x06
+#define ABS_RUDDER             0x07
+#define ABS_WHEEL              0x08
+#define ABS_GAS                        0x09
+#define ABS_BRAKE              0x0a
+#define ABS_HAT0X              0x10
+#define ABS_HAT0Y              0x11
+#define ABS_HAT1X              0x12
+#define ABS_HAT1Y              0x13
+#define ABS_HAT2X              0x14
+#define ABS_HAT2Y              0x15
+#define ABS_HAT3X              0x16
+#define ABS_HAT3Y              0x17
+#define ABS_PRESSURE           0x18
+#define ABS_DISTANCE           0x19
+#define ABS_TILT_X             0x1a
+#define ABS_TILT_Y             0x1b
+#define ABS_TOOL_WIDTH         0x1c
+#define ABS_VOLUME             0x20
+#define ABS_MISC               0x28
+#define ABS_MAX                        0x3f
+#define ABS_CNT                        (ABS_MAX+1)
+
+/*
+ * Switch events
+ */
+
+#define SW_LID                 0x00  /* set = lid shut */
+#define SW_TABLET_MODE         0x01  /* set = tablet mode */
+#define SW_HEADPHONE_INSERT    0x02  /* set = inserted */
+#define SW_RFKILL_ALL          0x03  /* rfkill master switch, type "any"
+                                        set = radio enabled */
+#define SW_RADIO               SW_RFKILL_ALL   /* deprecated */
+#define SW_MICROPHONE_INSERT   0x04  /* set = inserted */
+#define SW_DOCK                        0x05  /* set = plugged into dock */
+#define SW_MAX                 0x0f
+#define SW_CNT                 (SW_MAX+1)
+
+/*
+ * Misc events
+ */
+
+#define MSC_SERIAL             0x00
+#define MSC_PULSELED           0x01
+#define MSC_GESTURE            0x02
+#define MSC_RAW                        0x03
+#define MSC_SCAN               0x04
+#define MSC_MAX                        0x07
+#define MSC_CNT                        (MSC_MAX+1)
+
+/*
+ * LEDs
+ */
+
+#define LED_NUML               0x00
+#define LED_CAPSL              0x01
+#define LED_SCROLLL            0x02
+#define LED_COMPOSE            0x03
+#define LED_KANA               0x04
+#define LED_SLEEP              0x05
+#define LED_SUSPEND            0x06
+#define LED_MUTE               0x07
+#define LED_MISC               0x08
+#define LED_MAIL               0x09
+#define LED_CHARGING           0x0a
+#define LED_MAX                        0x0f
+#define LED_CNT                        (LED_MAX+1)
+
+/*
+ * Autorepeat values
+ */
+
+#define REP_DELAY              0x00
+#define REP_PERIOD             0x01
+#define REP_MAX                        0x01
+
+/*
+ * Sounds
+ */
+
+#define SND_CLICK              0x00
+#define SND_BELL               0x01
+#define SND_TONE               0x02
+#define SND_MAX                        0x07
+#define SND_CNT                        (SND_MAX+1)
+
+/*
+ * IDs.
+ */
+
+#define ID_BUS                 0
+#define ID_VENDOR              1
+#define ID_PRODUCT             2
+#define ID_VERSION             3
+
+#define BUS_PCI                        0x01
+#define BUS_ISAPNP             0x02
+#define BUS_USB                        0x03
+#define BUS_HIL                        0x04
+#define BUS_BLUETOOTH          0x05
+#define BUS_VIRTUAL            0x06
+
+#define BUS_ISA                        0x10
+#define BUS_I8042              0x11
+#define BUS_XTKBD              0x12
+#define BUS_RS232              0x13
+#define BUS_GAMEPORT           0x14
+#define BUS_PARPORT            0x15
+#define BUS_AMIGA              0x16
+#define BUS_ADB                        0x17
+#define BUS_I2C                        0x18
+#define BUS_HOST               0x19
+#define BUS_GSC                        0x1A
+#define BUS_ATARI              0x1B
+
+/* User input interface */
+
+#define UINPUT_IOCTL_BASE      'U'
+
+#define UI_DEV_CREATE          _IO(UINPUT_IOCTL_BASE, 1)
+#define UI_DEV_DESTROY         _IO(UINPUT_IOCTL_BASE, 2)
+
+#define UI_SET_EVBIT           _IOW(UINPUT_IOCTL_BASE, 100, int)
+#define UI_SET_KEYBIT          _IOW(UINPUT_IOCTL_BASE, 101, int)
+#define UI_SET_RELBIT          _IOW(UINPUT_IOCTL_BASE, 102, int)
+#define UI_SET_ABSBIT          _IOW(UINPUT_IOCTL_BASE, 103, int)
+#define UI_SET_MSCBIT          _IOW(UINPUT_IOCTL_BASE, 104, int)
+#define UI_SET_LEDBIT          _IOW(UINPUT_IOCTL_BASE, 105, int)
+#define UI_SET_SNDBIT          _IOW(UINPUT_IOCTL_BASE, 106, int)
+#define UI_SET_FFBIT           _IOW(UINPUT_IOCTL_BASE, 107, int)
+#define UI_SET_PHYS            _IOW(UINPUT_IOCTL_BASE, 108, char*)
+#define UI_SET_SWBIT           _IOW(UINPUT_IOCTL_BASE, 109, int)
+
+#ifndef NBITS
+#define NBITS(x) ((((x) - 1) / (sizeof(long) * 8)) + 1)
+#endif
+
+#define UINPUT_MAX_NAME_SIZE   80
+
+struct uinput_id {
+       uint16_t bustype;
+       uint16_t vendor;
+       uint16_t product;
+       uint16_t version;
+};
+
+struct uinput_dev {
+       char name[UINPUT_MAX_NAME_SIZE];
+       struct uinput_id id;
+       int ff_effects_max;
+       int absmax[ABS_MAX + 1];
+       int absmin[ABS_MAX + 1];
+       int absfuzz[ABS_MAX + 1];
+       int absflat[ABS_MAX + 1];
+};
+
+struct uinput_event {
+       struct timeval time;
+       uint16_t type;
+       uint16_t code;
+       int32_t value;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UINPUT_H */
diff --git a/test/agent.c b/test/agent.c
new file mode 100644 (file)
index 0000000..5cdeeb4
--- /dev/null
@@ -0,0 +1,717 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <getopt.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+
+static char *passkey_value = NULL;
+static int passkey_delay = 0;
+static int do_reject = 0;
+
+static volatile sig_atomic_t __io_canceled = 0;
+static volatile sig_atomic_t __io_terminated = 0;
+static volatile sig_atomic_t exit_on_release = 1;
+
+static void sig_term(int sig)
+{
+       __io_canceled = 1;
+}
+
+static DBusHandlerResult agent_filter(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       const char *name, *old, *new;
+
+       if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
+                                               "NameOwnerChanged"))
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_STRING, &name,
+                                       DBUS_TYPE_STRING, &old,
+                                       DBUS_TYPE_STRING, &new,
+                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (!strcmp(name, "org.bluez") && *new == '\0') {
+               fprintf(stderr, "Agent has been terminated\n");
+               __io_terminated = 1;
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult request_pincode_message(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       const char *path;
+
+       if (!passkey_value)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Invalid arguments for RequestPinCode method");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (do_reject) {
+               reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
+               goto send;
+       }
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply) {
+               fprintf(stderr, "Can't create reply message\n");
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+       }
+
+       printf("Pincode request for device %s\n", path);
+
+       if (passkey_delay) {
+               printf("Waiting for %d seconds\n", passkey_delay);
+               sleep(passkey_delay);
+       }
+
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &passkey_value,
+                                                       DBUS_TYPE_INVALID);
+
+send:
+       dbus_connection_send(conn, reply, NULL);
+
+       dbus_connection_flush(conn);
+
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult request_passkey_message(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       const char *path;
+       unsigned int passkey;
+
+       if (!passkey_value)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Invalid arguments for RequestPasskey method");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (do_reject) {
+               reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
+               goto send;
+       }
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply) {
+               fprintf(stderr, "Can't create reply message\n");
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+       }
+
+       printf("Passkey request for device %s\n", path);
+
+       if (passkey_delay) {
+               printf("Waiting for %d seconds\n", passkey_delay);
+               sleep(passkey_delay);
+       }
+
+       passkey = strtoul(passkey_value, NULL, 10);
+
+       dbus_message_append_args(reply, DBUS_TYPE_UINT32, &passkey,
+                                                       DBUS_TYPE_INVALID);
+
+send:
+       dbus_connection_send(conn, reply, NULL);
+
+       dbus_connection_flush(conn);
+
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult request_confirmation_message(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       const char *path;
+       unsigned int passkey;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_UINT32, &passkey,
+                                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Invalid arguments for RequestPasskey method");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (do_reject) {
+               reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
+               goto send;
+       }
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply) {
+               fprintf(stderr, "Can't create reply message\n");
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+       }
+
+       printf("Confirmation request of %u for device %s\n", passkey, path);
+
+       if (passkey_delay) {
+               printf("Waiting for %d seconds\n", passkey_delay);
+               sleep(passkey_delay);
+       }
+
+send:
+       dbus_connection_send(conn, reply, NULL);
+
+       dbus_connection_flush(conn);
+
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult authorize_message(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+       const char *path, *uuid;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_STRING, &uuid,
+                                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Invalid arguments for Authorize method");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (do_reject) {
+               reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
+               goto send;
+       }
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply) {
+               fprintf(stderr, "Can't create reply message\n");
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+       }
+
+       printf("Authorizing request for %s\n", path);
+
+send:
+       dbus_connection_send(conn, reply, NULL);
+
+       dbus_connection_flush(conn);
+
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult cancel_message(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Invalid arguments for passkey Confirm method");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       printf("Request canceled\n");
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply) {
+               fprintf(stderr, "Can't create reply message\n");
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+       }
+
+       dbus_connection_send(conn, reply, NULL);
+
+       dbus_connection_flush(conn);
+
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult release_message(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       DBusMessage *reply;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Invalid arguments for Release method");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (!__io_canceled)
+               fprintf(stderr, "Agent has been released\n");
+
+       if (exit_on_release)
+               __io_terminated = 1;
+
+       reply = dbus_message_new_method_return(msg);
+       if (!reply) {
+               fprintf(stderr, "Can't create reply message\n");
+               return DBUS_HANDLER_RESULT_NEED_MEMORY;
+       }
+
+       dbus_connection_send(conn, reply, NULL);
+
+       dbus_connection_flush(conn);
+
+       dbus_message_unref(reply);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult agent_message(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       if (dbus_message_is_method_call(msg, "org.bluez.Agent",
+                                                       "RequestPinCode"))
+               return request_pincode_message(conn, msg, data);
+
+       if (dbus_message_is_method_call(msg, "org.bluez.Agent",
+                                                       "RequestPasskey"))
+               return request_passkey_message(conn, msg, data);
+
+       if (dbus_message_is_method_call(msg, "org.bluez.Agent",
+                                                       "RequestConfirmation"))
+               return request_confirmation_message(conn, msg, data);
+
+       if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Authorize"))
+               return authorize_message(conn, msg, data);
+
+       if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Cancel"))
+               return cancel_message(conn, msg, data);
+
+       if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Release"))
+               return release_message(conn, msg, data);
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static const DBusObjectPathVTable agent_table = {
+       .message_function = agent_message,
+};
+
+static int register_agent(DBusConnection *conn, const char *adapter_path,
+                                               const char *agent_path,
+                                               const char *capabilities)
+{
+       DBusMessage *msg, *reply;
+       DBusError err;
+
+       msg = dbus_message_new_method_call("org.bluez", adapter_path,
+                                       "org.bluez.Adapter", "RegisterAgent");
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return -1;
+       }
+
+       dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_STRING, &capabilities,
+                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               fprintf(stderr, "Can't register agent\n");
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               return -1;
+       }
+
+       dbus_message_unref(reply);
+
+       dbus_connection_flush(conn);
+
+       return 0;
+}
+
+static int unregister_agent(DBusConnection *conn, const char *adapter_path,
+                                                       const char *agent_path)
+{
+       DBusMessage *msg, *reply;
+       DBusError err;
+
+       msg = dbus_message_new_method_call("org.bluez", adapter_path,
+                                       "org.bluez.Adapter", "UnregisterAgent");
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return -1;
+       }
+
+       dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               fprintf(stderr, "Can't unregister agent\n");
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               return -1;
+       }
+
+       dbus_message_unref(reply);
+
+       dbus_connection_flush(conn);
+
+       dbus_connection_unregister_object_path(conn, agent_path);
+
+       return 0;
+}
+
+static void create_paired_device_reply(DBusPendingCall *pending,
+                                                       void *user_data)
+{
+       __io_terminated = 1;
+       return;
+}
+
+static int create_paired_device(DBusConnection *conn, const char *adapter_path,
+                                               const char *agent_path,
+                                               const char *capabilities,
+                                               const char *device)
+{
+       dbus_bool_t success;
+       DBusMessage *msg;
+       DBusPendingCall *pending;
+
+       msg = dbus_message_new_method_call("org.bluez", adapter_path,
+                                               "org.bluez.Adapter",
+                                               "CreatePairedDevice");
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return -1;
+       }
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &device,
+                                       DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                       DBUS_TYPE_STRING, &capabilities,
+                                       DBUS_TYPE_INVALID);
+
+       exit_on_release = 0;
+       success = dbus_connection_send_with_reply(conn, msg, &pending, -1);
+       if (pending)
+               dbus_pending_call_set_notify(pending,
+                                               create_paired_device_reply,
+                                               NULL, NULL);
+
+       dbus_message_unref(msg);
+
+       if (!success) {
+               fprintf(stderr, "Not enough memory for message send\n");
+               return -1;
+       }
+
+       dbus_connection_flush(conn);
+
+       return 0;
+}
+
+static char *get_default_adapter_path(DBusConnection *conn)
+{
+       DBusMessage *msg, *reply;
+       DBusError err;
+       const char *reply_path;
+       char *path;
+
+       msg = dbus_message_new_method_call("org.bluez", "/",
+                                       "org.bluez.Manager", "DefaultAdapter");
+
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return NULL;
+       }
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               fprintf(stderr,
+                       "Can't get default adapter\n");
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               return NULL;
+       }
+
+       if (!dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_OBJECT_PATH, &reply_path,
+                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr,
+                       "Can't get reply arguments\n");
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       path = strdup(reply_path);
+
+       dbus_message_unref(reply);
+
+       dbus_connection_flush(conn);
+
+       return path;
+}
+
+static char *get_adapter_path(DBusConnection *conn, const char *adapter)
+{
+       DBusMessage *msg, *reply;
+       DBusError err;
+       const char *reply_path;
+       char *path;
+
+       if (!adapter)
+               return get_default_adapter_path(conn);
+
+       msg = dbus_message_new_method_call("org.bluez", "/",
+                                       "org.bluez.Manager", "FindAdapter");
+
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return NULL;
+       }
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &adapter,
+                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               fprintf(stderr,
+                       "Can't find adapter %s\n", adapter);
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               return NULL;
+       }
+
+       if (!dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_OBJECT_PATH, &reply_path,
+                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr,
+                       "Can't get reply arguments\n");
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       path = strdup(reply_path);
+
+       dbus_message_unref(reply);
+
+       dbus_connection_flush(conn);
+
+       return path;
+}
+
+static void usage(void)
+{
+       printf("Bluetooth agent ver %s\n\n", VERSION);
+
+       printf("Usage:\n"
+               "\tagent [--adapter adapter-path] [--path agent-path] <passkey> [<device>]\n"
+               "\n");
+}
+
+static struct option main_options[] = {
+       { "adapter",    1, 0, 'a' },
+       { "path",       1, 0, 'p' },
+       { "capabilites",1, 0, 'c' },
+       { "delay",      1, 0, 'd' },
+       { "reject",     0, 0, 'r' },
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       const char *capabilities = "DisplayYesNo";
+       struct sigaction sa;
+       DBusConnection *conn;
+       char match_string[128], default_path[128], *adapter_id = NULL;
+       char *adapter_path = NULL, *agent_path = NULL, *device = NULL;
+       int opt;
+
+       snprintf(default_path, sizeof(default_path),
+                                       "/org/bluez/agent_%d", getpid());
+
+       while ((opt = getopt_long(argc, argv, "+a:p:c:d:rh", main_options, NULL)) != EOF) {
+               switch(opt) {
+               case 'a':
+                       adapter_id = optarg;
+                       break;
+               case 'p':
+                       if (optarg[0] != '/') {
+                               fprintf(stderr, "Invalid path\n");
+                               exit(1);
+                       }
+                       agent_path = strdup(optarg);
+                       break;
+               case 'c':
+                       capabilities = optarg;
+                       break;
+               case 'd':
+                       passkey_delay = atoi(optarg);
+                       break;
+               case 'r':
+                       do_reject = 1;
+                       break;
+               case 'h':
+                       usage();
+                       exit(0);
+               default:
+                       exit(1);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       passkey_value = strdup(argv[0]);
+
+       if (argc > 1)
+               device = strdup(argv[1]);
+
+       if (!agent_path)
+               agent_path = strdup(default_path);
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (!conn) {
+               fprintf(stderr, "Can't get on system bus");
+               exit(1);
+       }
+
+       adapter_path = get_adapter_path(conn, adapter_id);
+       if (!adapter_path)
+               exit(1);
+
+       if (!dbus_connection_register_object_path(conn, agent_path,
+                                                       &agent_table, NULL)) {
+               fprintf(stderr, "Can't register object path for agent\n");
+               exit(1);
+       }
+
+       if (device) {
+               if (create_paired_device(conn, adapter_path, agent_path,
+                                               capabilities, device) < 0) {
+                       dbus_connection_unref(conn);
+                       exit(1);
+               }
+       } else {
+               if (register_agent(conn, adapter_path, agent_path,
+                                                       capabilities) < 0) {
+                       dbus_connection_unref(conn);
+                       exit(1);
+               }
+       }
+
+       if (!dbus_connection_add_filter(conn, agent_filter, NULL, NULL))
+               fprintf(stderr, "Can't add signal filter");
+
+       snprintf(match_string, sizeof(match_string),
+                       "interface=%s,member=NameOwnerChanged,arg0=%s",
+                       DBUS_INTERFACE_DBUS, "org.bluez");
+
+       dbus_bus_add_match(conn, match_string, NULL);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       while (!__io_canceled && !__io_terminated) {
+               if (dbus_connection_read_write_dispatch(conn, 500) != TRUE)
+                       break;
+       }
+
+       if (!__io_terminated && !device)
+               unregister_agent(conn, adapter_path, agent_path);
+
+       free(adapter_path);
+       free(agent_path);
+
+       free(passkey_value);
+
+       dbus_connection_unref(conn);
+
+       return 0;
+}
diff --git a/test/attest.c b/test/attest.c
new file mode 100644 (file)
index 0000000..12ba682
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+static int at_command(int fd, char *cmd, int to)
+{
+       fd_set rfds;
+       struct timeval timeout;
+       char buf[1024];
+       int sel, len, i, n;
+
+       len = write(fd, cmd, strlen(cmd));
+
+       for (i = 0; i < 100; i++) {
+
+               FD_ZERO(&rfds);
+               FD_SET(fd, &rfds);
+
+               timeout.tv_sec = 0;
+               timeout.tv_usec = to;
+
+               if ((sel = select(fd + 1, &rfds, NULL, NULL, &timeout)) > 0) {
+
+                       if (FD_ISSET(fd, &rfds)) {
+                               memset(buf, 0, sizeof(buf));
+                               len = read(fd, buf, sizeof(buf));
+                               for (n = 0; n < len; n++)
+                                       printf("%c", buf[n]);
+                               if (strstr(buf, "\r\nOK") != NULL)
+                                       break;
+                               if (strstr(buf, "\r\nERROR") != NULL)
+                                       break;
+                               if (strstr(buf, "\r\nCONNECT") != NULL)
+                                       break;
+                       }
+
+               }
+
+       }
+
+       return 0;
+}
+
+static int open_device(char *device)
+{
+       struct termios ti;
+       int fd;
+
+       fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
+       if (fd < 0) {
+               fprintf(stderr, "Can't open serial port: %s (%d)\n",
+                                                       strerror(errno), errno);
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       /* Switch tty to RAW mode */
+       cfmakeraw(&ti);
+       tcsetattr(fd, TCSANOW, &ti);
+
+       return fd;
+}
+
+static int open_socket(bdaddr_t *bdaddr, uint8_t channel)
+{
+       struct sockaddr_rc addr;
+       int sk;
+
+       sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+       if (sk < 0) {
+               fprintf(stderr, "Can't create socket: %s (%d)\n",
+                                                       strerror(errno), errno);
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, BDADDR_ANY);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               fprintf(stderr, "Can't bind socket: %s (%d)\n",
+                                                       strerror(errno), errno);
+               close(sk);
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, bdaddr);
+       addr.rc_channel = channel;
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               fprintf(stderr, "Can't connect: %s (%d)\n",
+                                                       strerror(errno), errno);
+               close(sk);
+               return -1;
+       }
+
+       return sk;
+}
+
+static void usage(void)
+{
+       printf("Usage:\n\tattest <device> | <bdaddr> [channel]\n");
+}
+
+int main(int argc, char *argv[])
+{
+       int fd;
+
+       bdaddr_t bdaddr;
+       uint8_t channel;
+
+       switch (argc) {
+       case 2:
+               str2ba(argv[1], &bdaddr);
+               channel = 1;
+               break;
+       case 3:
+               str2ba(argv[1], &bdaddr);
+               channel = atoi(argv[2]);
+               break;
+       default:
+               usage();
+               exit(-1);
+       }
+
+       if (bacmp(BDADDR_ANY, &bdaddr)) {
+               printf("Connecting to %s on channel %d\n", argv[1], channel);
+               fd = open_socket(&bdaddr, channel);
+       } else {
+               printf("Opening device %s\n", argv[1]);
+               fd = open_device(argv[1]);
+       }
+
+       if (fd < 0)
+               exit(-2);
+
+       at_command(fd, "ATZ\r\n", 10000);
+       at_command(fd, "AT+CPBS=\"ME\"\r\n", 10000);
+       at_command(fd, "AT+CPBR=1,100\r\n", 100000);
+
+       close(fd);
+
+       return 0;
+}
diff --git a/test/avtest.c b/test/avtest.c
new file mode 100644 (file)
index 0000000..541b3cd
--- /dev/null
@@ -0,0 +1,869 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+
+#define AVDTP_PKT_TYPE_SINGLE          0x00
+#define AVDTP_PKT_TYPE_START           0x01
+#define AVDTP_PKT_TYPE_CONTINUE                0x02
+#define AVDTP_PKT_TYPE_END             0x03
+
+#define AVDTP_MSG_TYPE_COMMAND         0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT      0x01
+#define AVDTP_MSG_TYPE_ACCEPT          0x02
+#define AVDTP_MSG_TYPE_REJECT          0x03
+
+#define AVDTP_DISCOVER                 0x01
+#define AVDTP_GET_CAPABILITIES         0x02
+#define AVDTP_SET_CONFIGURATION                0x03
+#define AVDTP_GET_CONFIGURATION                0x04
+#define AVDTP_RECONFIGURE              0x05
+#define AVDTP_OPEN                     0x06
+#define AVDTP_START                    0x07
+#define AVDTP_CLOSE                    0x08
+#define AVDTP_SUSPEND                  0x09
+#define AVDTP_ABORT                    0x0A
+
+#define AVDTP_SEP_TYPE_SOURCE          0x00
+#define AVDTP_SEP_TYPE_SINK            0x01
+
+#define AVDTP_MEDIA_TYPE_AUDIO         0x00
+#define AVDTP_MEDIA_TYPE_VIDEO         0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA    0x02
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+       uint8_t signal_id:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+       uint8_t rfa0:1;
+       uint8_t inuse:1;
+       uint8_t seid:6;
+       uint8_t rfa2:3;
+       uint8_t type:1;
+       uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+       uint8_t no_of_packets;
+       uint8_t signal_id:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avctp_header {
+       uint8_t ipid:1;
+       uint8_t cr:1;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+       uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+       uint8_t rfa0:2;
+       uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct seid_info {
+       uint8_t seid:6;
+       uint8_t inuse:1;
+       uint8_t rfa0:1;
+       uint8_t media_type:4;
+       uint8_t type:1;
+       uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+       uint8_t no_of_packets;
+       uint8_t rfa0:2;
+       uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avctp_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t cr:1;
+       uint8_t ipid:1;
+       uint16_t pid;
+} __attribute__ ((packed));
+#define AVCTP_HEADER_LENGTH 3
+
+#else
+#error "Unknown byte order"
+#endif
+
+#define AVCTP_COMMAND          0
+#define AVCTP_RESPONSE         1
+
+#define AVCTP_PACKET_SINGLE    0
+
+static const unsigned char media_transport[] = {
+               0x01,   /* Media transport category */
+               0x00,
+               0x07,   /* Media codec category */
+               0x06,
+               0x00,   /* Media type audio */
+               0x00,   /* Codec SBC */
+               0x22,   /* 44.1 kHz, stereo */
+               0x15,   /* 16 blocks, 8 subbands */
+               0x02,
+               0x33,
+};
+
+static int media_sock = -1;
+
+static void dump_avctp_header(struct avctp_header *hdr)
+{
+       printf("TL %d PT %d CR %d IPID %d PID 0x%04x\n", hdr->transaction,
+                       hdr->packet_type, hdr->cr, hdr->ipid, ntohs(hdr->pid));
+}
+
+static void dump_avdtp_header(struct avdtp_header *hdr)
+{
+       printf("TL %d PT %d MT %d SI %d\n", hdr->transaction,
+                       hdr->packet_type, hdr->message_type, hdr->signal_id);
+}
+
+static void dump_buffer(const unsigned char *buf, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++)
+               printf("%02x ", buf[i]);
+       printf("\n");
+}
+
+static void process_avdtp(int srv_sk, int sk, unsigned char reject,
+                                                               int fragment)
+{
+       unsigned char buf[672];
+       ssize_t len;
+
+       while (1) {
+               struct avdtp_header *hdr = (void *) buf;
+
+               len = read(sk, buf, sizeof(buf));
+               if (len <= 0) {
+                       perror("Read failed");
+                       break;
+               }
+
+               dump_buffer(buf, len);
+               dump_avdtp_header(hdr);
+
+               if (hdr->packet_type != AVDTP_PKT_TYPE_SINGLE) {
+                       fprintf(stderr, "Only single packets are supported\n");
+                       break;
+               }
+
+               if (hdr->message_type != AVDTP_MSG_TYPE_COMMAND) {
+                       fprintf(stderr, "Ignoring non-command messages\n");
+                       continue;
+               }
+
+               switch (hdr->signal_id) {
+               case AVDTP_DISCOVER:
+                       if (reject == AVDTP_DISCOVER) {
+                               hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+                               buf[2] = 0x29; /* Unsupported configuration */
+                               printf("Rejecting discover command\n");
+                               len = write(sk, buf, 3);
+                       } else {
+                               struct seid_info *sei = (void *) (buf + 2);
+                               hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+                               buf[2] = 0x00;
+                               buf[3] = 0x00;
+                               sei->seid = 0x01;
+                               sei->type = AVDTP_SEP_TYPE_SINK;
+                               sei->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+                               printf("Accepting discover command\n");
+                               len = write(sk, buf, 4);
+                       }
+                       break;
+
+               case AVDTP_GET_CAPABILITIES:
+                       if (reject == AVDTP_GET_CAPABILITIES) {
+                               hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+                               buf[2] = 0x29; /* Unsupported configuration */
+                               printf("Rejecting get capabilties command\n");
+                               len = write(sk, buf, 3);
+                       } else if (fragment) {
+                               struct avdtp_start_header *start = (void *) buf;
+
+                               printf("Sending fragmented reply to getcap\n");
+
+                               hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+
+                               /* Start packet */
+                               hdr->packet_type = AVDTP_PKT_TYPE_START;
+                               start->signal_id = AVDTP_GET_CAPABILITIES;
+                               start->no_of_packets = 3;
+                               memcpy(&buf[3], media_transport,
+                                               sizeof(media_transport));
+                               len = write(sk, buf,
+                                               3 + sizeof(media_transport));
+
+                               /* Continue packet */
+                               hdr->packet_type = AVDTP_PKT_TYPE_CONTINUE;
+                               memcpy(&buf[1], media_transport,
+                                               sizeof(media_transport));
+                               len = write(sk, buf,
+                                               1 + sizeof(media_transport));
+
+                               /* End packet */
+                               hdr->packet_type = AVDTP_PKT_TYPE_END;
+                               memcpy(&buf[1], media_transport,
+                                               sizeof(media_transport));
+                               len = write(sk, buf,
+                                               1 + sizeof(media_transport));
+                       } else {
+                               hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+                               memcpy(&buf[2], media_transport,
+                                               sizeof(media_transport));
+                               printf("Accepting get capabilities command\n");
+                               len = write(sk, buf,
+                                               2 + sizeof(media_transport));
+                       }
+                       break;
+
+               case AVDTP_SET_CONFIGURATION:
+                       if (reject == AVDTP_SET_CONFIGURATION) {
+                               hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+                               buf[2] = buf[4];
+                               buf[3] = 0x13; /* SEP In Use */
+                               printf("Rejecting set configuration command\n");
+                               len = write(sk, buf, 4);
+                       } else {
+                               hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+                               printf("Accepting set configuration command\n");
+                               len = write(sk, buf, 2);
+                       }
+                       break;
+
+               case AVDTP_GET_CONFIGURATION:
+                       if (reject == AVDTP_GET_CONFIGURATION) {
+                               hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+                               buf[2] = 0x12; /* Bad ACP SEID */
+                               printf("Rejecting get configuration command\n");
+                               len = write(sk, buf, 3);
+                       } else {
+                               hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+                               printf("Accepting get configuration command\n");
+                               len = write(sk, buf, 2);
+                       }
+                       break;
+
+               case AVDTP_OPEN:
+                       if (reject == AVDTP_OPEN) {
+                               hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+                               buf[2] = 0x31; /* Bad State */
+                               printf("Rejecting open command\n");
+                               len = write(sk, buf, 3);
+                       } else {
+                               struct sockaddr_l2 addr;
+                               socklen_t optlen;
+
+                               hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+                               printf("Accepting open command\n");
+                               len = write(sk, buf, 2);
+
+                               memset(&addr, 0, sizeof(addr));
+                               optlen = sizeof(addr);
+
+                               media_sock = accept(srv_sk,
+                                               (struct sockaddr *) &addr,
+                                                               &optlen);
+                               if (media_sock < 0) {
+                                       perror("Accept failed");
+                                       break;
+                               }
+                       }
+                       break;
+
+               case AVDTP_START:
+                       if (reject == AVDTP_ABORT)
+                               printf("Ignoring start to cause abort");
+                       else if (reject == AVDTP_START) {
+                               hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+                               buf[3] = 0x31; /* Bad State */
+                               printf("Rejecting start command\n");
+                               len = write(sk, buf, 4);
+                       } else {
+                               hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+                               printf("Accepting start command\n");
+                               len = write(sk, buf, 2);
+                       }
+                       break;
+
+               case AVDTP_CLOSE:
+                       if (reject == AVDTP_CLOSE) {
+                               hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+                               buf[2] = 0x31; /* Bad State */
+                               printf("Rejecting close command\n");
+                               len = write(sk, buf, 3);
+                       } else {
+                               hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+                               printf("Accepting close command\n");
+                               len = write(sk, buf, 2);
+                               if (media_sock >= 0) {
+                                       close(media_sock);
+                                       media_sock = -1;
+                               }
+                       }
+                       break;
+
+               case AVDTP_SUSPEND:
+                       if (reject == AVDTP_SUSPEND) {
+                               hdr->message_type = AVDTP_MSG_TYPE_REJECT;
+                               buf[3] = 0x31; /* Bad State */
+                               printf("Rejecting suspend command\n");
+                               len = write(sk, buf, 4);
+                       } else {
+                               hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+                               printf("Accepting suspend command\n");
+                               len = write(sk, buf, 2);
+                       }
+                       break;
+
+               case AVDTP_ABORT:
+                       hdr->message_type = AVDTP_MSG_TYPE_ACCEPT;
+                       printf("Accepting abort command\n");
+                       len = write(sk, buf, 2);
+                       if (media_sock >= 0) {
+                               close(media_sock);
+                               media_sock = -1;
+                       }
+                       break;
+
+               default:
+                       buf[1] = 0x00;
+                       printf("Unknown command\n");
+                       len = write(sk, buf, 2);
+                       break;
+               }
+       }
+}
+
+static void process_avctp(int sk, int reject)
+{
+       unsigned char buf[672];
+       ssize_t len;
+
+       while (1) {
+               struct avctp_header *hdr = (void *) buf;
+
+               len = read(sk, buf, sizeof(buf));
+               if (len <= 0) {
+                       perror("Read failed");
+                       break;
+               }
+
+               dump_buffer(buf, len);
+
+               if (len >= AVCTP_HEADER_LENGTH)
+                       dump_avctp_header(hdr);
+       }
+}
+
+static int set_minimum_mtu(int sk)
+{
+       struct l2cap_options l2o;
+       socklen_t optlen;
+
+       memset(&l2o, 0, sizeof(l2o));
+       optlen = sizeof(l2o);
+
+       if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &optlen) < 0) {
+               perror("getsockopt");
+               return -1;
+       }
+
+       l2o.imtu = 48;
+       l2o.omtu = 48;
+
+       if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) {
+               perror("setsockopt");
+               return -1;
+       }
+
+       return 0;
+}
+
+static void do_listen(const bdaddr_t *src, unsigned char reject, int fragment)
+{
+       struct sockaddr_l2 addr;
+       socklen_t optlen;
+       int sk, nsk;
+
+       sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+       if (sk < 0) {
+               perror("Can't create socket");
+               return;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, src);
+       addr.l2_psm = htobs(25);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Can't bind socket");
+               goto error;
+       }
+
+       if (fragment)
+               set_minimum_mtu(sk);
+
+       if (listen(sk, 10)) {
+               perror("Can't listen on the socket");
+               goto error;
+       }
+
+       while (1) {
+               memset(&addr, 0, sizeof(addr));
+               optlen = sizeof(addr);
+
+               nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+               if (nsk < 0) {
+                       perror("Accept failed");
+                       continue;
+               }
+
+               process_avdtp(sk, nsk, reject, fragment);
+
+               if (media_sock >= 0) {
+                       close(media_sock);
+                       media_sock = -1;
+               }
+
+               close(nsk);
+       }
+
+error:
+       close(sk);
+}
+
+static int do_connect(const bdaddr_t *src, const bdaddr_t *dst, int avctp,
+                                                               int fragment)
+{
+       struct sockaddr_l2 addr;
+       int sk, err;
+
+       sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+       if (sk < 0) {
+               perror("Can't create socket");
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, src);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Can't bind socket");
+               goto error;
+       }
+
+       if (fragment)
+               set_minimum_mtu(sk);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, dst);
+       addr.l2_psm = htobs(avctp ? 23 : 25);
+
+       err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0) {
+               perror("Unable to connect");
+               goto error;
+       }
+
+       return sk;
+
+error:
+       close(sk);
+       return -1;
+}
+
+static void do_avdtp_send(int sk, const bdaddr_t *src, const bdaddr_t *dst,
+                               unsigned char cmd, int invalid, int preconf)
+{
+       unsigned char buf[672];
+       struct avdtp_header *hdr = (void *) buf;
+       ssize_t len;
+
+       memset(buf, 0, sizeof(buf));
+
+       switch (cmd) {
+       case AVDTP_DISCOVER:
+               if (invalid)
+                       hdr->message_type = 0x01;
+               else
+                       hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+               hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+               hdr->signal_id = AVDTP_DISCOVER;
+               len = write(sk, buf, 2);
+               break;
+
+       case AVDTP_GET_CAPABILITIES:
+               hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+               hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+               hdr->signal_id = AVDTP_GET_CAPABILITIES;
+               buf[2] = 1 << 2; /* SEID 1 */
+               len = write(sk, buf, invalid ? 2 : 3);
+               break;
+
+       case AVDTP_SET_CONFIGURATION:
+               if (preconf)
+                       do_avdtp_send(sk, src, dst, cmd, 0, 0);
+               hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+               hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+               hdr->signal_id = AVDTP_SET_CONFIGURATION;
+               buf[2] = 1 << 2; /* ACP SEID */
+               buf[3] = 1 << 2; /* INT SEID */
+               memcpy(&buf[4], media_transport, sizeof(media_transport));
+               if (invalid)
+                       buf[5] = 0x01; /* LOSC != 0 */
+               len = write(sk, buf, 4 + sizeof(media_transport));
+               break;
+
+       case AVDTP_GET_CONFIGURATION:
+               if (preconf)
+                       do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+               hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+               hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+               hdr->signal_id = AVDTP_GET_CONFIGURATION;
+               if (invalid)
+                       buf[2] = 13 << 2; /* Invalid ACP SEID */
+               else
+                       buf[2] = 1 << 2; /* Valid ACP SEID */
+               len = write(sk, buf, 3);
+               break;
+
+       case AVDTP_OPEN:
+               if (preconf)
+                       do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+               hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+               hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+               hdr->signal_id = AVDTP_OPEN;
+               buf[2] = 1 << 2; /* ACP SEID */
+               len = write(sk, buf, 3);
+               break;
+
+       case AVDTP_START:
+               if (preconf)
+                       do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+               if (!invalid)
+                       do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 0);
+               hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+               hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+               hdr->signal_id = AVDTP_START;
+               buf[2] = 1 << 2; /* ACP SEID */
+               len = write(sk, buf, 3);
+               break;
+
+       case AVDTP_CLOSE:
+               if (preconf) {
+                       do_avdtp_send(sk, src, dst, AVDTP_SET_CONFIGURATION, 0, 0);
+                       do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 0);
+               }
+               hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+               hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+               hdr->signal_id = AVDTP_CLOSE;
+               if (invalid)
+                       buf[2] = 13 << 2; /* Invalid ACP SEID */
+               else
+                       buf[2] = 1 << 2; /* Valid ACP SEID */
+               len = write(sk, buf, 3);
+               break;
+
+       case AVDTP_SUSPEND:
+               if (invalid)
+                       do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, preconf);
+               else
+                       do_avdtp_send(sk, src, dst, AVDTP_START, 0, preconf);
+               hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+               hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+               hdr->signal_id = AVDTP_SUSPEND;
+               buf[2] = 1 << 2; /* ACP SEID */
+               len = write(sk, buf, 3);
+               break;
+
+       case AVDTP_ABORT:
+               do_avdtp_send(sk, src, dst, AVDTP_OPEN, 0, 1);
+               hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+               hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+               hdr->signal_id = AVDTP_ABORT;
+               buf[2] = 1 << 2; /* ACP SEID */
+               len = write(sk, buf, 3);
+               break;
+
+       default:
+               hdr->message_type = AVDTP_MSG_TYPE_COMMAND;
+               hdr->packet_type = AVDTP_PKT_TYPE_SINGLE;
+               hdr->signal_id = cmd;
+               len = write(sk, buf, 2);
+               break;
+       }
+
+       do {
+               len = read(sk, buf, sizeof(buf));
+
+               dump_buffer(buf, len);
+               dump_avdtp_header(hdr);
+       } while (len < 2 || (hdr->message_type != AVDTP_MSG_TYPE_ACCEPT &&
+                               hdr->message_type != AVDTP_MSG_TYPE_REJECT &&
+                               hdr->message_type != AVDTP_MSG_TYPE_GEN_REJECT));
+
+       if (cmd == AVDTP_OPEN && len >= 2 &&
+                               hdr->message_type == AVDTP_MSG_TYPE_ACCEPT)
+               media_sock = do_connect(src, dst, 0, 0);
+}
+
+static void do_avctp_send(int sk, int invalid)
+{
+       unsigned char buf[672];
+       struct avctp_header *hdr = (void *) buf;
+       unsigned char play_pressed[] = { 0x00, 0x48, 0x7c, 0x44, 0x00 };
+       ssize_t len;
+
+       memset(buf, 0, sizeof(buf));
+
+       hdr->packet_type = AVCTP_PACKET_SINGLE;
+       hdr->cr = AVCTP_COMMAND;
+       if (invalid)
+               hdr->pid = 0xffff;
+       else
+               hdr->pid = htons(AV_REMOTE_SVCLASS_ID);
+
+       memcpy(&buf[AVCTP_HEADER_LENGTH], play_pressed, sizeof(play_pressed));
+
+       len = write(sk, buf, AVCTP_HEADER_LENGTH + sizeof(play_pressed));
+
+       len = read(sk, buf, sizeof(buf));
+
+       dump_buffer(buf, len);
+       if (len >= AVCTP_HEADER_LENGTH)
+               dump_avctp_header(hdr);
+}
+
+static void usage(void)
+{
+       printf("avtest - Audio/Video testing ver %s\n", VERSION);
+       printf("Usage:\n"
+               "\tavtest [options] [remote address]\n");
+       printf("Options:\n"
+               "\t--device <hcidev>    HCI device\n"
+               "\t--reject <command>   Reject command\n"
+               "\t--send <command>     Send command\n"
+               "\t--preconf            Configure stream before actual command\n"
+               "\t--wait <N>           Wait N seconds before exiting\n"
+               "\t--fragment           Use minimum MTU and fragmented messages\n"
+               "\t--invalid <command>  Send invalid command\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "device",     1, 0, 'i' },
+       { "reject",     1, 0, 'r' },
+       { "send",       1, 0, 's' },
+       { "invalid",    1, 0, 'f' },
+       { "preconf",    0, 0, 'c' },
+       { "fragment",   0, 0, 'F' },
+       { "avctp",      0, 0, 'C' },
+       { "wait",       1, 0, 'w' },
+       { 0, 0, 0, 0 }
+};
+
+static unsigned char parse_cmd(const char *arg)
+{
+       if (!strncmp(arg, "discov", 6))
+               return AVDTP_DISCOVER;
+       else if (!strncmp(arg, "capa", 4))
+               return AVDTP_GET_CAPABILITIES;
+       else if (!strncmp(arg, "getcapa", 7))
+               return AVDTP_GET_CAPABILITIES;
+       else if (!strncmp(arg, "setconf", 7))
+               return AVDTP_SET_CONFIGURATION;
+       else if (!strncmp(arg, "getconf", 7))
+               return AVDTP_GET_CONFIGURATION;
+       else if (!strncmp(arg, "open", 4))
+               return AVDTP_OPEN;
+       else if (!strncmp(arg, "start", 5))
+               return AVDTP_START;
+       else if (!strncmp(arg, "close", 5))
+               return AVDTP_CLOSE;
+       else if (!strncmp(arg, "suspend", 7))
+               return AVDTP_SUSPEND;
+       else if (!strncmp(arg, "abort", 7))
+               return AVDTP_ABORT;
+       else
+               return atoi(arg);
+}
+
+enum {
+       MODE_NONE, MODE_REJECT, MODE_SEND,
+};
+
+int main(int argc, char *argv[])
+{
+       unsigned char cmd = 0x00;
+       bdaddr_t src, dst;
+       int opt, mode = MODE_NONE, sk, invalid = 0, preconf = 0, fragment = 0;
+       int avctp = 0, wait_before_exit = 0;
+
+       bacpy(&src, BDADDR_ANY);
+       bacpy(&dst, BDADDR_ANY);
+
+       while ((opt = getopt_long(argc, argv, "+i:r:s:f:hcFCw:",
+                                               main_options, NULL)) != EOF) {
+               switch (opt) {
+               case 'i':
+                       if (!strncmp(optarg, "hci", 3))
+                               hci_devba(atoi(optarg + 3), &src);
+                       else
+                               str2ba(optarg, &src);
+                       break;
+
+               case 'r':
+                       mode = MODE_REJECT;
+                       cmd = parse_cmd(optarg);
+                       break;
+
+               case 'f':
+                       invalid = 1;
+                       /* Intentionally missing break */
+
+               case 's':
+                       mode = MODE_SEND;
+                       cmd = parse_cmd(optarg);
+                       break;
+
+               case 'c':
+                       preconf = 1;
+                       break;
+
+               case 'F':
+                       fragment = 1;
+                       break;
+
+               case 'C':
+                       avctp = 1;
+                       break;
+
+               case 'w':
+                       wait_before_exit = atoi(optarg);
+                       break;
+
+               case 'h':
+               default:
+                       usage();
+                       exit(0);
+               }
+       }
+
+       if (argv[optind])
+               str2ba(argv[optind], &dst);
+
+       if (avctp) {
+               avctp = mode;
+               mode = MODE_SEND;
+       }
+
+       switch (mode) {
+       case MODE_REJECT:
+               do_listen(&src, cmd, fragment);
+               break;
+       case MODE_SEND:
+               sk = do_connect(&src, &dst, avctp, fragment);
+               if (sk < 0)
+                       exit(1);
+               if (avctp) {
+                       if (avctp == MODE_SEND)
+                               do_avctp_send(sk, invalid);
+                       else
+                               process_avctp(sk, cmd);
+               } else
+                       do_avdtp_send(sk, &src, &dst, cmd, invalid, preconf);
+               if (wait_before_exit) {
+                       printf("Waiting %d seconds before exiting\n", wait_before_exit);
+                       sleep(wait_before_exit);
+               }
+               if (media_sock >= 0)
+                       close(media_sock);
+               close(sk);
+               break;
+       default:
+               fprintf(stderr, "No operating mode specified!\n");
+               exit(1);
+       }
+
+       return 0;
+}
diff --git a/test/bdaddr.8 b/test/bdaddr.8
new file mode 100644 (file)
index 0000000..88345f8
--- /dev/null
@@ -0,0 +1,68 @@
+.TH BDADDR 8 "Sep 27 2005" BlueZ "Linux System Administration"
+.SH NAME
+bdaddr \- Utility for changing the Bluetooth device address
+.SH SYNOPSIS
+.B bdaddr
+.br
+.B bdaddr -h
+.br
+.B bdaddr [-i <dev>] [-r] [-t] [new bdaddr]
+
+.SH DESCRIPTION
+.LP
+.B
+bdaddr
+is used to query or set the local Bluetooth device address (BD_ADDR). If run
+with no arguments,
+.B
+bdaddr
+prints the chip manufacturer's name, and the current BD_ADDR. If the IEEE OUI
+index file "oui.txt" is installed on the system, the BD_ADDR owner will be
+displayed. If the optional [new bdaddr] argument is given, the device will be
+reprogrammed with that address. This can either be permanent or temporary, as
+specified by the -t flag. In both cases, the device must be reset before the
+new address will become active. This can be done with a 'soft' reset by
+specifying the -r flag, or a 'hard' reset by removing and replugging the
+device. A 'hard' reset will cause the address to revert to the current
+non-volatile value.
+.PP
+.B
+bdaddr
+uses manufacturer specific commands to set the address, and is therefore
+device specific. For this reason, not all devices are supported, and not all
+options are supported on all devices.
+Current supported manufacturers are:
+.B Ericsson, Cambridge Silicon Radio (CSR), Texas Instruments (TI), Zeevo
+and
+.B ST Microelectronics (ST)
+
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -i\ <dev>
+Specify a particular device to operate on. If not specified, default is the
+first available device.
+.TP
+.BI -r
+Reset device and make new BD_ADDR active.
+.B
+CSR
+devices only.
+.TP
+.BI -t
+Temporary change. Do not write to non-volatile memory.
+.B
+CSR
+devices only.
+.SH FILES
+.TP
+.I
+/usr/share/misc/oui.txt
+IEEE Organizationally Unique Identifier master file.
+Manually update from: http://standards.ieee.org/regauth/oui/oui.txt
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org>,
+man page by Adam Laurie <adam@algroup.co.uk>
+.PP
diff --git a/test/bdaddr.c b/test/bdaddr.c
new file mode 100644 (file)
index 0000000..f87fa38
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "oui.h"
+
+static int transient = 0;
+
+static int generic_reset_device(int dd)
+{
+       bdaddr_t bdaddr;
+       int err;
+
+       err = hci_send_cmd(dd, 0x03, 0x0003, 0, NULL);
+       if (err < 0)
+               return err;
+
+       return hci_read_bd_addr(dd, &bdaddr, 10000);
+}
+
+#define OCF_ERICSSON_WRITE_BD_ADDR     0x000d
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) ericsson_write_bd_addr_cp;
+#define ERICSSON_WRITE_BD_ADDR_CP_SIZE 6
+
+static int ericsson_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+       struct hci_request rq;
+       ericsson_write_bd_addr_cp cp;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = OCF_ERICSSON_WRITE_BD_ADDR;
+       rq.cparam = &cp;
+       rq.clen   = ERICSSON_WRITE_BD_ADDR_CP_SIZE;
+       rq.rparam = NULL;
+       rq.rlen   = 0;
+
+       if (hci_send_req(dd, &rq, 1000) < 0)
+               return -1;
+
+       return 0;
+}
+
+#define OCF_ERICSSON_STORE_IN_FLASH    0x0022
+typedef struct {
+       uint8_t         user_id;
+       uint8_t         flash_length;
+       uint8_t         flash_data[253];
+} __attribute__ ((packed)) ericsson_store_in_flash_cp;
+#define ERICSSON_STORE_IN_FLASH_CP_SIZE 255
+
+static int ericsson_store_in_flash(int dd, uint8_t user_id, uint8_t flash_length, uint8_t *flash_data)
+{
+       struct hci_request rq;
+       ericsson_store_in_flash_cp cp;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.user_id = user_id;
+       cp.flash_length = flash_length;
+       if (flash_length > 0)
+               memcpy(cp.flash_data, flash_data, flash_length);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = OCF_ERICSSON_STORE_IN_FLASH;
+       rq.cparam = &cp;
+       rq.clen   = ERICSSON_STORE_IN_FLASH_CP_SIZE;
+       rq.rparam = NULL;
+       rq.rlen   = 0;
+
+       if (hci_send_req(dd, &rq, 1000) < 0)
+               return -1;
+
+       return 0;
+}
+
+static int csr_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+       unsigned char cmd[] = { 0x02, 0x00, 0x0c, 0x00, 0x11, 0x47, 0x03, 0x70,
+                               0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       unsigned char cp[254], rp[254];
+       struct hci_request rq;
+
+       if (transient)
+               cmd[14] = 0x08;
+
+       cmd[16] = bdaddr->b[2];
+       cmd[17] = 0x00;
+       cmd[18] = bdaddr->b[0];
+       cmd[19] = bdaddr->b[1];
+       cmd[20] = bdaddr->b[3];
+       cmd[21] = 0x00;
+       cmd[22] = bdaddr->b[4];
+       cmd[23] = bdaddr->b[5];
+
+       memset(&cp, 0, sizeof(cp));
+       cp[0] = 0xc2;
+       memcpy(cp + 1, cmd, sizeof(cmd));
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x00;
+       rq.event  = EVT_VENDOR;
+       rq.cparam = cp;
+       rq.clen   = sizeof(cmd) + 1;
+       rq.rparam = rp;
+       rq.rlen   = sizeof(rp);
+
+       if (hci_send_req(dd, &rq, 2000) < 0)
+               return -1;
+
+       if (rp[0] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[9] + (rp[10] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+static int csr_reset_device(int dd)
+{
+       unsigned char cmd[] = { 0x02, 0x00, 0x09, 0x00,
+                               0x00, 0x00, 0x01, 0x40, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       unsigned char cp[254], rp[254];
+       struct hci_request rq;
+
+       if (transient)
+               cmd[6] = 0x02;
+
+       memset(&cp, 0, sizeof(cp));
+       cp[0] = 0xc2;
+       memcpy(cp + 1, cmd, sizeof(cmd));
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x00;
+       rq.event  = EVT_VENDOR;
+       rq.cparam = cp;
+       rq.clen   = sizeof(cmd) + 1;
+       rq.rparam = rp;
+       rq.rlen   = sizeof(rp);
+
+       if (hci_send_req(dd, &rq, 2000) < 0)
+               return -1;
+
+       return 0;
+}
+
+#define OCF_TI_WRITE_BD_ADDR           0x0006
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) ti_write_bd_addr_cp;
+#define TI_WRITE_BD_ADDR_CP_SIZE 6
+
+static int ti_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+       struct hci_request rq;
+       ti_write_bd_addr_cp cp;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = OCF_TI_WRITE_BD_ADDR;
+       rq.cparam = &cp;
+       rq.clen   = TI_WRITE_BD_ADDR_CP_SIZE;
+       rq.rparam = NULL;
+       rq.rlen   = 0;
+
+       if (hci_send_req(dd, &rq, 1000) < 0)
+               return -1;
+
+       return 0;
+}
+
+#define OCF_BCM_WRITE_BD_ADDR          0x0001
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) bcm_write_bd_addr_cp;
+#define BCM_WRITE_BD_ADDR_CP_SIZE 6
+
+static int bcm_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+       struct hci_request rq;
+       bcm_write_bd_addr_cp cp;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = OCF_BCM_WRITE_BD_ADDR;
+       rq.cparam = &cp;
+       rq.clen   = BCM_WRITE_BD_ADDR_CP_SIZE;
+       rq.rparam = NULL;
+       rq.rlen   = 0;
+
+       if (hci_send_req(dd, &rq, 1000) < 0)
+               return -1;
+
+       return 0;
+}
+
+#define OCF_ZEEVO_WRITE_BD_ADDR                0x0001
+typedef struct {
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) zeevo_write_bd_addr_cp;
+#define ZEEVO_WRITE_BD_ADDR_CP_SIZE 6
+
+static int zeevo_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+       struct hci_request rq;
+       zeevo_write_bd_addr_cp cp;
+
+       memset(&cp, 0, sizeof(cp));
+       bacpy(&cp.bdaddr, bdaddr);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = OCF_ZEEVO_WRITE_BD_ADDR;
+       rq.cparam = &cp;
+       rq.clen   = ZEEVO_WRITE_BD_ADDR_CP_SIZE;
+       rq.rparam = NULL;
+       rq.rlen   = 0;
+
+       if (hci_send_req(dd, &rq, 1000) < 0)
+               return -1;
+
+       return 0;
+}
+
+static int st_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+       return ericsson_store_in_flash(dd, 0xfe, 6, (uint8_t *) bdaddr);
+}
+
+static struct {
+       uint16_t compid;
+       int (*write_bd_addr)(int dd, bdaddr_t *bdaddr);
+       int (*reset_device)(int dd);
+} vendor[] = {
+       { 0,            ericsson_write_bd_addr, NULL                    },
+       { 10,           csr_write_bd_addr,      csr_reset_device        },
+       { 13,           ti_write_bd_addr,       NULL                    },
+       { 15,           bcm_write_bd_addr,      generic_reset_device    },
+       { 18,           zeevo_write_bd_addr,    NULL                    },
+       { 48,           st_write_bd_addr,       generic_reset_device    },
+       { 57,           ericsson_write_bd_addr, generic_reset_device    },
+       { 65535,        NULL,                   NULL                    },
+};
+
+static void usage(void)
+{
+       printf("bdaddr - Utility for changing the Bluetooth device address\n\n");
+       printf("Usage:\n"
+               "\tbdaddr [-i <dev>] [-r] [-t] [new bdaddr]\n");
+}
+
+static struct option main_options[] = {
+       { "device",     1, 0, 'i' },
+       { "reset",      0, 0, 'r' },
+       { "transient",  0, 0, 't' },
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       struct hci_dev_info di;
+       struct hci_version ver;
+       bdaddr_t bdaddr;
+       char addr[18], oui[9], *comp;
+       int i, dd, opt, dev = 0, reset = 0;
+
+       bacpy(&bdaddr, BDADDR_ANY);
+
+       while ((opt=getopt_long(argc, argv, "+i:rth", main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'i':
+                       dev = hci_devid(optarg);
+                       if (dev < 0) {
+                               perror("Invalid device");
+                               exit(1);
+                       }
+                       break;
+
+               case 'r':
+                       reset = 1;
+                       break;
+
+               case 't':
+                       transient = 1;
+                       break;
+
+               case 'h':
+               default:
+                       usage();
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       dd = hci_open_dev(dev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (hci_devinfo(dev, &di) < 0) {
+               fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               hci_close_dev(dd);
+               exit(1);
+       }
+
+       if (hci_read_local_version(dd, &ver, 1000) < 0) {
+               fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               hci_close_dev(dd);
+               exit(1);
+       }
+
+       if (!bacmp(&di.bdaddr, BDADDR_ANY)) {
+               if (hci_read_bd_addr(dd, &bdaddr, 1000) < 0) {
+                       fprintf(stderr, "Can't read address for hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+                       hci_close_dev(dd);
+                       exit(1);
+               }
+       } else
+               bacpy(&bdaddr, &di.bdaddr);
+
+       printf("Manufacturer:   %s (%d)\n",
+                       bt_compidtostr(ver.manufacturer), ver.manufacturer);
+
+       ba2oui(&bdaddr, oui);
+       comp = ouitocomp(oui);
+
+       ba2str(&bdaddr, addr);
+       printf("Device address: %s", addr);
+
+       if (comp) {
+               printf(" (%s)\n", comp);
+               free(comp);
+       } else
+               printf("\n");
+
+       if (argc < 1) {
+               hci_close_dev(dd);
+               exit(0);
+       }
+
+       str2ba(argv[0], &bdaddr);
+       if (!bacmp(&bdaddr, BDADDR_ANY)) {
+               hci_close_dev(dd);
+               exit(0);
+       }
+
+       for (i = 0; vendor[i].compid != 65535; i++)
+               if (ver.manufacturer == vendor[i].compid) {
+                       ba2oui(&bdaddr, oui);
+                       comp = ouitocomp(oui);
+
+                       ba2str(&bdaddr, addr);
+                       printf("New BD address: %s", addr);
+
+                       if (comp) {
+                               printf(" (%s)\n\n", comp);
+                               free(comp);
+                       } else
+                               printf("\n\n");
+
+
+                       if (vendor[i].write_bd_addr(dd, &bdaddr) < 0) {
+                               fprintf(stderr, "Can't write new address\n");
+                               hci_close_dev(dd);
+                               exit(1);
+                       }
+
+                       printf("Address changed - ");
+
+                       if (reset && vendor[i].reset_device) {
+                               if (vendor[i].reset_device(dd) < 0) {
+                                       printf("Reset device manually\n");
+                               } else {
+                                       ioctl(dd, HCIDEVRESET, dev);
+                                       printf("Device reset successfully\n");
+                               }
+                       } else {
+                               printf("Reset device now\n");
+                       }
+
+                       //ioctl(dd, HCIDEVRESET, dev);
+                       //ioctl(dd, HCIDEVDOWN, dev);
+                       //ioctl(dd, HCIDEVUP, dev);
+
+                       hci_close_dev(dd);
+                       exit(0);
+               }
+
+       hci_close_dev(dd);
+
+       printf("\n");
+       fprintf(stderr, "Unsupported manufacturer\n");
+
+       exit(1);
+}
diff --git a/test/btiotest.c b/test/btiotest.c
new file mode 100644 (file)
index 0000000..66a7dd2
--- /dev/null
@@ -0,0 +1,618 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009-2010  Nokia Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "btio.h"
+
+#define DEFAULT_ACCEPT_TIMEOUT 2
+static gint opt_update_sec = 0;
+
+struct io_data {
+       guint ref;
+       GIOChannel *io;
+       BtIOType type;
+       gint reject;
+       gint disconn;
+       gint accept;
+};
+
+static void io_data_unref(struct io_data *data)
+{
+       data->ref--;
+
+       if (data->ref)
+               return;
+
+       if (data->io)
+               g_io_channel_unref(data->io);
+
+       g_free(data);
+}
+
+static struct io_data *io_data_ref(struct io_data *data)
+{
+       data->ref++;
+       return data;
+}
+
+static struct io_data *io_data_new(GIOChannel *io, BtIOType type, gint reject,
+                                               gint disconn, gint accept)
+{
+       struct io_data *data;
+
+       data = g_new0(struct io_data, 1);
+       data->io = io;
+       data->type = type;
+       data->reject = reject;
+       data->disconn = disconn;
+       data->accept = accept;
+
+       return io_data_ref(data);
+}
+
+static gboolean io_watch(GIOChannel *io, GIOCondition cond, gpointer user_data)
+{
+       printf("Disconnected\n");
+       return FALSE;
+}
+
+static gboolean disconn_timeout(gpointer user_data)
+{
+       struct io_data *data = user_data;
+
+       printf("Disconnecting\n");
+
+       g_io_channel_shutdown(data->io, TRUE, NULL);
+
+       return FALSE;
+}
+
+static void update_sec_level(struct io_data *data)
+{
+       GError *err = NULL;
+       int sec_level;
+
+       if (!bt_io_get(data->io, data->type, &err,
+                                       BT_IO_OPT_SEC_LEVEL, &sec_level,
+                                       BT_IO_OPT_INVALID)) {
+               printf("bt_io_get(OPT_SEC_LEVEL): %s\n", err->message);
+               g_clear_error(&err);
+               return;
+       }
+
+       printf("sec_level=%d\n", sec_level);
+
+       if (opt_update_sec == sec_level)
+               return;
+
+       if (!bt_io_set(data->io, data->type, &err,
+                                       BT_IO_OPT_SEC_LEVEL, opt_update_sec,
+                                       BT_IO_OPT_INVALID)) {
+               printf("bt_io_set(OPT_SEC_LEVEL): %s\n", err->message);
+               g_clear_error(&err);
+       }
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+       struct io_data *data = user_data;
+       GIOCondition cond;
+       char addr[18];
+       uint16_t handle;
+       uint8_t cls[3];
+
+       if (err) {
+               printf("Connecting failed: %s\n", err->message);
+               return;
+       }
+
+       if (!bt_io_get(io, data->type, &err,
+                       BT_IO_OPT_DEST, addr,
+                       BT_IO_OPT_HANDLE, &handle,
+                       BT_IO_OPT_CLASS, cls,
+                       BT_IO_OPT_INVALID)) {
+               printf("Unable to get destination address: %s\n",
+                                                               err->message);
+               g_clear_error(&err);
+               strcpy(addr, "(unknown)");
+       }
+
+       printf("Successfully connected to %s. handle=%u, class=%02x%02x%02x\n",
+                       addr, handle, cls[0], cls[1], cls[2]);
+
+       if (data->type == BT_IO_L2CAP || data->type == BT_IO_SCO) {
+               uint16_t omtu, imtu;
+
+               if (!bt_io_get(io, data->type, &err,
+                                       BT_IO_OPT_OMTU, &omtu,
+                                       BT_IO_OPT_IMTU, &imtu,
+                                       BT_IO_OPT_INVALID)) {
+                       printf("Unable to get L2CAP MTU sizes: %s\n",
+                                                               err->message);
+                       g_clear_error(&err);
+               } else
+                       printf("imtu=%u, omtu=%u\n", imtu, omtu);
+       }
+
+       if (data->type == BT_IO_L2CAP) {
+               uint8_t key_size;
+
+               if (!bt_io_get(io, data->type, &err,
+                                       BT_IO_OPT_KEY_SIZE, &key_size,
+                                       BT_IO_OPT_INVALID)) {
+                       printf("Unable to get L2CAP Key size: %s\n",
+                                                               err->message);
+                       g_clear_error(&err);
+               } else
+                       printf("key_size=%u\n", key_size);
+       }
+
+       if (data->disconn == 0) {
+               g_io_channel_shutdown(io, TRUE, NULL);
+               printf("Disconnected\n");
+               return;
+       }
+
+       if (data->io == NULL)
+               data->io = g_io_channel_ref(io);
+
+       if (data->disconn > 0) {
+               io_data_ref(data);
+               g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, data->disconn,
+                                       disconn_timeout, data,
+                                       (GDestroyNotify) io_data_unref);
+       }
+
+
+       io_data_ref(data);
+
+       if (opt_update_sec > 0)
+               update_sec_level(data);
+
+       cond = G_IO_NVAL | G_IO_HUP | G_IO_ERR;
+       g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, io_watch, data,
+                                       (GDestroyNotify) io_data_unref);
+}
+
+static gboolean confirm_timeout(gpointer user_data)
+{
+       struct io_data *data = user_data;
+
+       if (data->reject >= 0) {
+               printf("Rejecting connection\n");
+               g_io_channel_shutdown(data->io, TRUE, NULL);
+               return FALSE;
+       }
+
+       printf("Accepting connection\n");
+
+       io_data_ref(data);
+
+       if (opt_update_sec > 0)
+               update_sec_level(data);
+
+       if (!bt_io_accept(data->io, connect_cb, data,
+                               (GDestroyNotify) io_data_unref, NULL)) {
+               printf("bt_io_accept() failed\n");
+               io_data_unref(data);
+       }
+
+       return FALSE;
+}
+
+static void confirm_cb(GIOChannel *io, gpointer user_data)
+{
+       char addr[18];
+       struct io_data *data = user_data;
+       GError *err = NULL;
+
+       if (!bt_io_get(io, data->type, &err, BT_IO_OPT_DEST, addr,
+                                                       BT_IO_OPT_INVALID)) {
+               printf("bt_io_get(OPT_DEST): %s\n", err->message);
+               g_clear_error(&err);
+       } else
+               printf("Got confirmation request for %s\n", addr);
+
+       if (data->accept < 0 && data->reject < 0)
+               return;
+
+       if (data->reject == 0) {
+               printf("Rejecting connection\n");
+               g_io_channel_shutdown(io, TRUE, NULL);
+               return;
+       }
+
+       data->io = g_io_channel_ref(io);
+       io_data_ref(data);
+
+       if (data->accept == 0) {
+               if (!bt_io_accept(io, connect_cb, data,
+                                       (GDestroyNotify) io_data_unref,
+                                       &err)) {
+                       printf("bt_io_accept() failed: %s\n", err->message);
+                       g_clear_error(&err);
+                       io_data_unref(data);
+                       return;
+               }
+       } else {
+               gint seconds = (data->reject > 0) ?
+                                               data->reject : data->accept;
+               g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, seconds,
+                                       confirm_timeout, data,
+                                       (GDestroyNotify) io_data_unref);
+       }
+}
+
+static void l2cap_connect(const char *src, const char *dst, uint16_t psm,
+                                               uint16_t cid, gint disconn,
+                                               gint sec, gint prio)
+{
+       struct io_data *data;
+       GError *err = NULL;
+
+       printf("Connecting to %s L2CAP PSM %u\n", dst, psm);
+
+       data = io_data_new(NULL, BT_IO_L2CAP, -1, disconn, -1);
+
+       if (src)
+               data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
+                                               (GDestroyNotify) io_data_unref,
+                                               &err,
+                                               BT_IO_OPT_SOURCE, src,
+                                               BT_IO_OPT_DEST, dst,
+                                               BT_IO_OPT_PSM, psm,
+                                               BT_IO_OPT_CID, cid,
+                                               BT_IO_OPT_SEC_LEVEL, sec,
+                                               BT_IO_OPT_PRIORITY, prio,
+                                               BT_IO_OPT_INVALID);
+       else
+               data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
+                                               (GDestroyNotify) io_data_unref,
+                                               &err,
+                                               BT_IO_OPT_DEST, dst,
+                                               BT_IO_OPT_PSM, psm,
+                                               BT_IO_OPT_CID, cid,
+                                               BT_IO_OPT_SEC_LEVEL, sec,
+                                               BT_IO_OPT_PRIORITY, prio,
+                                               BT_IO_OPT_INVALID);
+
+       if (!data->io) {
+               printf("Connecting to %s failed: %s\n", dst, err->message);
+               g_error_free(err);
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void l2cap_listen(const char *src, uint16_t psm, gint defer,
+                               gint reject, gint disconn, gint accept,
+                               gint sec, gboolean master)
+{
+       struct io_data *data;
+       BtIOConnect conn;
+       BtIOConfirm cfm;
+       GIOChannel *l2_srv;
+       GError *err = NULL;
+
+       if (defer) {
+               conn = NULL;
+               cfm = confirm_cb;
+       } else {
+               conn = connect_cb;
+               cfm = NULL;
+       }
+
+       printf("Listening on L2CAP PSM %u\n", psm);
+
+       data = io_data_new(NULL, BT_IO_L2CAP, reject, disconn, accept);
+
+       if (src)
+               l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
+                                       data, (GDestroyNotify) io_data_unref,
+                                       &err,
+                                       BT_IO_OPT_SOURCE, src,
+                                       BT_IO_OPT_PSM, psm,
+                                       BT_IO_OPT_SEC_LEVEL, sec,
+                                       BT_IO_OPT_MASTER, master,
+                                       BT_IO_OPT_INVALID);
+       else
+               l2_srv = bt_io_listen(BT_IO_L2CAP, conn, cfm,
+                                       data, (GDestroyNotify) io_data_unref,
+                                       &err,
+                                       BT_IO_OPT_PSM, psm,
+                                       BT_IO_OPT_SEC_LEVEL, sec,
+                                       BT_IO_OPT_MASTER, master,
+                                       BT_IO_OPT_INVALID);
+
+       if (!l2_srv) {
+               printf("Listening failed: %s\n", err->message);
+               g_error_free(err);
+               exit(EXIT_FAILURE);
+       }
+
+       g_io_channel_unref(l2_srv);
+}
+
+static void rfcomm_connect(const char *src, const char *dst, uint8_t ch,
+                                               gint disconn, gint sec)
+{
+       struct io_data *data;
+       GError *err = NULL;
+
+       printf("Connecting to %s RFCOMM channel %u\n", dst, ch);
+
+       data = io_data_new(NULL, BT_IO_RFCOMM, -1, disconn, -1);
+
+       if (src)
+               data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
+                                               (GDestroyNotify) io_data_unref,
+                                               &err,
+                                               BT_IO_OPT_SOURCE, src,
+                                               BT_IO_OPT_DEST, dst,
+                                               BT_IO_OPT_CHANNEL, ch,
+                                               BT_IO_OPT_SEC_LEVEL, sec,
+                                               BT_IO_OPT_INVALID);
+       else
+               data->io = bt_io_connect(BT_IO_RFCOMM, connect_cb, data,
+                                               (GDestroyNotify) io_data_unref,
+                                               &err,
+                                               BT_IO_OPT_DEST, dst,
+                                               BT_IO_OPT_CHANNEL, ch,
+                                               BT_IO_OPT_SEC_LEVEL, sec,
+                                               BT_IO_OPT_INVALID);
+
+       if (!data->io) {
+               printf("Connecting to %s failed: %s\n", dst, err->message);
+               g_error_free(err);
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void rfcomm_listen(const char *src, uint8_t ch, gboolean defer,
+                               gint reject, gint disconn, gint accept,
+                               gint sec, gboolean master)
+{
+       struct io_data *data;
+       BtIOConnect conn;
+       BtIOConfirm cfm;
+       GIOChannel *rc_srv;
+       GError *err = NULL;
+
+       if (defer) {
+               conn = NULL;
+               cfm = confirm_cb;
+       } else {
+               conn = connect_cb;
+               cfm = NULL;
+       }
+
+       data = io_data_new(NULL, BT_IO_RFCOMM, reject, disconn, accept);
+
+       if (src)
+               rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
+                                       data, (GDestroyNotify) io_data_unref,
+                                       &err,
+                                       BT_IO_OPT_SOURCE, src,
+                                       BT_IO_OPT_CHANNEL, ch,
+                                       BT_IO_OPT_SEC_LEVEL, sec,
+                                       BT_IO_OPT_MASTER, master,
+                                       BT_IO_OPT_INVALID);
+       else
+               rc_srv = bt_io_listen(BT_IO_RFCOMM, conn, cfm,
+                                       data, (GDestroyNotify) io_data_unref,
+                                       &err,
+                                       BT_IO_OPT_CHANNEL, ch,
+                                       BT_IO_OPT_SEC_LEVEL, sec,
+                                       BT_IO_OPT_MASTER, master,
+                                       BT_IO_OPT_INVALID);
+
+       if (!rc_srv) {
+               printf("Listening failed: %s\n", err->message);
+               g_error_free(err);
+               exit(EXIT_FAILURE);
+       }
+
+       bt_io_get(rc_srv, BT_IO_RFCOMM, &err,
+                       BT_IO_OPT_CHANNEL, &ch,
+                       BT_IO_OPT_INVALID);
+
+       printf("Listening on RFCOMM channel %u\n", ch);
+
+       g_io_channel_unref(rc_srv);
+}
+
+static void sco_connect(const char *src, const char *dst, gint disconn)
+{
+       struct io_data *data;
+       GError *err = NULL;
+
+       printf("Connecting SCO to %s\n", dst);
+
+       data = io_data_new(NULL, BT_IO_SCO, -1, disconn, -1);
+
+       if (src)
+               data->io = bt_io_connect(BT_IO_SCO, connect_cb, data,
+                                               (GDestroyNotify) io_data_unref,
+                                               &err,
+                                               BT_IO_OPT_SOURCE, src,
+                                               BT_IO_OPT_DEST, dst,
+                                               BT_IO_OPT_INVALID);
+       else
+               data->io = bt_io_connect(BT_IO_SCO, connect_cb, data,
+                                               (GDestroyNotify) io_data_unref,
+                                               &err,
+                                               BT_IO_OPT_DEST, dst,
+                                               BT_IO_OPT_INVALID);
+
+       if (!data->io) {
+               printf("Connecting to %s failed: %s\n", dst, err->message);
+               g_error_free(err);
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void sco_listen(const char *src, gint disconn)
+{
+       struct io_data *data;
+       GIOChannel *sco_srv;
+       GError *err = NULL;
+
+       printf("Listening for SCO connections\n");
+
+       data = io_data_new(NULL, BT_IO_SCO, -1, disconn, -1);
+
+       if (src)
+               sco_srv = bt_io_listen(BT_IO_SCO, connect_cb, NULL,
+                                       data, (GDestroyNotify) io_data_unref,
+                                       &err,
+                                       BT_IO_OPT_SOURCE, src,
+                                       BT_IO_OPT_INVALID);
+       else
+               sco_srv = bt_io_listen(BT_IO_SCO, connect_cb, NULL,
+                                       data, (GDestroyNotify) io_data_unref,
+                                       &err, BT_IO_OPT_INVALID);
+
+       if (!sco_srv) {
+               printf("Listening failed: %s\n", err->message);
+               g_error_free(err);
+               exit(EXIT_FAILURE);
+       }
+
+       g_io_channel_unref(sco_srv);
+}
+
+static gint opt_channel = -1;
+static gint opt_psm = 0;
+static gboolean opt_sco = FALSE;
+static gboolean opt_defer = FALSE;
+static char *opt_dev = NULL;
+static gint opt_reject = -1;
+static gint opt_disconn = -1;
+static gint opt_accept = DEFAULT_ACCEPT_TIMEOUT;
+static gint opt_sec = 0;
+static gboolean opt_master = FALSE;
+static gint opt_priority = 0;
+static gint opt_cid = 0;
+
+static GMainLoop *main_loop;
+
+static GOptionEntry options[] = {
+       { "channel", 'c', 0, G_OPTION_ARG_INT, &opt_channel,
+                               "RFCOMM channel" },
+       { "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
+                               "L2CAP PSM" },
+       { "cid", 'j', 0, G_OPTION_ARG_INT, &opt_cid,
+                               "L2CAP CID" },
+       { "sco", 's', 0, G_OPTION_ARG_NONE, &opt_sco,
+                               "Use SCO" },
+       { "defer", 'd', 0, G_OPTION_ARG_NONE, &opt_defer,
+                               "Use DEFER_SETUP for incoming connections" },
+       { "sec-level", 'S', 0, G_OPTION_ARG_INT, &opt_sec,
+                               "Security level" },
+       { "update-sec-level", 'U', 0, G_OPTION_ARG_INT, &opt_update_sec,
+                               "Update security level" },
+       { "dev", 'i', 0, G_OPTION_ARG_STRING, &opt_dev,
+                               "Which HCI device to use" },
+       { "reject", 'r', 0, G_OPTION_ARG_INT, &opt_reject,
+                               "Reject connection after N seconds" },
+       { "disconnect", 'D', 0, G_OPTION_ARG_INT, &opt_disconn,
+                               "Disconnect connection after N seconds" },
+       { "accept", 'a', 0, G_OPTION_ARG_INT, &opt_accept,
+                               "Accept connection after N seconds" },
+       { "master", 'm', 0, G_OPTION_ARG_NONE, &opt_master,
+                               "Master role switch (incoming connections)" },
+       { "priority", 'P', 0, G_OPTION_ARG_INT, &opt_priority,
+                               "Transmission priority: Setting a priority "
+                               "outside the range 0 to 6 requires the"
+                               "CAP_NET_ADMIN capability." },
+       { NULL },
+};
+
+static void sig_term(int sig)
+{
+       g_main_loop_quit(main_loop);
+}
+
+int main(int argc, char *argv[])
+{
+       GOptionContext *context;
+
+       context = g_option_context_new(NULL);
+       g_option_context_add_main_entries(context, options, NULL);
+
+       if (!g_option_context_parse(context, &argc, &argv, NULL))
+               exit(EXIT_FAILURE);
+
+       g_option_context_free(context);
+
+       printf("accept=%d, reject=%d, discon=%d, defer=%d, sec=%d,"
+               " update_sec=%d, prio=%d\n", opt_accept, opt_reject,
+               opt_disconn, opt_defer, opt_sec, opt_update_sec, opt_priority);
+
+       if (opt_psm || opt_cid) {
+               if (argc > 1)
+                       l2cap_connect(opt_dev, argv[1], opt_psm, opt_cid,
+                                       opt_disconn, opt_sec, opt_priority);
+               else
+                       l2cap_listen(opt_dev, opt_psm, opt_defer, opt_reject,
+                                       opt_disconn, opt_accept, opt_sec,
+                                       opt_master);
+       }
+
+       if (opt_channel != -1) {
+               if (argc > 1)
+                       rfcomm_connect(opt_dev, argv[1], opt_channel,
+                                                       opt_disconn, opt_sec);
+               else
+                       rfcomm_listen(opt_dev, opt_channel, opt_defer,
+                                       opt_reject, opt_disconn, opt_accept,
+                                       opt_sec, opt_master);
+       }
+
+       if (opt_sco) {
+               if (argc > 1)
+                       sco_connect(opt_dev, argv[1], opt_disconn);
+               else
+                       sco_listen(opt_dev, opt_disconn);
+       }
+
+       signal(SIGTERM, sig_term);
+       signal(SIGINT, sig_term);
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       g_main_loop_run(main_loop);
+
+       g_main_loop_unref(main_loop);
+
+       printf("Exiting\n");
+
+       exit(EXIT_SUCCESS);
+}
diff --git a/test/dbusdef.py b/test/dbusdef.py
new file mode 100644 (file)
index 0000000..5af6153
--- /dev/null
@@ -0,0 +1,16 @@
+import dbus
+
+bus = dbus.SystemBus()
+
+
+dummy = dbus.Interface(bus.get_object('org.bluez', '/'), 'org.freedesktop.DBus.Introspectable')
+
+#print dummy.Introspect()
+
+
+manager = dbus.Interface(bus.get_object('org.bluez', '/'), 'org.bluez.Manager')
+
+try:
+       adapter = dbus.Interface(bus.get_object('org.bluez', manager.DefaultAdapter()), 'org.bluez.Adapter')
+except:
+       pass
diff --git a/test/gaptest.c b/test/gaptest.c
new file mode 100644 (file)
index 0000000..3e9f534
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <dbus/dbus.h>
+
+#define BLUEZ_SERVICE  "org.bluez"
+
+#define MANAGER_PATH   "/"
+#define MANAGER_INTF   BLUEZ_SERVICE ".Manager"
+#define ADAPTER_INTF   BLUEZ_SERVICE ".Adapter"
+
+static char *get_adapter(DBusConnection *conn)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+       const char *path;
+       char *result = NULL;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, MANAGER_PATH,
+                                       MANAGER_INTF, "DefaultAdapter");
+       if (!message)
+               return NULL;
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(conn,
+                                                       message, -1, &error);
+
+       dbus_message_unref(message);
+
+       if (!reply) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to set property\n");
+               return NULL;
+       }
+
+       if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               goto done;
+
+       printf("Using default adapter %s\n", path);
+
+       result = strdup(path);
+
+done:
+       dbus_message_unref(reply);
+
+       return result;
+}
+
+static char *find_device(DBusConnection *conn, const char *adapter,
+                                                       const char *address)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+       const char *path;
+       char *result = NULL;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, adapter,
+                                       ADAPTER_INTF, "FindDevice");
+       if (!message)
+               return NULL;
+
+       dbus_message_append_args(message, DBUS_TYPE_STRING, &address,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(conn,
+                                                       message, -1, &error);
+
+       dbus_message_unref(message);
+
+       if (!reply) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to set property\n");
+               return NULL;
+       }
+
+       if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                               DBUS_TYPE_INVALID) == FALSE)
+               goto done;
+
+       printf("Using device %s for address %s\n", path, address);
+
+       result = strdup(path);
+
+done:
+       dbus_message_unref(reply);
+
+       return result;
+}
+
+static int remove_device(DBusConnection *conn, const char *adapter,
+                                                       const char *device)
+{
+       DBusMessage *message, *reply;
+       DBusError error;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, adapter,
+                                       ADAPTER_INTF, "RemoveDevice");
+       if (!message)
+               return -ENOMEM;
+
+       dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &device,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(conn,
+                                                       message, -1, &error);
+
+       dbus_message_unref(message);
+
+       if (!reply) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to set property\n");
+               return -EIO;
+       }
+
+       dbus_message_unref(reply);
+
+       printf("Removed device %s\n", device);
+
+       return 0;
+}
+
+static int set_property(DBusConnection *conn, const char *adapter,
+                                       const char *key, int type, void *val)
+{
+       DBusMessage *message, *reply;
+       DBusMessageIter array, value;
+       DBusError error;
+       const char *signature;
+
+       message = dbus_message_new_method_call(BLUEZ_SERVICE, adapter,
+                                               ADAPTER_INTF, "SetProperty");
+       if (!message)
+               return -ENOMEM;
+
+       switch (type) {
+       case DBUS_TYPE_BOOLEAN:
+               signature = DBUS_TYPE_BOOLEAN_AS_STRING;
+               break;
+       case DBUS_TYPE_UINT32:
+               signature = DBUS_TYPE_UINT32_AS_STRING;
+               break;
+       default:
+               return -EILSEQ;
+       }
+
+       dbus_message_iter_init_append(message, &array);
+
+       dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &key);
+
+       dbus_message_iter_open_container(&array, DBUS_TYPE_VARIANT,
+                                                       signature, &value);
+       dbus_message_iter_append_basic(&value, type, val);
+       dbus_message_iter_close_container(&array, &value);
+
+       dbus_error_init(&error);
+
+       reply = dbus_connection_send_with_reply_and_block(conn,
+                                                       message, -1, &error);
+
+       dbus_message_unref(message);
+
+       if (!reply) {
+               if (dbus_error_is_set(&error) == TRUE) {
+                       fprintf(stderr, "%s\n", error.message);
+                       dbus_error_free(&error);
+               } else
+                       fprintf(stderr, "Failed to set property\n");
+               return -EIO;
+       }
+
+       dbus_message_unref(reply);
+
+       printf("Set property %s for %s\n", key, adapter);
+
+       return 0;
+}
+
+static void usage(void)
+{
+       printf("gaptest - GAP testing\n"
+               "Usage:\n");
+       printf("\tgaptest [options]\n");
+       printf("Options:\n"
+               "\t-T <timeout>        Set timeout\n"
+               "\t-P <powered>        Set powered\n"
+               "\t-D <discoverable>   Set discoverable\n"
+               "\t-B <pairable>       Set pairable\n"
+               "\t-C <address>        Create device\n"
+               "\t-R <address>        Remove device\n");
+}
+
+int main(int argc, char *argv[])
+{
+       DBusConnection *conn;
+       char *adapter, *device;
+       const char *create = NULL, *remove = NULL;
+       int opt, timeout = -1, powered = -1, discoverable = -1, pairable = -1;
+
+       while ((opt = getopt(argc, argv, "T:P:D:B:C:R:h")) != EOF) {
+               switch (opt) {
+               case 'T':
+                       timeout = atoi(optarg);
+                       break;
+               case 'P':
+                       powered = atoi(optarg);
+                       break;
+               case 'D':
+                       discoverable = atoi(optarg);
+                       break;
+               case 'B':
+                       pairable = atoi(optarg);
+                       break;
+               case 'C':
+                       create = optarg;
+                       break;
+               case 'R':
+                       remove = optarg;
+                       break;
+               case 'h':
+                       usage();
+                       exit(0);
+               default:
+                       usage();
+                       exit(1);
+               }
+       }
+
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (!conn) {
+               fprintf(stderr, "Can't get on system bus\n");
+               exit(1);
+       }
+
+       adapter = get_adapter(conn);
+       if (!adapter) {
+               fprintf(stderr, "Can't get default adapter\n");
+               exit(1);
+       }
+
+       if (powered >= 0) {
+               set_property(conn, adapter, "Powered",
+                                       DBUS_TYPE_BOOLEAN, &powered);
+       }
+
+       if (discoverable >= 0) {
+               set_property(conn, adapter, "Discoverable",
+                                       DBUS_TYPE_BOOLEAN, &discoverable);
+
+               if (timeout >= 0)
+                       set_property(conn, adapter, "DiscoverableTimeout",
+                                               DBUS_TYPE_UINT32, &timeout);
+       }
+
+       if (pairable >= 0) {
+               set_property(conn, adapter, "Pairable",
+                                       DBUS_TYPE_BOOLEAN, &pairable);
+
+               if (timeout >= 0)
+                       set_property(conn, adapter, "PairableTimeout",
+                                               DBUS_TYPE_UINT32, &timeout);
+       }
+
+       if (create) {
+               device = find_device(conn, adapter, create);
+               if (!device) {
+                       fprintf(stderr, "Can't find device\n");
+                       exit(1);
+               }
+
+               free(device);
+       }
+
+       if (remove) {
+               device = find_device(conn, adapter, remove);
+               if (!device) {
+                       fprintf(stderr, "Can't find device\n");
+                       exit(1);
+               }
+
+               remove_device(conn, adapter, device);
+
+               free(device);
+       }
+
+       free(adapter);
+
+       dbus_connection_unref(conn);
+
+       return 0;
+}
diff --git a/test/hciemu.1 b/test/hciemu.1
new file mode 100644 (file)
index 0000000..cecaeb7
--- /dev/null
@@ -0,0 +1,31 @@
+.TH HCIEMU 1 "Jul 6 2009" BlueZ ""
+.SH NAME
+hciemu \- HCI emulator
+.SH SYNOPSIS
+.B hciemu
+[\fIoptions\fR] \fIlocal_address\fR
+
+.SH DESCRIPTION
+.LP
+.B
+hciemu
+is used to emulate an HCI via \fBhci_vhci\fR kernel module
+
+.SH OPTIONS
+.TP
+.BI -d\  device
+use specified \fIdevice\fR
+.TP
+.BI -b\  bdaddr
+emulate \fIbdaddr\fR
+.TP
+.BI -s\  file
+create snoop file \fIfile\fR
+.TP
+.B -n
+do not detach
+
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org> and Maxim Krasnyansky
+<maxk@qualcomm.com>, man page by Filippo Giunchedi <filippo@debian.org>
+.PP
diff --git a/test/hciemu.c b/test/hciemu.c
new file mode 100644 (file)
index 0000000..4c62223
--- /dev/null
@@ -0,0 +1,1292 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2002  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#define VHCI_DEV               "/dev/vhci"
+
+#define VHCI_MAX_CONN          12
+
+#define VHCI_ACL_MTU           192
+#define VHCI_ACL_MAX_PKT       8
+
+struct vhci_device {
+       uint8_t         features[8];
+       uint8_t         name[248];
+       uint8_t         dev_class[3];
+       uint8_t         scan_enable;
+       uint8_t         ssp_mode;
+       uint8_t         inq_mode;
+       uint8_t         eir_fec;
+       uint8_t         eir_data[HCI_MAX_EIR_LENGTH];
+       uint8_t         le_mode;
+       uint8_t         le_simul;
+       uint16_t        acl_cnt;
+       bdaddr_t        bdaddr;
+       int             dev_fd;
+       int             scan_fd;
+       int             dd;
+};
+
+struct vhci_conn {
+       bdaddr_t        dest;
+       uint16_t        handle;
+       int             fd;
+};
+
+struct vhci_link_info {
+       bdaddr_t        bdaddr;
+       uint8_t         dev_class[3];
+       uint8_t         link_type;
+       uint8_t         role;
+} __attribute__ ((packed));
+
+static struct vhci_device vdev;
+static struct vhci_conn *vconn[VHCI_MAX_CONN];
+
+struct btsnoop_hdr {
+       uint8_t         id[8];          /* Identification Pattern */
+       uint32_t        version;        /* Version Number = 1 */
+       uint32_t        type;           /* Datalink Type */
+} __attribute__ ((packed));
+#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
+
+struct btsnoop_pkt {
+       uint32_t        size;           /* Original Length */
+       uint32_t        len;            /* Included Length */
+       uint32_t        flags;          /* Packet Flags */
+       uint32_t        drops;          /* Cumulative Drops */
+       uint64_t        ts;             /* Timestamp microseconds */
+       uint8_t         data[0];        /* Packet Data */
+} __attribute__ ((packed));
+#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
+
+static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
+
+#define MAX_EPOLL_EVENTS 10
+
+static int epoll_fd;
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_term(int sig)
+{
+       __io_canceled = 1;
+}
+
+static inline int read_n(int fd, void *buf, int len)
+{
+       register int w, t = 0;
+
+       while (!__io_canceled && len > 0) {
+               if ((w = read(fd, buf, len)) < 0 ){
+                       if( errno == EINTR || errno == EAGAIN )
+                               continue;
+                       return -1;
+               }
+               if (!w)
+                       return 0;
+               len -= w; buf += w; t += w;
+       }
+       return t;
+}
+
+/* Write exactly len bytes (Signal safe)*/
+static inline int write_n(int fd, void *buf, int len)
+{
+       register int w, t = 0;
+
+       while (!__io_canceled && len > 0) {
+               if ((w = write(fd, buf, len)) < 0 ){
+                       if( errno == EINTR || errno == EAGAIN )
+                               continue;
+                       return -1;
+               }
+               if (!w)
+                       return 0;
+               len -= w; buf += w; t += w;
+       }
+       return t;
+}
+
+static int create_snoop(char *file)
+{
+       struct btsnoop_hdr hdr;
+       int fd, len;
+
+       fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+       if (fd < 0)
+               return fd;
+
+       memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+       hdr.version = htonl(1);
+       hdr.type = htonl(1002);
+
+       len = write(fd, &hdr, BTSNOOP_HDR_SIZE);
+       if (len < 0) {
+               close(fd);
+               return -EIO;
+       }
+
+       if (len != BTSNOOP_HDR_SIZE) {
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+static int write_snoop(int fd, int type, int incoming,
+                               unsigned char *buf, int len)
+{
+       struct btsnoop_pkt pkt;
+       struct timeval tv;
+       uint32_t size = len;
+       uint64_t ts;
+
+       if (fd < 0)
+               return -1;
+
+       memset(&tv, 0, sizeof(tv));
+       gettimeofday(&tv, NULL);
+       ts = (tv.tv_sec - 946684800ll) * 1000000ll + tv.tv_usec;
+
+       pkt.size = htonl(size);
+       pkt.len  = pkt.size;
+       pkt.flags = ntohl(incoming & 0x01);
+       pkt.drops = htonl(0);
+       pkt.ts = hton64(ts + 0x00E03AB44A676000ll);
+
+       if (type == HCI_COMMAND_PKT || type == HCI_EVENT_PKT)
+               pkt.flags |= ntohl(0x02);
+
+       if (write(fd, &pkt, BTSNOOP_PKT_SIZE) < 0)
+               return -errno;
+
+       if (write(fd, buf, size) < 0)
+               return -errno;
+
+       return 0;
+}
+
+static struct vhci_conn *conn_get_by_bdaddr(bdaddr_t *ba)
+{
+       register int i;
+
+       for (i = 0; i < VHCI_MAX_CONN; i++)
+               if (!bacmp(&vconn[i]->dest, ba))
+                       return vconn[i];
+
+       return NULL;
+}
+
+static void reset_vdev(void)
+{
+       /* Device settings */
+       vdev.features[0] = 0xff;
+       vdev.features[1] = 0xff;
+       vdev.features[2] = 0x8f;
+       vdev.features[3] = 0xfe;
+       vdev.features[4] = 0x9b;
+       vdev.features[5] = 0xf9;
+       vdev.features[6] = 0x00;
+       vdev.features[7] = 0x80;
+
+       vdev.features[4] |= 0x40;       /* LE Supported */
+       vdev.features[6] |= 0x01;       /* Extended Inquiry Response */
+       vdev.features[6] |= 0x02;       /* BR/EDR and LE */
+       vdev.features[6] |= 0x08;       /* Secure Simple Pairing */
+
+       memset(vdev.name, 0, sizeof(vdev.name));
+       strncpy((char *) vdev.name, "BlueZ (Virtual HCI)",
+                                                       sizeof(vdev.name) - 1);
+
+       vdev.dev_class[0] = 0x00;
+       vdev.dev_class[1] = 0x00;
+       vdev.dev_class[2] = 0x00;
+
+       vdev.scan_enable = 0x00;
+       vdev.ssp_mode = 0x00;
+       vdev.inq_mode = 0x00;
+       vdev.eir_fec = 0x00;
+       memset(vdev.eir_data, 0, sizeof(vdev.eir_data));
+       vdev.le_mode = 0x00;
+       vdev.le_simul = 0x00;
+}
+
+static void command_status(uint16_t ogf, uint16_t ocf, uint8_t status)
+{
+       uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+       evt_cmd_status *cs;
+       hci_event_hdr *he;
+
+       /* Packet type */
+       *ptr++ = HCI_EVENT_PKT;
+
+       /* Event header */
+       he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+       he->evt  = EVT_CMD_STATUS;
+       he->plen = EVT_CMD_STATUS_SIZE;
+
+       cs = (void *) ptr; ptr += EVT_CMD_STATUS_SIZE;
+
+       cs->status = status;
+       cs->ncmd   = 1;
+       cs->opcode = htobs(cmd_opcode_pack(ogf, ocf));
+
+       write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+       if (write(vdev.dev_fd, buf, ptr - buf) < 0)
+               syslog(LOG_ERR, "Can't send event: %s(%d)",
+                                               strerror(errno), errno);
+}
+
+static void command_complete(uint16_t ogf, uint16_t ocf, int plen, void *data)
+{
+       uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+       evt_cmd_complete *cc;
+       hci_event_hdr *he;
+
+       /* Packet type */
+       *ptr++ = HCI_EVENT_PKT;
+
+       /* Event header */
+       he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+       he->evt  = EVT_CMD_COMPLETE;
+       he->plen = EVT_CMD_COMPLETE_SIZE + plen;
+
+       cc = (void *) ptr; ptr += EVT_CMD_COMPLETE_SIZE;
+
+       cc->ncmd = 1;
+       cc->opcode = htobs(cmd_opcode_pack(ogf, ocf));
+
+       if (plen) {
+               memcpy(ptr, data, plen);
+               ptr += plen;
+       }
+
+       write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+       if (write(vdev.dev_fd, buf, ptr - buf) < 0)
+               syslog(LOG_ERR, "Can't send event: %s(%d)",
+                                               strerror(errno), errno);
+}
+
+static void connect_request(struct vhci_conn *conn)
+{
+       uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+       evt_conn_request *cr;
+       hci_event_hdr *he;
+
+       /* Packet type */
+       *ptr++ = HCI_EVENT_PKT;
+
+       /* Event header */
+       he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+       he->evt  = EVT_CONN_REQUEST;
+       he->plen = EVT_CONN_REQUEST_SIZE;
+
+       cr = (void *) ptr; ptr += EVT_CONN_REQUEST_SIZE;
+
+       bacpy(&cr->bdaddr, &conn->dest);
+       memset(&cr->dev_class, 0, sizeof(cr->dev_class));
+       cr->link_type = ACL_LINK;
+
+       write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+       if (write(vdev.dev_fd, buf, ptr - buf) < 0)
+               syslog(LOG_ERR, "Can't send event: %s (%d)",
+                                               strerror(errno), errno);
+}
+
+static void connect_complete(struct vhci_conn *conn)
+{
+       uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+       evt_conn_complete *cc;
+       hci_event_hdr *he;
+
+       /* Packet type */
+       *ptr++ = HCI_EVENT_PKT;
+
+       /* Event header */
+       he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+       he->evt  = EVT_CONN_COMPLETE;
+       he->plen = EVT_CONN_COMPLETE_SIZE;
+
+       cc = (void *) ptr; ptr += EVT_CONN_COMPLETE_SIZE;
+
+       bacpy(&cc->bdaddr, &conn->dest);
+       cc->status = 0x00;
+       cc->handle = htobs(conn->handle);
+       cc->link_type = ACL_LINK;
+       cc->encr_mode = 0x00;
+
+       write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+       if (write(vdev.dev_fd, buf, ptr - buf) < 0)
+               syslog(LOG_ERR, "Can't send event: %s (%d)",
+                                               strerror(errno), errno);
+
+       /* TODO: Add io_acl_data() handling */
+}
+
+static void disconn_complete(struct vhci_conn *conn)
+{
+       uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+       evt_disconn_complete *dc;
+       hci_event_hdr *he;
+
+       /* Packet type */
+       *ptr++ = HCI_EVENT_PKT;
+
+       /* Event header */
+       he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+       he->evt  = EVT_DISCONN_COMPLETE;
+       he->plen = EVT_DISCONN_COMPLETE_SIZE;
+
+       dc = (void *) ptr; ptr += EVT_DISCONN_COMPLETE_SIZE;
+
+       dc->status = 0x00;
+       dc->handle = htobs(conn->handle);
+       dc->reason = 0x00;
+
+       write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+       if (write(vdev.dev_fd, buf, ptr - buf) < 0)
+               syslog(LOG_ERR, "Can't send event: %s (%d)",
+                                               strerror(errno), errno);
+
+       vdev.acl_cnt = 0;
+}
+
+static void num_completed_pkts(struct vhci_conn *conn)
+{
+       uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+       evt_num_comp_pkts *np;
+       hci_event_hdr *he;
+
+       /* Packet type */
+       *ptr++ = HCI_EVENT_PKT;
+
+       /* Event header */
+       he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+       he->evt  = EVT_NUM_COMP_PKTS;
+       he->plen = EVT_NUM_COMP_PKTS_SIZE;
+
+       np = (void *) ptr; ptr += EVT_NUM_COMP_PKTS_SIZE;
+       np->num_hndl = 1;
+
+       *((uint16_t *) ptr) = htobs(conn->handle); ptr += 2;
+       *((uint16_t *) ptr) = htobs(vdev.acl_cnt); ptr += 2;
+
+       write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+       if (write(vdev.dev_fd, buf, ptr - buf) < 0)
+               syslog(LOG_ERR, "Can't send event: %s (%d)",
+                                               strerror(errno), errno);
+}
+
+static uint8_t scan_enable(uint8_t *data)
+{
+#if 0
+       struct epoll_event scan_event;
+       struct sockaddr_in sa;
+       bdaddr_t ba;
+       int sk, opt;
+
+       if (!(*data & SCAN_PAGE)) {
+               if (vdev.scan_fd >= 0) {
+                       close(vdev.scan_fd);
+                       vdev.scan_fd = -1;
+               }
+               return 0;
+       }
+
+       if (vdev.scan_fd >= 0)
+               return 0;
+
+       if ((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               syslog(LOG_ERR, "Can't create socket: %s (%d)",
+                                               strerror(errno), errno);
+               return 1;
+       }
+
+       opt = 1;
+       setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+       baswap(&ba, &vdev.bdaddr);
+       sa.sin_family = AF_INET;
+       memcpy(&sa.sin_addr.s_addr, &ba, sizeof(sa.sin_addr.s_addr));
+       memcpy(&sa.sin_port, &ba.b[4], sizeof(sa.sin_port));
+       if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) {
+               syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+                                               strerror(errno), errno);
+               goto failed;
+       }
+
+       if (listen(sk, 10)) {
+               syslog(LOG_ERR, "Can't listen on socket: %s (%d)",
+                                               strerror(errno), errno);
+               goto failed;
+       }
+
+       memset(&scan_event, 0, sizeof(scan_event));
+       scan_event.events = EPOLLIN;
+       scan_event.data.fd = sk;
+
+       if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sk, &scan_event) < 0) {
+               syslog(LOG_ERR, "Failed to setup scan event watch");
+               goto failed;
+       }
+
+       vdev.scan_fd = sk;
+       return 0;
+
+failed:
+       close(sk);
+       return 1;
+#endif
+
+       return data[0];
+}
+
+static void accept_connection(uint8_t *data)
+{
+       accept_conn_req_cp *cp = (void *) data;
+       struct vhci_conn *conn;
+
+       if (!(conn = conn_get_by_bdaddr(&cp->bdaddr)))
+               return;
+
+       connect_complete(conn);
+}
+
+static void close_connection(struct vhci_conn *conn)
+{
+       char addr[18];
+
+       ba2str(&conn->dest, addr);
+       syslog(LOG_INFO, "Closing connection %s handle %d",
+                                       addr, conn->handle);
+
+       close(conn->fd);
+
+       vconn[conn->handle - 1] = NULL;
+       disconn_complete(conn);
+       free(conn);
+}
+
+static void disconnect(uint8_t *data)
+{
+       disconnect_cp *cp = (void *) data;
+       struct vhci_conn *conn;
+       uint16_t handle;
+
+       handle = btohs(cp->handle);
+
+       if (handle > VHCI_MAX_CONN)
+               return;
+
+       if (!(conn = vconn[handle-1]))
+               return;
+
+       close_connection(conn);
+}
+
+static void create_connection(uint8_t *data)
+{
+       create_conn_cp *cp = (void *) data;
+       struct vhci_link_info info;
+       struct vhci_conn *conn;
+       struct sockaddr_in sa;
+       int h, sk, opt;
+       bdaddr_t ba;
+
+       for (h = 0; h < VHCI_MAX_CONN; h++)
+               if (!vconn[h])
+                       goto do_connect;
+
+       syslog(LOG_ERR, "Too many connections");
+       return;
+
+do_connect:
+       if ((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               syslog(LOG_ERR, "Can't create socket: %s (%d)",
+                                               strerror(errno), errno);
+               return;
+       }
+
+       opt = 1;
+       setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+       baswap(&ba, &vdev.bdaddr);
+       sa.sin_family = AF_INET;
+       sa.sin_addr.s_addr = INADDR_ANY;        // *(uint32_t *) &ba;
+       sa.sin_port = 0;                        // *(uint16_t *) &ba.b[4];
+       if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) {
+               syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+                                               strerror(errno), errno);
+               close(sk);
+               return;
+       }
+
+       baswap(&ba, &cp->bdaddr);
+       sa.sin_family = AF_INET;
+       memcpy(&sa.sin_addr.s_addr, &ba, sizeof(sa.sin_addr.s_addr));
+       memcpy(&sa.sin_port, &ba.b[4], sizeof(sa.sin_port));
+       if (connect(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+               syslog(LOG_ERR, "Can't connect: %s (%d)",
+                                               strerror(errno), errno);
+               close(sk);
+               return;
+       }
+
+       /* Send info */
+       memset(&info, 0, sizeof(info));
+       bacpy(&info.bdaddr, &vdev.bdaddr);
+       info.link_type = ACL_LINK;
+       info.role = 1;
+       write_n(sk, (void *) &info, sizeof(info));
+
+       if (!(conn = malloc(sizeof(*conn)))) {
+               syslog(LOG_ERR, "Can't alloc new connection: %s (%d)",
+                                               strerror(errno), errno);
+               close(sk);
+               return;
+       }
+
+       memcpy((uint8_t *) &ba, (uint8_t *) &sa.sin_addr, 4);
+       memcpy((uint8_t *) &ba.b[4], (uint8_t *) &sa.sin_port, 2);
+       baswap(&conn->dest, &ba);
+
+       vconn[h] = conn;
+       conn->handle = h + 1;
+       conn->fd = sk;
+
+       connect_complete(conn);
+}
+
+static void hci_link_control(uint16_t ocf, int plen, uint8_t *data)
+{
+       const uint16_t ogf = OGF_LINK_CTL;
+
+       switch (ocf) {
+       case OCF_CREATE_CONN:
+               command_status(ogf, ocf, 0x00);
+               create_connection(data);
+               break;
+
+       case OCF_ACCEPT_CONN_REQ:
+               command_status(ogf, ocf, 0x00);
+               accept_connection(data);
+               break;
+
+       case OCF_DISCONNECT:
+               command_status(ogf, ocf, 0x00);
+               disconnect(data);
+               break;
+
+       default:
+               command_status(ogf, ocf, 0x01);
+               break;
+       }
+}
+
+static void hci_link_policy(uint16_t ocf, int plen, uint8_t *data)
+{
+       const uint16_t ogf = OGF_INFO_PARAM;
+
+       switch (ocf) {
+       default:
+               command_status(ogf, ocf, 0x01);
+               break;
+       }
+}
+
+static void hci_host_control(uint16_t ocf, int plen, uint8_t *data)
+{
+       read_scan_enable_rp se;
+       read_local_name_rp ln;
+       read_class_of_dev_rp cd;
+       read_inquiry_mode_rp im;
+       read_ext_inquiry_response_rp ir;
+       read_simple_pairing_mode_rp pm;
+       read_le_host_supported_rp hs;
+       uint8_t status;
+
+       const uint16_t ogf = OGF_HOST_CTL;
+
+       switch (ocf) {
+       case OCF_RESET:
+               status = 0x00;
+               reset_vdev();
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_SET_EVENT_FLT:
+               status = 0x00;
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_CHANGE_LOCAL_NAME:
+               status = 0x00;
+               memcpy(vdev.name, data, sizeof(vdev.name));
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_READ_LOCAL_NAME:
+               ln.status = 0x00;
+               memcpy(ln.name, vdev.name, sizeof(ln.name));
+               command_complete(ogf, ocf, sizeof(ln), &ln);
+               break;
+
+       case OCF_WRITE_CONN_ACCEPT_TIMEOUT:
+       case OCF_WRITE_PAGE_TIMEOUT:
+               status = 0x00;
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_READ_SCAN_ENABLE:
+               se.status = 0x00;
+               se.enable = vdev.scan_enable;
+               command_complete(ogf, ocf, sizeof(se), &se);
+               break;
+
+       case OCF_WRITE_SCAN_ENABLE:
+               status = 0x00;
+               vdev.scan_enable = scan_enable(data);
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_WRITE_AUTH_ENABLE:
+               status = 0x00;
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_WRITE_ENCRYPT_MODE:
+               status = 0x00;
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_READ_CLASS_OF_DEV:
+               cd.status = 0x00;
+               memcpy(cd.dev_class, vdev.dev_class, 3);
+               command_complete(ogf, ocf, sizeof(cd), &cd);
+               break;
+
+       case OCF_WRITE_CLASS_OF_DEV:
+               status = 0x00;
+               memcpy(vdev.dev_class, data, 3);
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_READ_INQUIRY_MODE:
+               im.status = 0x00;
+               im.mode = vdev.inq_mode;
+               command_complete(ogf, ocf, sizeof(im), &im);
+               break;
+
+       case OCF_WRITE_INQUIRY_MODE:
+               status = 0x00;
+               vdev.inq_mode = data[0];
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_READ_EXT_INQUIRY_RESPONSE:
+               ir.status = 0x00;
+               ir.fec = vdev.eir_fec;
+               memcpy(ir.data, vdev.eir_data, HCI_MAX_EIR_LENGTH);
+               command_complete(ogf, ocf, sizeof(ir), &ir);
+               break;
+
+       case OCF_WRITE_EXT_INQUIRY_RESPONSE:
+               status = 0x00;
+               vdev.eir_fec = data[0];
+               memcpy(vdev.eir_data, data + 1, HCI_MAX_EIR_LENGTH);
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_READ_SIMPLE_PAIRING_MODE:
+               pm.status = 0x00;
+               pm.mode = vdev.ssp_mode;
+               command_complete(ogf, ocf, sizeof(pm), &pm);
+               break;
+
+       case OCF_WRITE_SIMPLE_PAIRING_MODE:
+               status = 0x00;
+               vdev.ssp_mode = data[0];
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       case OCF_READ_LE_HOST_SUPPORTED:
+               hs.status = 0x00;
+               hs.le = vdev.le_mode;
+               hs.simul = vdev.le_simul;
+               command_complete(ogf, ocf, sizeof(hs), &hs);
+               break;
+
+       case OCF_WRITE_LE_HOST_SUPPORTED:
+               status = 0x00;
+               vdev.le_mode = data[0];
+               vdev.le_simul = data[1];
+               command_complete(ogf, ocf, 1, &status);
+               break;
+
+       default:
+               command_status(ogf, ocf, 0x01);
+               break;
+       }
+}
+
+static void hci_info_param(uint16_t ocf, int plen, uint8_t *data)
+{
+       read_local_version_rp lv;
+       read_local_features_rp lf;
+       read_local_ext_features_rp ef;
+       read_buffer_size_rp bs;
+       read_bd_addr_rp ba;
+
+       const uint16_t ogf = OGF_INFO_PARAM;
+
+       switch (ocf) {
+       case OCF_READ_LOCAL_VERSION:
+               lv.status = 0x00;
+               lv.hci_ver = 0x06;
+               lv.hci_rev = htobs(0x0000);
+               lv.lmp_ver = 0x06;
+               lv.manufacturer = htobs(63);
+               lv.lmp_subver = htobs(0x0000);
+               command_complete(ogf, ocf, sizeof(lv), &lv);
+               break;
+
+       case OCF_READ_LOCAL_FEATURES:
+               lf.status = 0x00;
+               memcpy(lf.features, vdev.features, 8);
+               command_complete(ogf, ocf, sizeof(lf), &lf);
+               break;
+
+       case OCF_READ_LOCAL_EXT_FEATURES:
+               ef.status = 0x00;
+               if (*data == 0) {
+                       ef.page_num = 0;
+                       ef.max_page_num = 1;
+                       memcpy(ef.features, vdev.features, 8);
+               } else if (*data == 1) {
+                       ef.page_num = 1;
+                       ef.max_page_num = 1;
+                       memset(ef.features, 0, 8);
+                       ef.features[0] |= (!!vdev.ssp_mode << 0);
+                       ef.features[0] |= (!!vdev.le_mode << 1);
+                       ef.features[0] |= (!!vdev.le_simul << 2);
+               } else {
+                       ef.page_num = *data;
+                       ef.max_page_num = 0;
+                       memset(ef.features, 0, 8);
+               }
+               command_complete(ogf, ocf, sizeof(ef), &ef);
+               break;
+
+       case OCF_READ_BUFFER_SIZE:
+               bs.status = 0x00;
+               bs.acl_mtu = htobs(VHCI_ACL_MTU);
+               bs.sco_mtu = 0;
+               bs.acl_max_pkt = htobs(VHCI_ACL_MAX_PKT);
+               bs.sco_max_pkt = htobs(0);
+               command_complete(ogf, ocf, sizeof(bs), &bs);
+               break;
+
+       case OCF_READ_BD_ADDR:
+               ba.status = 0x00;
+               bacpy(&ba.bdaddr, &vdev.bdaddr);
+               command_complete(ogf, ocf, sizeof(ba), &ba);
+               break;
+
+       default:
+               command_status(ogf, ocf, 0x01);
+               break;
+       }
+}
+
+static void hci_status_param(uint16_t ocf, int plen, uint8_t *data)
+{
+       read_local_amp_info_rp ai;
+
+       const uint16_t ogf = OGF_STATUS_PARAM;
+
+       switch (ocf) {
+       case OCF_READ_LOCAL_AMP_INFO:
+               memset(&ai, 0, sizeof(ai));
+
+               /* BT only */
+               ai.amp_status = 0x01;
+               ai.max_pdu_size = htobl(L2CAP_DEFAULT_MTU);
+               ai.controller_type = HCI_AMP;
+               ai.max_amp_assoc_length = htobl(HCI_MAX_ACL_SIZE);
+               /* No flushing at all */
+               ai.max_flush_timeout = 0xFFFFFFFF;
+               ai.best_effort_flush_timeout = 0xFFFFFFFF;
+
+               command_complete(ogf, ocf, sizeof(ai), &ai);
+               break;
+
+       default:
+               command_status(ogf, ocf, 0x01);
+               break;
+       }
+}
+
+static void hci_le_control(uint16_t ocf, int plen, uint8_t *data)
+{
+       le_read_buffer_size_rp bs;
+
+       const uint16_t ogf = OGF_LE_CTL;
+
+       switch (ocf) {
+       case OCF_LE_READ_BUFFER_SIZE:
+               bs.status = 0;
+               bs.pkt_len = htobs(VHCI_ACL_MTU);
+               bs.max_pkt = htobs(VHCI_ACL_MAX_PKT);
+               command_complete(ogf, ocf, sizeof(bs), &bs);
+               break;
+
+       default:
+               command_status(ogf, ocf, 0x01);
+               break;
+       }
+}
+
+static void hci_command(uint8_t *data)
+{
+       hci_command_hdr *ch;
+       uint8_t *ptr = data;
+       uint16_t ogf, ocf;
+
+       ch = (hci_command_hdr *) ptr;
+       ptr += HCI_COMMAND_HDR_SIZE;
+
+       ch->opcode = btohs(ch->opcode);
+       ogf = cmd_opcode_ogf(ch->opcode);
+       ocf = cmd_opcode_ocf(ch->opcode);
+
+       switch (ogf) {
+       case OGF_LINK_CTL:
+               hci_link_control(ocf, ch->plen, ptr);
+               break;
+
+       case OGF_LINK_POLICY:
+               hci_link_policy(ocf, ch->plen, ptr);
+               break;
+
+       case OGF_HOST_CTL:
+               hci_host_control(ocf, ch->plen, ptr);
+               break;
+
+       case OGF_INFO_PARAM:
+               hci_info_param(ocf, ch->plen, ptr);
+               break;
+
+       case OGF_STATUS_PARAM:
+               hci_status_param(ocf, ch->plen, ptr);
+               break;
+
+       case OGF_LE_CTL:
+               hci_le_control(ocf, ch->plen, ptr);
+               break;
+
+       default:
+               command_status(ogf, ocf, 0x01);
+               break;
+       }
+}
+
+static void hci_acl_data(uint8_t *data)
+{
+       hci_acl_hdr *ah = (void *) data;
+       struct vhci_conn *conn;
+       uint16_t handle;
+
+       handle = acl_handle(btohs(ah->handle));
+
+       if (handle > VHCI_MAX_CONN || !(conn = vconn[handle - 1])) {
+               syslog(LOG_ERR, "Bad connection handle %d", handle);
+               return;
+       }
+
+       if (write_n(conn->fd, data, btohs(ah->dlen) + HCI_ACL_HDR_SIZE) < 0) {
+               close_connection(conn);
+               return;
+       }
+
+       if (++vdev.acl_cnt > VHCI_ACL_MAX_PKT - 1) {
+               /* Send num of complete packets event */
+               num_completed_pkts(conn);
+               vdev.acl_cnt = 0;
+       }
+}
+
+#if 0
+static void io_acl_data(void *data)
+{
+       struct vhci_conn *conn = data;
+       unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
+       hci_acl_hdr *ah;
+       uint16_t flags;
+       int len;
+
+       ptr = buf + 1;
+       if (read_n(conn->fd, ptr, HCI_ACL_HDR_SIZE) <= 0) {
+               close_connection(conn);
+               return;
+       }
+
+       ah = (void *) ptr;
+       ptr += HCI_ACL_HDR_SIZE;
+
+       len = btohs(ah->dlen);
+       if (read_n(conn->fd, ptr, len) <= 0) {
+               close_connection(conn);
+               return;
+       }
+
+       buf[0] = HCI_ACLDATA_PKT;
+
+       flags = acl_flags(btohs(ah->handle));
+       ah->handle = htobs(acl_handle_pack(conn->handle, flags));
+       len += HCI_ACL_HDR_SIZE + 1;
+
+       write_snoop(vdev.dd, HCI_ACLDATA_PKT, 1, buf, len);
+
+       if (write(vdev.dev_fd, buf, len) < 0)
+               syslog(LOG_ERR, "ACL data write error");
+}
+#endif
+
+static void io_conn_ind(void)
+{
+       struct vhci_link_info info;
+       struct vhci_conn *conn;
+       struct sockaddr_in sa;
+       socklen_t len;
+       int nsk, h;
+
+       len = sizeof(sa);
+       if ((nsk = accept(vdev.scan_fd, (struct sockaddr *) &sa, &len)) < 0)
+               return;
+
+       if (read_n(nsk, &info, sizeof(info)) < 0) {
+               syslog(LOG_ERR, "Can't read link info");
+               return;
+       }
+
+       if (!(conn = malloc(sizeof(*conn)))) {
+               syslog(LOG_ERR, "Can't alloc new connection");
+               close(nsk);
+               return;
+       }
+
+       bacpy(&conn->dest, &info.bdaddr);
+
+       for (h = 0; h < VHCI_MAX_CONN; h++)
+               if (!vconn[h])
+                       goto accepted;
+
+       syslog(LOG_ERR, "Too many connections");
+       free(conn);
+       close(nsk);
+       return;
+
+accepted:
+       vconn[h] = conn;
+       conn->handle = h + 1;
+       conn->fd = nsk;
+       connect_request(conn);
+}
+
+static void io_hci_data(void)
+{
+       unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
+       int type;
+       ssize_t len;
+
+       ptr = buf;
+
+       len = read(vdev.dev_fd, buf, sizeof(buf));
+       if (len < 0) {
+               if (errno == EAGAIN)
+                       return;
+
+               syslog(LOG_ERR, "Read failed: %s (%d)", strerror(errno), errno);
+               __io_canceled = 1;
+               return;
+       }
+
+       type = *ptr++;
+
+       write_snoop(vdev.dd, type, 0, buf, len);
+
+       switch (type) {
+       case HCI_COMMAND_PKT:
+               hci_command(ptr);
+               break;
+
+       case HCI_ACLDATA_PKT:
+               hci_acl_data(ptr);
+               break;
+
+       default:
+               syslog(LOG_ERR, "Unknown packet type 0x%2.2x", type);
+               break;
+       }
+}
+
+static int getbdaddrbyname(char *str, bdaddr_t *ba)
+{
+       int i, n, len;
+
+       len = strlen(str);
+
+       /* Check address format */
+       for (i = 0, n = 0; i < len; i++)
+               if (str[i] == ':')
+                       n++;
+
+       if (n == 5) {
+               /* BD address */
+               str2ba(str, ba);
+               return 0;
+       }
+
+       if (n == 0) {
+               /* loopback port */
+               in_addr_t addr = INADDR_LOOPBACK;
+               uint16_t be16 = htons(atoi(str));
+               bdaddr_t b;
+
+               memcpy(&b, &addr, 4);
+               memcpy(&b.b[4], &be16, sizeof(be16));
+               baswap(ba, &b);
+
+               return 0;
+       }
+
+       fprintf(stderr, "Invalid address format\n");
+
+       return -1;
+}
+
+static void usage(void)
+{
+       printf("hciemu - HCI emulator ver %s\n", VERSION);
+       printf("Usage: \n");
+       printf("\thciemu [options] port_number\n"
+               "Options:\n"
+               "\t[-d device] use specified device node\n"
+               "\t[-s file] create snoop file\n"
+               "\t[-n] do not detach\n"
+               "\t[-h] help, you are looking at it\n");
+}
+
+static const struct option options[] = {
+       { "device",     1, 0, 'd' },
+       { "bdaddr",     1, 0, 'b' },
+       { "snoop",      1, 0, 's' },
+       { "nodetach",   0, 0, 'n' },
+       { "help",       0, 0, 'h' },
+       { }
+};
+
+int main(int argc, char *argv[])
+{
+       int exitcode = EXIT_FAILURE;
+       struct sigaction sa;
+       char *device = NULL, *snoop = NULL;
+       int device_fd;
+       struct epoll_event device_event;
+       int dd, opt, detach = 1;
+
+       while ((opt=getopt_long(argc, argv, "d:s:nh", options, NULL)) != EOF) {
+               switch(opt) {
+               case 'd':
+                       device = strdup(optarg);
+                       break;
+               case 's':
+                       snoop = strdup(optarg);
+                       break;
+               case 'n':
+                       detach = 0;
+                       break;
+               case 'h':
+                       usage();
+                       exit(0);
+               default:
+                       usage();
+                       exit(1);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       if (getbdaddrbyname(argv[0], &vdev.bdaddr) < 0)
+               exit(1);
+
+       if (detach) {
+               if (daemon(0, 0)) {
+                       perror("Can't start daemon");
+                       exit(1);
+               }
+       }
+
+       /* Start logging to syslog and stderr */
+       openlog("hciemu", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+       syslog(LOG_INFO, "HCI emulation daemon ver %s started", VERSION);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       if (!device)
+               device = strdup(VHCI_DEV);
+
+       /* Open and create virtual HCI device */
+       device_fd = open(device, O_RDWR);
+       if (device_fd < 0) {
+               syslog(LOG_ERR, "Can't open device %s: %s (%d)",
+                                       device, strerror(errno), errno);
+               free(device);
+               return exitcode;
+       }
+
+       free(device);
+
+       /* Create snoop file */
+       if (snoop) {
+               dd = create_snoop(snoop);
+               if (dd < 0)
+                       syslog(LOG_ERR, "Can't create snoop file %s: %s (%d)",
+                                               snoop, strerror(errno), errno);
+               free(snoop);
+       } else
+               dd = -1;
+
+       /* Create event loop */
+       epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+       if (epoll_fd < 0) {
+               perror("Failed to create epoll descriptor");
+               goto close_device;
+       }
+
+       reset_vdev();
+
+       vdev.dev_fd = device_fd;
+       vdev.dd = dd;
+
+       memset(&device_event, 0, sizeof(device_event));
+       device_event.events = EPOLLIN;
+       device_event.data.fd = device_fd;
+
+       if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, device_fd, &device_event) < 0) {
+               perror("Failed to setup device event watch");
+               goto close_device;
+       }
+
+       setpriority(PRIO_PROCESS, 0, -19);
+
+       /* Start event processor */
+       for (;;) {
+               struct epoll_event events[MAX_EPOLL_EVENTS];
+               int n, nfds;
+
+               if (__io_canceled)
+                       break;
+
+               nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, -1);
+               if (nfds < 0)
+                       continue;
+
+               for (n = 0; n < nfds; n++) {
+                       if (events[n].data.fd == vdev.dev_fd)
+                               io_hci_data();
+                       else if (events[n].data.fd == vdev.scan_fd)
+                               io_conn_ind();
+               }
+       }
+
+       exitcode = EXIT_SUCCESS;
+
+       epoll_ctl(epoll_fd, EPOLL_CTL_DEL, device_fd, NULL);
+
+close_device:
+       close(device_fd);
+
+       if (dd >= 0)
+               close(dd);
+
+       close(epoll_fd);
+
+       syslog(LOG_INFO, "Exit");
+
+       return exitcode;
+}
diff --git a/test/hsmicro b/test/hsmicro
new file mode 100755 (executable)
index 0000000..c254226
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+SOX=`which sox`
+HSTEST=`which hstest`
+
+if [ -z "$HSTEST" ]
+then
+       HSTEST="./hstest"
+fi
+
+if [ -z "$1" ]
+then
+       echo -e "Usage:\n\thsmicro <bdaddr> [channel]"
+       exit
+fi
+
+BDADDR=$1
+CHANNEL=$2
+
+$HSTEST record - $BDADDR $CHANNEL | $SOX -t raw -r 8000 -c 1 -s -w - -t ossdsp -r 44100 -c 2 -s -w /dev/dsp polyphase vol 5.0 2> /dev/null
diff --git a/test/hsplay b/test/hsplay
new file mode 100755 (executable)
index 0000000..8cecbff
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+MPG123=`which mpg123`
+SOX=`which sox`
+HSTEST=`which hstest`
+
+if [ -z "$HSTEST" ]
+then
+       HSTEST="./hstest"
+fi
+
+if [ -z "$1" ] || [ -z "$2" ]
+then
+       echo -e "Usage:\n\thsplay <file> <bdaddr> [channel]"
+       exit
+fi
+
+FILE=$1
+BDADDR=$2
+CHANNEL=$3
+
+$MPG123 -q -s "$FILE" | $SOX -t raw -r 44100 -c 2 -s -w - -t raw -r 8000 -c 1 -s -w - | $HSTEST play - $BDADDR $CHANNEL
diff --git a/test/hstest.c b/test/hstest.c
new file mode 100644 (file)
index 0000000..ac68059
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/rfcomm.h>
+
+static volatile int terminate = 0;
+
+static void sig_term(int sig) {
+       terminate = 1;
+}
+
+static int rfcomm_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t channel)
+{
+       struct sockaddr_rc addr;
+       int s;
+
+       if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, src);
+       addr.rc_channel = 0;
+       if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+               close(s);
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, dst);
+       addr.rc_channel = channel;
+       if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 ){
+               close(s);
+               return -1;
+       }
+
+       return s;
+}
+
+static int sco_connect(bdaddr_t *src, bdaddr_t *dst, uint16_t *handle, uint16_t *mtu)
+{
+       struct sockaddr_sco addr;
+       struct sco_conninfo conn;
+       struct sco_options opts;
+       socklen_t size;
+       int s;
+
+       if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sco_family = AF_BLUETOOTH;
+       bacpy(&addr.sco_bdaddr, src);
+
+       if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+               close(s);
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sco_family = AF_BLUETOOTH;
+       bacpy(&addr.sco_bdaddr, dst);
+
+       if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 ){
+               close(s);
+               return -1;
+       }
+
+       memset(&conn, 0, sizeof(conn));
+       size = sizeof(conn);
+
+       if (getsockopt(s, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) {
+               close(s);
+               return -1;
+       }
+
+       memset(&opts, 0, sizeof(opts));
+       size = sizeof(opts);
+
+       if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) {
+               close(s);
+               return -1;
+       }
+
+       if (handle)
+               *handle = conn.hci_handle;
+
+       if (mtu)
+               *mtu = opts.mtu;
+
+       return s;
+}
+
+static void usage(void)
+{
+       printf("Usage:\n"
+               "\thstest play   <file> <bdaddr> [channel]\n"
+               "\thstest record <file> <bdaddr> [channel]\n");
+}
+
+#define PLAY   1
+#define RECORD 2
+
+int main(int argc, char *argv[])
+{
+       struct sigaction sa;
+
+       fd_set rfds;
+       struct timeval timeout;
+       unsigned char buf[2048], *p;
+       int maxfd, sel, rlen, wlen;
+
+       bdaddr_t local;
+       bdaddr_t bdaddr;
+       uint8_t channel;
+
+       char *filename;
+       mode_t filemode;
+       int mode = 0;
+       int dd, rd, sd, fd;
+       uint16_t sco_handle, sco_mtu, vs;
+
+       switch (argc) {
+       case 4:
+               str2ba(argv[3], &bdaddr);
+               channel = 6;
+               break;
+       case 5:
+               str2ba(argv[3], &bdaddr);
+               channel = atoi(argv[4]);
+               break;
+       default:
+               usage();
+               exit(-1);
+       }
+
+       if (strncmp(argv[1], "play", 4) == 0) {
+               mode = PLAY;
+               filemode = O_RDONLY;
+       } else if (strncmp(argv[1], "rec", 3) == 0) {
+               mode = RECORD;
+               filemode = O_WRONLY | O_CREAT | O_TRUNC;
+       } else {
+               usage();
+               exit(-1);
+       }
+
+       filename = argv[2];
+
+       hci_devba(0, &local);
+       dd = hci_open_dev(0);
+       hci_read_voice_setting(dd, &vs, 1000);
+       vs = htobs(vs);
+       fprintf(stderr, "Voice setting: 0x%04x\n", vs);
+       close(dd);
+       if (vs != 0x0060) {
+               fprintf(stderr, "The voice setting must be 0x0060\n");
+               return -1;
+       }
+
+       if (strcmp(filename, "-") == 0) {
+               switch (mode) {
+               case PLAY:
+                       fd = 0;
+                       break;
+               case RECORD:
+                       fd = 1;
+                       break;
+               default:
+                       return -1;
+               }
+       } else {
+               if ((fd = open(filename, filemode)) < 0) {
+                       perror("Can't open input/output file");
+                       return -1;
+               }
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags = SA_NOCLDSTOP;
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       if ((rd = rfcomm_connect(&local, &bdaddr, channel)) < 0) {
+               perror("Can't connect RFCOMM channel");
+               return -1;
+       }
+
+       fprintf(stderr, "RFCOMM channel connected\n");
+
+       if ((sd = sco_connect(&local, &bdaddr, &sco_handle, &sco_mtu)) < 0) {
+               perror("Can't connect SCO audio channel");
+               close(rd);
+               return -1;
+       }
+
+       fprintf(stderr, "SCO audio channel connected (handle %d, mtu %d)\n", sco_handle, sco_mtu);
+
+       if (mode == RECORD) {
+               if (write(rd, "RING\r\n", 6) < 0)
+                       return -errno;
+       }
+
+       maxfd = (rd > sd) ? rd : sd;
+
+       while (!terminate) {
+
+               FD_ZERO(&rfds);
+               FD_SET(rd, &rfds);
+               FD_SET(sd, &rfds);
+
+               timeout.tv_sec = 0;
+               timeout.tv_usec = 10000;
+
+               if ((sel = select(maxfd + 1, &rfds, NULL, NULL, &timeout)) > 0) {
+
+                       if (FD_ISSET(rd, &rfds)) {
+                               memset(buf, 0, sizeof(buf));
+                               rlen = read(rd, buf, sizeof(buf));
+                               if (rlen > 0) {
+                                       fprintf(stderr, "%s\n", buf);
+                                       wlen = write(rd, "OK\r\n", 4);
+                               }
+                       }
+
+                       if (FD_ISSET(sd, &rfds)) {
+                               memset(buf, 0, sizeof(buf));
+                               rlen = read(sd, buf, sizeof(buf));
+                               if (rlen > 0)
+                                       switch (mode) {
+                                       case PLAY:
+                                               rlen = read(fd, buf, rlen);
+
+                                               wlen = 0;
+                                               p = buf;
+                                               while (rlen > sco_mtu) {
+                                                       wlen += write(sd, p, sco_mtu);
+                                                       rlen -= sco_mtu;
+                                                       p += sco_mtu;
+                                               }
+                                               wlen += write(sd, p, rlen);
+                                               break;
+                                       case RECORD:
+                                               wlen = write(fd, buf, rlen);
+                                               break;
+                                       default:
+                                               break;
+                                       }
+                       }
+
+               }
+
+       }
+
+       close(sd);
+       sleep(5);
+       close(rd);
+
+       close(fd);
+
+       return 0;
+}
diff --git a/test/ipctest.c b/test/ipctest.c
new file mode 100644 (file)
index 0000000..cbfd78d
--- /dev/null
@@ -0,0 +1,1133 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2009 Lennart Poettering
+ *  Copyright (C) 2008 Joao Paulo Rechi Vita
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <glib.h>
+
+#include "ipc.h"
+#include "sbc.h"
+
+#define DBG(fmt, arg...)                               \
+       printf("debug %s: " fmt "\n" , __FUNCTION__ , ## arg)
+#define ERR(fmt, arg...)                               \
+       fprintf(stderr, "ERROR %s: " fmt "\n" , __FUNCTION__ , ## arg)
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#ifndef MIN
+# define MIN(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifndef TRUE
+# define TRUE (1)
+#endif
+
+#ifndef FALSE
+# define FALSE (0)
+#endif
+
+#define YES_NO(t) ((t) ? "yes" : "no")
+
+#define BUFFER_SIZE 2048
+#define MAX_BITPOOL 64
+#define MIN_BITPOOL 2
+
+struct a2dp_info {
+       sbc_capabilities_t sbc_capabilities;
+       sbc_t sbc; /* Codec data */
+       int sbc_initialized; /* Keep track if the encoder is initialized */
+       size_t codesize; /* SBC codesize */
+
+       void* buffer; /* Codec transfer buffer */
+       size_t buffer_size; /* Size of the buffer */
+
+       uint16_t seq_num; /* Cumulative packet sequence */
+};
+
+struct hsp_info {
+       pcm_capabilities_t pcm_capabilities;
+};
+
+struct userdata {
+       int service_fd;
+       int stream_fd;
+       GIOChannel *stream_channel;
+       guint stream_watch;
+       GIOChannel *gin; /* dude, I am thirsty now */
+       guint gin_watch;
+       int transport;
+       uint32_t rate;
+       int channels;
+       char *address;
+       struct a2dp_info a2dp;
+       struct hsp_info hsp;
+       size_t link_mtu;
+       size_t block_size;
+       gboolean debug_stream_read : 1;
+       gboolean debug_stream_write : 1;
+};
+
+static struct userdata data = {
+       .service_fd = -1,
+       .stream_fd = -1,
+       .transport = BT_CAPABILITIES_TRANSPORT_A2DP,
+       .rate = 48000,
+       .channels = 2,
+       .address = NULL
+};
+
+static int start_stream(struct userdata *u);
+static int stop_stream(struct userdata *u);
+static gboolean input_cb(GIOChannel *gin, GIOCondition condition, gpointer data);
+
+static GMainLoop *main_loop;
+
+static int service_send(struct userdata *u, const bt_audio_msg_header_t *msg)
+{
+       int err;
+       uint16_t length;
+
+       assert(u);
+
+       length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
+
+       DBG("sending %s:%s", bt_audio_strtype(msg->type),
+               bt_audio_strname(msg->name));
+
+       if (send(u->service_fd, msg, length, 0) > 0)
+               err = 0;
+       else {
+               err = -errno;
+               ERR("Error sending data to audio service: %s(%d)",
+                       strerror(-err), -err);
+       }
+
+       return err;
+}
+
+static int service_recv(struct userdata *u, bt_audio_msg_header_t *rsp)
+{
+       int err;
+       const char *type, *name;
+       uint16_t length;
+
+       assert(u);
+
+       length = rsp->length ? : BT_SUGGESTED_BUFFER_SIZE;
+
+       DBG("trying to receive msg from audio service...");
+       if (recv(u->service_fd, rsp, length, 0) > 0) {
+               type = bt_audio_strtype(rsp->type);
+               name = bt_audio_strname(rsp->name);
+               if (type && name) {
+                       DBG("Received %s - %s", type, name);
+                       err = 0;
+               } else {
+                       err = -EINVAL;
+                       ERR("Bogus message type %d - name %d"
+                               "received from audio service",
+                               rsp->type, rsp->name);
+               }
+       } else {
+               err = -errno;
+               ERR("Error receiving data from audio service: %s(%d)",
+                       strerror(-err), -err);
+       }
+
+       return err;
+}
+
+static ssize_t service_expect(struct userdata *u, bt_audio_msg_header_t *rsp,
+                               uint8_t expected_name)
+{
+       int r;
+
+       assert(u);
+       assert(u->service_fd >= 0);
+       assert(rsp);
+
+       if ((r = service_recv(u, rsp)) < 0)
+               return r;
+
+       if ((rsp->type != BT_INDICATION && rsp->type != BT_RESPONSE) ||
+                       (rsp->name != expected_name)) {
+               if (rsp->type == BT_ERROR && rsp->length == sizeof(bt_audio_error_t))
+                       ERR("Received error condition: %s",
+                               strerror(((bt_audio_error_t*) rsp)->posix_errno));
+               else
+                       ERR("Bogus message %s received while %s was expected",
+                               bt_audio_strname(rsp->name),
+                               bt_audio_strname(expected_name));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int init_bt(struct userdata *u)
+{
+       assert(u);
+
+       if (u->service_fd != -1)
+               return 0;
+
+       DBG("bt_audio_service_open");
+
+       u->service_fd = bt_audio_service_open();
+       if (u->service_fd < 0) {
+               int err = -errno;
+
+               ERR("bt_audio_service_open() failed: %s (%d)", strerror(-err),
+                                                                       -err);
+
+               return err;
+       }
+
+       return 0;
+}
+
+static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *rsp)
+{
+       unsigned char *ptr;
+       uint16_t bytes_left;
+       codec_capabilities_t codec;
+
+       assert(u);
+       assert(rsp);
+
+       bytes_left = rsp->h.length - sizeof(*rsp);
+
+       if (bytes_left < sizeof(codec_capabilities_t)) {
+               ERR("Packet too small to store codec information.");
+               return -1;
+       }
+
+       ptr = ((void *) rsp) + sizeof(*rsp);
+
+       memcpy(&codec, ptr, sizeof(codec)); /** ALIGNMENT? **/
+
+       DBG("Payload size is %lu %lu",
+               (unsigned long) bytes_left, (unsigned long) sizeof(codec));
+
+       if (u->transport != codec.transport) {
+               ERR("Got capabilities for wrong codec.");
+               return -1;
+       }
+
+       if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO) {
+
+               if (bytes_left <= 0 ||
+                               codec.length != sizeof(u->hsp.pcm_capabilities))
+                       return -1;
+
+               assert(codec.type == BT_HFP_CODEC_PCM);
+
+               memcpy(&u->hsp.pcm_capabilities,
+                               &codec, sizeof(u->hsp.pcm_capabilities));
+
+               DBG("Has NREC: %s",
+                       YES_NO(u->hsp.pcm_capabilities.flags & BT_PCM_FLAG_NREC));
+
+       } else if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+
+               while (bytes_left > 0) {
+                       if (codec.type == BT_A2DP_SBC_SINK &&
+                                       !(codec.lock & BT_WRITE_LOCK))
+                               break;
+
+                       bytes_left -= codec.length;
+                       ptr += codec.length;
+                       memcpy(&codec, ptr, sizeof(codec));
+               }
+
+               DBG("bytes_left = %d, codec.length = %d",
+                                               bytes_left, codec.length);
+
+               if (bytes_left <= 0 ||
+                               codec.length != sizeof(u->a2dp.sbc_capabilities))
+                       return -1;
+
+               assert(codec.type == BT_A2DP_SBC_SINK);
+
+               memcpy(&u->a2dp.sbc_capabilities, &codec,
+                                       sizeof(u->a2dp.sbc_capabilities));
+       } else {
+               assert(0);
+       }
+
+       return 0;
+}
+
+static int get_caps(struct userdata *u)
+{
+       union {
+               struct bt_get_capabilities_req getcaps_req;
+               struct bt_get_capabilities_rsp getcaps_rsp;
+               bt_audio_error_t error;
+               uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
+       } msg;
+
+       assert(u);
+
+       memset(&msg, 0, sizeof(msg));
+       msg.getcaps_req.h.type = BT_REQUEST;
+       msg.getcaps_req.h.name = BT_GET_CAPABILITIES;
+       msg.getcaps_req.h.length = sizeof(msg.getcaps_req);
+
+       strncpy(msg.getcaps_req.destination, u->address,
+                       sizeof(msg.getcaps_req.destination));
+       msg.getcaps_req.transport = u->transport;
+       msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT;
+
+       if (service_send(u, &msg.getcaps_req.h) < 0)
+               return -1;
+
+       msg.getcaps_rsp.h.length = 0;
+       if (service_expect(u, &msg.getcaps_rsp.h, BT_GET_CAPABILITIES) < 0)
+               return -1;
+
+       return parse_caps(u, &msg.getcaps_rsp);
+}
+
+static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode)
+{
+       switch (freq) {
+       case BT_SBC_SAMPLING_FREQ_16000:
+       case BT_SBC_SAMPLING_FREQ_32000:
+               return 53;
+
+       case BT_SBC_SAMPLING_FREQ_44100:
+
+               switch (mode) {
+               case BT_A2DP_CHANNEL_MODE_MONO:
+               case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+                       return 31;
+
+               case BT_A2DP_CHANNEL_MODE_STEREO:
+               case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+                       return 53;
+
+               default:
+                       DBG("Invalid channel mode %u", mode);
+                       return 53;
+               }
+
+       case BT_SBC_SAMPLING_FREQ_48000:
+
+               switch (mode) {
+               case BT_A2DP_CHANNEL_MODE_MONO:
+               case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+                       return 29;
+
+               case BT_A2DP_CHANNEL_MODE_STEREO:
+               case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+                       return 51;
+
+               default:
+                       DBG("Invalid channel mode %u", mode);
+                       return 51;
+               }
+
+       default:
+               DBG("Invalid sampling freq %u", freq);
+               return 53;
+       }
+}
+
+static int setup_a2dp(struct userdata *u)
+{
+       sbc_capabilities_t *cap;
+       int i;
+
+       static const struct {
+               uint32_t rate;
+               uint8_t cap;
+       } freq_table[] = {
+               { 16000U, BT_SBC_SAMPLING_FREQ_16000 },
+               { 32000U, BT_SBC_SAMPLING_FREQ_32000 },
+               { 44100U, BT_SBC_SAMPLING_FREQ_44100 },
+               { 48000U, BT_SBC_SAMPLING_FREQ_48000 }
+       };
+
+       assert(u);
+       assert(u->transport == BT_CAPABILITIES_TRANSPORT_A2DP);
+
+       cap = &u->a2dp.sbc_capabilities;
+
+       /* Find the lowest freq that is at least as high as the requested
+        * sampling rate */
+       for (i = 0; (unsigned) i < ARRAY_SIZE(freq_table); i++)
+               if (freq_table[i].rate >= u->rate &&
+                       (cap->frequency & freq_table[i].cap)) {
+                       u->rate = freq_table[i].rate;
+                       cap->frequency = freq_table[i].cap;
+                       break;
+               }
+
+       if ((unsigned) i >= ARRAY_SIZE(freq_table)) {
+               for (; i >= 0; i--) {
+                       if (cap->frequency & freq_table[i].cap) {
+                               u->rate = freq_table[i].rate;
+                               cap->frequency = freq_table[i].cap;
+                               break;
+                       }
+               }
+
+               if (i < 0) {
+                       DBG("Not suitable sample rate");
+                       return -1;
+               }
+       }
+
+       if (u->channels <= 1) {
+               if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+                       cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+                       u->channels = 1;
+               } else
+                       u->channels = 2;
+       }
+
+       if (u->channels >= 2) {
+               u->channels = 2;
+
+               if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
+                       cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
+               else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
+                       cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
+               else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
+                       cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
+               else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+                       cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+                       u->channels = 1;
+               } else {
+                       DBG("No supported channel modes");
+                       return -1;
+               }
+       }
+
+       if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
+               cap->block_length = BT_A2DP_BLOCK_LENGTH_16;
+       else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)
+               cap->block_length = BT_A2DP_BLOCK_LENGTH_12;
+       else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8)
+               cap->block_length = BT_A2DP_BLOCK_LENGTH_8;
+       else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4)
+               cap->block_length = BT_A2DP_BLOCK_LENGTH_4;
+       else {
+               DBG("No supported block lengths");
+               return -1;
+       }
+
+       if (cap->subbands & BT_A2DP_SUBBANDS_8)
+               cap->subbands = BT_A2DP_SUBBANDS_8;
+       else if (cap->subbands & BT_A2DP_SUBBANDS_4)
+               cap->subbands = BT_A2DP_SUBBANDS_4;
+       else {
+               DBG("No supported subbands");
+               return -1;
+       }
+
+       if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)
+               cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
+       else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
+               cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
+
+       cap->min_bitpool = (uint8_t) MAX(MIN_BITPOOL, cap->min_bitpool);
+       cap->max_bitpool = (uint8_t) MIN(
+               a2dp_default_bitpool(cap->frequency, cap->channel_mode),
+               cap->max_bitpool);
+
+       return 0;
+}
+
+static void setup_sbc(struct a2dp_info *a2dp)
+{
+       sbc_capabilities_t *active_capabilities;
+
+       assert(a2dp);
+
+       active_capabilities = &a2dp->sbc_capabilities;
+
+       if (a2dp->sbc_initialized)
+               sbc_reinit(&a2dp->sbc, 0);
+       else
+               sbc_init(&a2dp->sbc, 0);
+       a2dp->sbc_initialized = TRUE;
+
+       switch (active_capabilities->frequency) {
+       case BT_SBC_SAMPLING_FREQ_16000:
+               a2dp->sbc.frequency = SBC_FREQ_16000;
+               break;
+       case BT_SBC_SAMPLING_FREQ_32000:
+               a2dp->sbc.frequency = SBC_FREQ_32000;
+               break;
+       case BT_SBC_SAMPLING_FREQ_44100:
+               a2dp->sbc.frequency = SBC_FREQ_44100;
+               break;
+       case BT_SBC_SAMPLING_FREQ_48000:
+               a2dp->sbc.frequency = SBC_FREQ_48000;
+               break;
+       default:
+               assert(0);
+       }
+
+       switch (active_capabilities->channel_mode) {
+       case BT_A2DP_CHANNEL_MODE_MONO:
+               a2dp->sbc.mode = SBC_MODE_MONO;
+               break;
+       case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+               a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
+               break;
+       case BT_A2DP_CHANNEL_MODE_STEREO:
+               a2dp->sbc.mode = SBC_MODE_STEREO;
+               break;
+       case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+               a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+               break;
+       default:
+               assert(0);
+       }
+
+       switch (active_capabilities->allocation_method) {
+       case BT_A2DP_ALLOCATION_SNR:
+               a2dp->sbc.allocation = SBC_AM_SNR;
+               break;
+       case BT_A2DP_ALLOCATION_LOUDNESS:
+               a2dp->sbc.allocation = SBC_AM_LOUDNESS;
+               break;
+       default:
+               assert(0);
+       }
+
+       switch (active_capabilities->subbands) {
+       case BT_A2DP_SUBBANDS_4:
+               a2dp->sbc.subbands = SBC_SB_4;
+               break;
+       case BT_A2DP_SUBBANDS_8:
+               a2dp->sbc.subbands = SBC_SB_8;
+               break;
+       default:
+               assert(0);
+       }
+
+       switch (active_capabilities->block_length) {
+       case BT_A2DP_BLOCK_LENGTH_4:
+               a2dp->sbc.blocks = SBC_BLK_4;
+               break;
+       case BT_A2DP_BLOCK_LENGTH_8:
+               a2dp->sbc.blocks = SBC_BLK_8;
+               break;
+       case BT_A2DP_BLOCK_LENGTH_12:
+               a2dp->sbc.blocks = SBC_BLK_12;
+               break;
+       case BT_A2DP_BLOCK_LENGTH_16:
+               a2dp->sbc.blocks = SBC_BLK_16;
+               break;
+       default:
+               assert(0);
+       }
+
+       a2dp->sbc.bitpool = active_capabilities->max_bitpool;
+       a2dp->codesize = (uint16_t) sbc_get_codesize(&a2dp->sbc);
+}
+
+static int bt_open(struct userdata *u)
+{
+       union {
+               struct bt_open_req open_req;
+               struct bt_open_rsp open_rsp;
+               bt_audio_error_t error;
+               uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
+       } msg;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.open_req.h.type = BT_REQUEST;
+       msg.open_req.h.name = BT_OPEN;
+       msg.open_req.h.length = sizeof(msg.open_req);
+
+       strncpy(msg.open_req.destination, u->address,
+                       sizeof(msg.open_req.destination));
+       msg.open_req.seid = u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ?
+                               u->a2dp.sbc_capabilities.capability.seid :
+                               BT_A2DP_SEID_RANGE + 1;
+       msg.open_req.lock = u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ?
+                               BT_WRITE_LOCK : BT_READ_LOCK | BT_WRITE_LOCK;
+
+       if (service_send(u, &msg.open_req.h) < 0)
+               return -1;
+
+       msg.open_rsp.h.length = sizeof(msg.open_rsp);
+       if (service_expect(u, &msg.open_rsp.h, BT_OPEN) < 0)
+               return -1;
+
+       return 0;
+}
+
+static int set_conf(struct userdata *u)
+{
+       union {
+               struct bt_set_configuration_req setconf_req;
+               struct bt_set_configuration_rsp setconf_rsp;
+               bt_audio_error_t error;
+               uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
+       } msg;
+
+       if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+               if (setup_a2dp(u) < 0)
+                       return -1;
+       }
+
+       memset(&msg, 0, sizeof(msg));
+       msg.setconf_req.h.type = BT_REQUEST;
+       msg.setconf_req.h.name = BT_SET_CONFIGURATION;
+       msg.setconf_req.h.length = sizeof(msg.setconf_req);
+
+       if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+               memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities,
+                       sizeof(u->a2dp.sbc_capabilities));
+               msg.setconf_req.h.length += msg.setconf_req.codec.length -
+                       sizeof(msg.setconf_req.codec);
+       } else {
+               msg.setconf_req.codec.transport = BT_CAPABILITIES_TRANSPORT_SCO;
+               msg.setconf_req.codec.seid = BT_A2DP_SEID_RANGE + 1;
+               msg.setconf_req.codec.length = sizeof(pcm_capabilities_t);
+       }
+
+       if (service_send(u, &msg.setconf_req.h) < 0)
+               return -1;
+
+       msg.setconf_rsp.h.length = sizeof(msg.setconf_rsp);
+       if (service_expect(u, &msg.setconf_rsp.h, BT_SET_CONFIGURATION) < 0)
+               return -1;
+
+       u->link_mtu = msg.setconf_rsp.link_mtu;
+
+       /* setup SBC encoder now we agree on parameters */
+       if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+               setup_sbc(&u->a2dp);
+               u->block_size = u->a2dp.codesize;
+               DBG("SBC parameters:\n\tallocation=%u\n"
+                       "\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
+                       u->a2dp.sbc.allocation, u->a2dp.sbc.subbands,
+                       u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
+       } else
+               u->block_size = u->link_mtu;
+
+       return 0;
+}
+
+static int setup_bt(struct userdata *u)
+{
+       assert(u);
+
+       if (get_caps(u) < 0)
+               return -1;
+
+       DBG("Got device caps");
+
+       if (bt_open(u) < 0)
+               return -1;
+
+       if (set_conf(u) < 0)
+               return -1;
+
+       return 0;
+}
+
+static int init_profile(struct userdata *u)
+{
+       assert(u);
+
+       return setup_bt(u);
+}
+
+static void shutdown_bt(struct userdata *u)
+{
+       assert(u);
+
+       if (u->stream_fd != -1) {
+               stop_stream(u);
+               DBG("close(stream_fd)");
+               close(u->stream_fd);
+               u->stream_fd = -1;
+       }
+
+       if (u->service_fd != -1) {
+               DBG("bt_audio_service_close");
+               bt_audio_service_close(u->service_fd);
+               u->service_fd = -1;
+       }
+}
+
+static void make_fd_nonblock(int fd)
+{
+       int v;
+
+       assert(fd >= 0);
+       assert((v = fcntl(fd, F_GETFL)) >= 0);
+
+       if (!(v & O_NONBLOCK))
+               assert(fcntl(fd, F_SETFL, v|O_NONBLOCK) >= 0);
+}
+
+static void make_socket_low_delay(int fd)
+{
+/* FIXME: is this widely supported? */
+#ifdef SO_PRIORITY
+       int priority;
+       assert(fd >= 0);
+
+       priority = 6;
+       if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void*)&priority,
+                       sizeof(priority)) < 0)
+               ERR("SO_PRIORITY failed: %s", strerror(errno));
+#endif
+}
+
+static int read_stream(struct userdata *u)
+{
+       int ret = 0;
+       ssize_t l;
+       char *buf;
+
+       assert(u);
+       assert(u->stream_fd >= 0);
+
+       buf = alloca(u->link_mtu);
+
+       for (;;) {
+               l = read(u->stream_fd, buf, u->link_mtu);
+               if (u->debug_stream_read)
+                       DBG("read from socket: %lli bytes", (long long) l);
+               if (l <= 0) {
+                       if (l < 0 && errno == EINTR)
+                               continue;
+                       else {
+                               ERR("Failed to read date from stream_fd: %s",
+                                       ret < 0 ? strerror(errno) : "EOF");
+                               return -1;
+                       }
+               } else {
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+/* It's what PulseAudio is doing, not sure it's necessary for this
+ * test */
+static ssize_t pa_write(int fd, const void *buf, size_t count)
+{
+       ssize_t r;
+
+       if ((r = send(fd, buf, count, MSG_NOSIGNAL)) >= 0)
+               return r;
+
+       if (errno != ENOTSOCK)
+               return r;
+
+       return write(fd, buf, count);
+}
+
+static int write_stream(struct userdata *u)
+{
+       int ret = 0;
+       ssize_t l;
+       char *buf;
+
+       assert(u);
+       assert(u->stream_fd >= 0);
+       buf = alloca(u->link_mtu);
+
+       for (;;) {
+               l = pa_write(u->stream_fd, buf, u->link_mtu);
+               if (u->debug_stream_write)
+                       DBG("written to socket: %lli bytes", (long long) l);
+               assert(l != 0);
+               if (l < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       else {
+                               ERR("Failed to write data: %s", strerror(errno));
+                               ret = -1;
+                               break;
+                       }
+               } else {
+                       assert((size_t)l <= u->link_mtu);
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static gboolean stream_cb(GIOChannel *gin, GIOCondition condition, gpointer data)
+{
+       struct userdata *u;
+
+       assert(u = data);
+
+       if (condition & G_IO_IN) {
+               if (read_stream(u) < 0)
+                       goto fail;
+       } else if (condition & G_IO_OUT) {
+               if (write_stream(u) < 0)
+                       goto fail;
+       } else {
+               DBG("Got %d", condition);
+               g_main_loop_quit(main_loop);
+               return FALSE;
+       }
+
+       return TRUE;
+
+fail:
+       stop_stream(u);
+       return FALSE;
+}
+
+static int start_stream(struct userdata *u)
+{
+       union {
+               bt_audio_msg_header_t rsp;
+               struct bt_start_stream_req start_req;
+               struct bt_start_stream_rsp start_rsp;
+               struct bt_new_stream_ind streamfd_ind;
+               bt_audio_error_t error;
+               uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
+       } msg;
+
+       assert(u);
+
+       if (u->stream_fd >= 0)
+               return 0;
+       if (u->stream_watch != 0) {
+               g_source_remove(u->stream_watch);
+               u->stream_watch = 0;
+       }
+       if (u->stream_channel != 0) {
+               g_io_channel_unref(u->stream_channel);
+               u->stream_channel = NULL;
+       }
+
+       memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+       msg.start_req.h.type = BT_REQUEST;
+       msg.start_req.h.name = BT_START_STREAM;
+       msg.start_req.h.length = sizeof(msg.start_req);
+
+       if (service_send(u, &msg.start_req.h) < 0)
+               return -1;
+
+       msg.rsp.length = sizeof(msg.start_rsp);
+       if (service_expect(u, &msg.rsp, BT_START_STREAM) < 0)
+               return -1;
+
+       msg.rsp.length = sizeof(msg.streamfd_ind);
+       if (service_expect(u, &msg.rsp, BT_NEW_STREAM) < 0)
+               return -1;
+
+       if ((u->stream_fd = bt_audio_service_get_data_fd(u->service_fd)) < 0) {
+               DBG("Failed to get stream fd from audio service.");
+               return -1;
+       }
+
+       make_fd_nonblock(u->stream_fd);
+       make_socket_low_delay(u->stream_fd);
+
+       assert(u->stream_channel = g_io_channel_unix_new(u->stream_fd));
+
+       u->stream_watch = g_io_add_watch(u->stream_channel,
+                                       G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
+                                       stream_cb, u);
+
+       return 0;
+}
+
+static int stop_stream(struct userdata *u)
+{
+       union {
+               bt_audio_msg_header_t rsp;
+               struct bt_stop_stream_req stop_req;
+               struct bt_stop_stream_rsp stop_rsp;
+               bt_audio_error_t error;
+               uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
+       } msg;
+       int r = 0;
+
+       if (u->stream_fd < 0)
+               return 0;
+
+       assert(u);
+       assert(u->stream_channel);
+
+       g_source_remove(u->stream_watch);
+       u->stream_watch = 0;
+       g_io_channel_unref(u->stream_channel);
+       u->stream_channel = NULL;
+
+       memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+       msg.stop_req.h.type = BT_REQUEST;
+       msg.stop_req.h.name = BT_STOP_STREAM;
+       msg.stop_req.h.length = sizeof(msg.stop_req);
+
+       if (service_send(u, &msg.stop_req.h) < 0) {
+               r = -1;
+               goto done;
+       }
+
+       msg.rsp.length = sizeof(msg.stop_rsp);
+       if (service_expect(u, &msg.rsp, BT_STOP_STREAM) < 0)
+               r = -1;
+
+done:
+       close(u->stream_fd);
+       u->stream_fd = -1;
+
+       return r;
+}
+
+static gboolean sleep_cb(gpointer data)
+{
+       struct userdata *u;
+
+       assert(u = data);
+
+       u->gin_watch = g_io_add_watch(u->gin,
+               G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, input_cb, data);
+
+       printf(">>> ");
+       fflush(stdout);
+
+       return FALSE;
+}
+
+static gboolean input_cb(GIOChannel *gin, GIOCondition condition, gpointer data)
+{
+       char *line, *tmp;
+       gsize term_pos;
+       GError *error = NULL;
+       struct userdata *u;
+       int success;
+
+       assert(u = data);
+       if (!(condition & G_IO_IN)) {
+               DBG("Got %d", condition);
+               g_main_loop_quit(main_loop);
+               return FALSE;
+       }
+
+       if (g_io_channel_read_line(gin, &line, NULL, &term_pos, &error) !=
+               G_IO_STATUS_NORMAL)
+               return FALSE;
+
+       line[term_pos] = '\0';
+       g_strstrip(line);
+       if ((tmp = strchr(line, '#')))
+               *tmp = '\0';
+       success = FALSE;
+
+#define IF_CMD(cmd) \
+       if (!success && (success = (strncmp(line, #cmd, strlen(#cmd)) == 0)))
+
+       IF_CMD(quit) {
+               g_main_loop_quit(main_loop);
+               return FALSE;
+       }
+
+       IF_CMD(sleep) {
+               unsigned int seconds;
+               if (sscanf(line, "%*s %d", &seconds) != 1)
+                       DBG("sleep SECONDS");
+               else {
+                       g_source_remove(u->gin_watch);
+                       g_timeout_add_seconds(seconds, sleep_cb, u);
+                       return FALSE;
+               }
+       }
+
+       IF_CMD(debug) {
+               char *what = NULL;
+               int enable;
+
+               if (sscanf(line, "%*s %as %d", &what, &enable) != 1)
+                       DBG("debug [stream_read|stream_write] [0|1]");
+               if (strncmp(what, "stream_read", 12) == 0) {
+                       u->debug_stream_read = enable;
+               } else if (strncmp(what, "stream_write", 13) == 0) {
+                       u->debug_stream_write = enable;
+               } else {
+                       DBG("debug [stream_read|stream_write] [0|1]");
+               }
+       }
+
+       IF_CMD(init_bt) {
+               DBG("%d", init_bt(u));
+       }
+
+       IF_CMD(init_profile) {
+               DBG("%d", init_profile(u));
+       }
+
+       IF_CMD(start_stream) {
+               DBG("%d", start_stream(u));
+       }
+
+       IF_CMD(stop_stream) {
+               DBG("%d", stop_stream(u));
+       }
+
+       IF_CMD(shutdown_bt) {
+               shutdown_bt(u);
+       }
+
+       IF_CMD(rate) {
+               if (sscanf(line, "%*s %d", &u->rate) != 1)
+                       DBG("set with rate RATE");
+               DBG("rate %d", u->rate);
+       }
+
+       IF_CMD(bdaddr) {
+               char *address;
+
+               if (sscanf(line, "%*s %as", &address) != 1)
+                       DBG("set with bdaddr BDADDR");
+
+               free(u->address);
+
+               u->address = address;
+               DBG("bdaddr %s", u->address);
+       }
+
+       IF_CMD(profile) {
+               char *profile = NULL;
+
+               if (sscanf(line, "%*s %as", &profile) != 1)
+                       DBG("set with profile [hsp|a2dp]");
+               if (strncmp(profile, "hsp", 4) == 0) {
+                       u->transport = BT_CAPABILITIES_TRANSPORT_SCO;
+               } else if (strncmp(profile, "a2dp", 5) == 0) {
+                       u->transport = BT_CAPABILITIES_TRANSPORT_A2DP;
+               } else {
+                       DBG("set with profile [hsp|a2dp]");
+               }
+
+               free(profile);
+               DBG("profile %s", u->transport == BT_CAPABILITIES_TRANSPORT_SCO ?
+                       "hsp" : "a2dp");
+       }
+
+       if (!success && strlen(line) != 0) {
+               DBG("%s, unknown command", line);
+       }
+
+       printf(">>> ");
+       fflush(stdout);
+       return TRUE;
+}
+
+
+static void show_usage(char* prgname)
+{
+       printf("%s: ipctest [--interactive] BDADDR\n", basename(prgname));
+}
+
+static void sig_term(int sig)
+{
+       g_main_loop_quit(main_loop);
+}
+
+int main(int argc, char *argv[])
+{
+       if (argc < 2) {
+               show_usage(argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       assert(main_loop = g_main_loop_new(NULL, FALSE));
+
+       if (strncmp("--interactive", argv[1], 14) == 0) {
+               if (argc < 3) {
+                       show_usage(argv[0]);
+                       exit(EXIT_FAILURE);
+               }
+
+               data.address = strdup(argv[2]);
+
+               signal(SIGTERM, sig_term);
+               signal(SIGINT, sig_term);
+
+               assert(data.gin = g_io_channel_unix_new(fileno(stdin)));
+
+               data.gin_watch = g_io_add_watch(data.gin,
+                       G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, input_cb, &data);
+
+               printf(">>> ");
+               fflush(stdout);
+
+               g_main_loop_run(main_loop);
+
+       } else {
+               data.address = strdup(argv[1]);
+
+               assert(init_bt(&data) == 0);
+
+               assert(init_profile(&data) == 0);
+
+               assert(start_stream(&data) == 0);
+
+               g_main_loop_run(main_loop);
+
+               assert(stop_stream(&data) == 0);
+
+               shutdown_bt(&data);
+       }
+
+       g_main_loop_unref(main_loop);
+
+       printf("\nExiting\n");
+
+       exit(EXIT_SUCCESS);
+
+       return 0;
+}
diff --git a/test/l2test.c b/test/l2test.c
new file mode 100644 (file)
index 0000000..f66486d
--- /dev/null
@@ -0,0 +1,1560 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#define NIBBLE_TO_ASCII(c)  ((c) < 0x0a ? (c) + 0x30 : (c) + 0x57)
+
+/* Test modes */
+enum {
+       SEND,
+       RECV,
+       RECONNECT,
+       MULTY,
+       DUMP,
+       CONNECT,
+       CRECV,
+       LSEND,
+       SENDDUMP,
+       LSENDDUMP,
+       LSENDRECV,
+       CSENDRECV,
+       INFOREQ,
+       PAIRING,
+};
+
+static unsigned char *buf;
+
+/* Default mtu */
+static int imtu = 672;
+static int omtu = 0;
+
+/* Default FCS option */
+static int fcs = 0x01;
+
+/* Default Transmission Window */
+static int txwin_size = 63;
+
+/* Default Max Transmission */
+static int max_transmit = 3;
+
+/* Default data size */
+static long data_size = -1;
+static long buffer_size = 2048;
+
+/* Default addr and psm and cid */
+static bdaddr_t bdaddr;
+static unsigned short psm = 0x1011;
+static unsigned short cid = 0;
+
+/* Default number of frames to send (-1 = infinite) */
+static int num_frames = -1;
+
+/* Default number of consecutive frames before the delay */
+static int count = 1;
+
+/* Default delay after sending count number of frames */
+static unsigned long send_delay = 0;
+
+/* Default delay before receiving */
+static unsigned long recv_delay = 0;
+
+static char *filename = NULL;
+
+static int rfcmode = 0;
+static int master = 0;
+static int auth = 0;
+static int encrypt = 0;
+static int secure = 0;
+static int socktype = SOCK_SEQPACKET;
+static int linger = 0;
+static int reliable = 0;
+static int timestamp = 0;
+static int defer_setup = 0;
+static int priority = -1;
+static int rcvbuf = 0;
+static int chan_policy = -1;
+static int bdaddr_type = 0;
+
+struct lookup_table {
+       char    *name;
+       int     flag;
+};
+
+static struct lookup_table l2cap_modes[] = {
+       { "basic",      L2CAP_MODE_BASIC        },
+       /* Not implemented
+       { "flowctl",    L2CAP_MODE_FLOWCTL      },
+       { "retrans",    L2CAP_MODE_RETRANS      },
+       */
+       { "ertm",       L2CAP_MODE_ERTM         },
+       { "streaming",  L2CAP_MODE_STREAMING    },
+       { 0 }
+};
+
+static struct lookup_table chan_policies[] = {
+       { "bredr",      BT_CHANNEL_POLICY_BREDR_ONLY            },
+       { "bredr_pref", BT_CHANNEL_POLICY_BREDR_PREFERRED       },
+       { "amp_pref",   BT_CHANNEL_POLICY_AMP_PREFERRED         },
+       { NULL,         0                                       },
+};
+
+static struct lookup_table bdaddr_types[] = {
+       { "bredr",      BDADDR_BREDR            },
+       { "le_public",  BDADDR_LE_PUBLIC        },
+       { "le_random",  BDADDR_LE_RANDOM        },
+       { NULL,         0                       },
+};
+
+static int get_lookup_flag(struct lookup_table *table, char *name)
+{
+       int i;
+
+       for (i = 0; table[i].name; i++)
+               if (!strcasecmp(table[i].name, name))
+                       return table[i].flag;
+
+       return -1;
+}
+
+static void print_lookup_values(struct lookup_table *table, char *header)
+{
+       int i;
+
+       printf("%s\n", header);
+
+       for (i = 0; table[i].name; i++)
+               printf("\t%s\n", table[i].name);
+}
+
+static float tv2fl(struct timeval tv)
+{
+       return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static char *ltoh(unsigned long c, char* s)
+{
+       int c1;
+
+       c1     = (c >> 28) & 0x0f;
+       *(s++) = NIBBLE_TO_ASCII (c1);
+       c1     = (c >> 24) & 0x0f;
+       *(s++) = NIBBLE_TO_ASCII (c1);
+       c1     = (c >> 20) & 0x0f;
+       *(s++) = NIBBLE_TO_ASCII (c1);
+       c1     = (c >> 16) & 0x0f;
+       *(s++) = NIBBLE_TO_ASCII (c1);
+       c1     = (c >> 12) & 0x0f;
+       *(s++) = NIBBLE_TO_ASCII (c1);
+       c1     = (c >>  8) & 0x0f;
+       *(s++) = NIBBLE_TO_ASCII (c1);
+       c1     = (c >>  4) & 0x0f;
+       *(s++) = NIBBLE_TO_ASCII (c1);
+       c1     = c & 0x0f;
+       *(s++) = NIBBLE_TO_ASCII (c1);
+       *s     = 0;
+       return s;
+}
+
+static char *ctoh(char c, char* s)
+{
+       char c1;
+
+       c1     = (c >> 4) & 0x0f;
+       *(s++) = NIBBLE_TO_ASCII (c1);
+       c1     = c & 0x0f;
+       *(s++) = NIBBLE_TO_ASCII (c1);
+       *s     = 0;
+       return s;
+}
+
+static void hexdump(unsigned char *s, unsigned long l)
+{
+       char bfr[80];
+       char *pb;
+       unsigned long i, n = 0;
+
+       if (l == 0)
+               return;
+
+       while (n < l) {
+               pb = bfr;
+               pb = ltoh (n, pb);
+               *(pb++) = ':';
+               *(pb++) = ' ';
+               for (i = 0; i < 16; i++) {
+                       if (n + i >= l) {
+                               *(pb++) = ' ';
+                               *(pb++) = ' ';
+                       } else
+                               pb = ctoh (*(s + i), pb);
+                       *(pb++) = ' ';
+               }
+               *(pb++) = ' ';
+               for (i = 0; i < 16; i++) {
+                       if (n + i >= l)
+                               break;
+                       else
+                               *(pb++) = (isprint (*(s + i)) ? *(s + i) : '.');
+               }
+               *pb = 0;
+               n += 16;
+               s += 16;
+               puts(bfr);
+       }
+}
+
+static int do_connect(char *svr)
+{
+       struct sockaddr_l2 addr;
+       struct l2cap_options opts;
+       struct l2cap_conninfo conn;
+       socklen_t optlen;
+       int sk, opt;
+
+       /* Create socket */
+       sk = socket(PF_BLUETOOTH, socktype, BTPROTO_L2CAP);
+       if (sk < 0) {
+               syslog(LOG_ERR, "Can't create socket: %s (%d)",
+                                                       strerror(errno), errno);
+               return -1;
+       }
+
+       /* Bind to local address */
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, &bdaddr);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Get default options */
+       memset(&opts, 0, sizeof(opts));
+       optlen = sizeof(opts);
+
+       if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Set new options */
+       opts.omtu = omtu;
+       opts.imtu = imtu;
+       opts.mode = rfcmode;
+
+       opts.fcs = fcs;
+       opts.txwin_size = txwin_size;
+       opts.max_tx = max_transmit;
+
+       if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+               syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+#if 0
+       /* Enable SO_TIMESTAMP */
+       if (timestamp) {
+               int t = 1;
+
+               if (setsockopt(sk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+                       syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+       }
+#endif
+
+       if (chan_policy != -1) {
+               if (setsockopt(sk, SOL_BLUETOOTH, BT_CHANNEL_POLICY,
+                               &chan_policy, sizeof(chan_policy)) < 0) {
+                       syslog(LOG_ERR, "Can't enable chan policy : %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+       }
+
+       /* Enable SO_LINGER */
+       if (linger) {
+               struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+               if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+                       syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+       }
+
+       /* Set link mode */
+       opt = 0;
+       if (reliable)
+               opt |= L2CAP_LM_RELIABLE;
+       if (master)
+               opt |= L2CAP_LM_MASTER;
+       if (auth)
+               opt |= L2CAP_LM_AUTH;
+       if (encrypt)
+               opt |= L2CAP_LM_ENCRYPT;
+       if (secure)
+               opt |= L2CAP_LM_SECURE;
+
+       if (setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+               syslog(LOG_ERR, "Can't set L2CAP link mode: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Set receive buffer size */
+       if (rcvbuf && setsockopt(sk, SOL_SOCKET, SO_RCVBUF,
+                                               &rcvbuf, sizeof(rcvbuf)) < 0) {
+               syslog(LOG_ERR, "Can't set socket rcv buf size: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       optlen = sizeof(rcvbuf);
+       if (getsockopt(sk, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get socket rcv buf size: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Connect to remote device */
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       str2ba(svr, &addr.l2_bdaddr);
+       addr.l2_bdaddr_type = bdaddr_type;
+       if (cid)
+               addr.l2_cid = htobs(cid);
+       else if (psm)
+               addr.l2_psm = htobs(psm);
+       else
+               goto error;
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
+               syslog(LOG_ERR, "Can't connect: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Get current options */
+       memset(&opts, 0, sizeof(opts));
+       optlen = sizeof(opts);
+
+       if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Get connection information */
+       memset(&conn, 0, sizeof(conn));
+       optlen = sizeof(conn);
+
+       if (getsockopt(sk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get L2CAP connection information: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY, &priority,
+                                               sizeof(priority)) < 0) {
+               syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       if (getsockopt(sk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       syslog(LOG_INFO, "Connected [imtu %d, omtu %d, flush_to %d, "
+               "mode %d, handle %d, class 0x%02x%02x%02x, priority %d, rcvbuf %d]",
+               opts.imtu, opts.omtu, opts.flush_to, opts.mode, conn.hci_handle,
+               conn.dev_class[2], conn.dev_class[1], conn.dev_class[0], opt,
+               rcvbuf);
+
+       omtu = (opts.omtu > buffer_size) ? buffer_size : opts.omtu;
+       imtu = (opts.imtu > buffer_size) ? buffer_size : opts.imtu;
+
+       return sk;
+
+error:
+       close(sk);
+       return -1;
+}
+
+static void do_listen(void (*handler)(int sk))
+{
+       struct sockaddr_l2 addr;
+       struct l2cap_options opts;
+       struct l2cap_conninfo conn;
+       socklen_t optlen;
+       int sk, nsk, opt;
+       char ba[18];
+
+       /* Create socket */
+       sk = socket(PF_BLUETOOTH, socktype, BTPROTO_L2CAP);
+       if (sk < 0) {
+               syslog(LOG_ERR, "Can't create socket: %s (%d)",
+                                                       strerror(errno), errno);
+               exit(1);
+       }
+
+       /* Bind to local address */
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, &bdaddr);
+       if (cid)
+               addr.l2_cid = htobs(cid);
+       else if (psm)
+               addr.l2_psm = htobs(psm);
+       else
+               goto error;
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Set link mode */
+       opt = 0;
+       if (reliable)
+               opt |= L2CAP_LM_RELIABLE;
+       if (master)
+               opt |= L2CAP_LM_MASTER;
+       if (auth)
+               opt |= L2CAP_LM_AUTH;
+       if (encrypt)
+               opt |= L2CAP_LM_ENCRYPT;
+       if (secure)
+               opt |= L2CAP_LM_SECURE;
+
+       if (opt && setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+               syslog(LOG_ERR, "Can't set L2CAP link mode: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Get default options */
+       memset(&opts, 0, sizeof(opts));
+       optlen = sizeof(opts);
+
+       if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Set new options */
+       opts.omtu = omtu;
+       opts.imtu = imtu;
+       if (rfcmode > 0)
+               opts.mode = rfcmode;
+
+       opts.fcs = fcs;
+       opts.txwin_size = txwin_size;
+       opts.max_tx = max_transmit;
+
+       if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+               syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       if (socktype == SOCK_DGRAM) {
+               handler(sk);
+               return;
+       }
+
+       /* Enable deferred setup */
+       opt = defer_setup;
+
+       if (opt && setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP,
+                                               &opt, sizeof(opt)) < 0) {
+               syslog(LOG_ERR, "Can't enable deferred setup : %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+
+       /* Listen for connections */
+       if (listen(sk, 10)) {
+               syslog(LOG_ERR, "Can not listen on the socket: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Check for socket address */
+       memset(&addr, 0, sizeof(addr));
+       optlen = sizeof(addr);
+
+       if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get socket name: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       psm = btohs(addr.l2_psm);
+       cid = btohs(addr.l2_cid);
+
+       syslog(LOG_INFO, "Waiting for connection on psm %d ...", psm);
+
+       while (1) {
+               memset(&addr, 0, sizeof(addr));
+               optlen = sizeof(addr);
+
+               nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+               if (nsk < 0) {
+                       syslog(LOG_ERR, "Accept failed: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+               if (fork()) {
+                       /* Parent */
+                       close(nsk);
+                       continue;
+               }
+               /* Child */
+               close(sk);
+
+               /* Set receive buffer size */
+               if (rcvbuf && setsockopt(nsk, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
+                                                       sizeof(rcvbuf)) < 0) {
+                       syslog(LOG_ERR, "Can't set rcv buf size: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+
+               optlen = sizeof(rcvbuf);
+               if (getsockopt(nsk, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen)
+                                                                       < 0) {
+                       syslog(LOG_ERR, "Can't get rcv buf size: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+
+               /* Get current options */
+               memset(&opts, 0, sizeof(opts));
+               optlen = sizeof(opts);
+
+               if (getsockopt(nsk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+                       syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)",
+                                                       strerror(errno), errno);
+                       if (!defer_setup) {
+                               close(nsk);
+                               goto error;
+                       }
+               }
+
+               /* Get connection information */
+               memset(&conn, 0, sizeof(conn));
+               optlen = sizeof(conn);
+
+               if (getsockopt(nsk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &optlen) < 0) {
+                       syslog(LOG_ERR, "Can't get L2CAP connection information: %s (%d)",
+                                                       strerror(errno), errno);
+                       if (!defer_setup) {
+                               close(nsk);
+                               goto error;
+                       }
+               }
+
+               if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY,
+                                       &priority, sizeof(priority)) < 0) {
+                       syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+                                               strerror(errno), errno);
+                       close(nsk);
+                       goto error;
+               }
+
+               optlen = sizeof(priority);
+               if (getsockopt(nsk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+                       syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+
+               ba2str(&addr.l2_bdaddr, ba);
+               syslog(LOG_INFO, "Connect from %s [imtu %d, omtu %d, "
+                               "flush_to %d, mode %d, handle %d, "
+                               "class 0x%02x%02x%02x, priority %d, rcvbuf %d]",
+                               ba, opts.imtu, opts.omtu, opts.flush_to,
+                               opts.mode, conn.hci_handle, conn.dev_class[2],
+                               conn.dev_class[1], conn.dev_class[0], opt,
+                               rcvbuf);
+
+               omtu = (opts.omtu > buffer_size) ? buffer_size : opts.omtu;
+               imtu = (opts.imtu > buffer_size) ? buffer_size : opts.imtu;
+
+#if 0
+               /* Enable SO_TIMESTAMP */
+               if (timestamp) {
+                       int t = 1;
+
+                       if (setsockopt(nsk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+                               syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+                                                       strerror(errno), errno);
+                               goto error;
+                       }
+               }
+#endif
+
+               /* Enable SO_LINGER */
+               if (linger) {
+                       struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+                       if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+                               syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+                                                       strerror(errno), errno);
+                               close(nsk);
+                               goto error;
+                       }
+               }
+
+               /* Handle deferred setup */
+               if (defer_setup) {
+                       syslog(LOG_INFO, "Waiting for %d seconds",
+                                                       abs(defer_setup) - 1);
+                       sleep(abs(defer_setup) - 1);
+
+                       if (defer_setup < 0) {
+                               close(nsk);
+                               goto error;
+                       }
+               }
+
+               handler(nsk);
+
+               syslog(LOG_INFO, "Disconnect: %m");
+               exit(0);
+       }
+
+       return;
+
+error:
+       close(sk);
+       exit(1);
+}
+
+static void dump_mode(int sk)
+{
+       socklen_t optlen;
+       int opt, len;
+
+       if (data_size < 0)
+               data_size = imtu;
+
+       if (defer_setup) {
+               len = read(sk, buf, sizeof(buf));
+               if (len < 0)
+                       syslog(LOG_ERR, "Initial read error: %s (%d)",
+                                               strerror(errno), errno);
+               else
+                       syslog(LOG_INFO, "Initial bytes %d", len);
+       }
+
+       syslog(LOG_INFO, "Receiving ...");
+       while (1) {
+               fd_set rset;
+
+               FD_ZERO(&rset);
+               FD_SET(sk, &rset);
+
+               if (select(sk + 1, &rset, NULL, NULL, NULL) < 0)
+                       return;
+
+               if (!FD_ISSET(sk, &rset))
+                       continue;
+
+               len = read(sk, buf, data_size);
+               if (len <= 0) {
+                       if (len < 0) {
+                               if (reliable && (errno == ECOMM)) {
+                                       syslog(LOG_INFO, "L2CAP Error ECOMM - clearing error and continuing.");
+                                       optlen = sizeof(opt);
+                                       if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0) {
+                                               syslog(LOG_ERR, "Couldn't getsockopt(SO_ERROR): %s (%d)",
+                                                       strerror(errno), errno);
+                                               return;
+                                       }
+                                       continue;
+                               } else {
+                                       syslog(LOG_ERR, "Read error: %s(%d)",
+                                                       strerror(errno), errno);
+                               }
+                       }
+                       return;
+               }
+
+               syslog(LOG_INFO, "Recevied %d bytes", len);
+               hexdump(buf, len);
+       }
+}
+
+static void recv_mode(int sk)
+{
+       struct timeval tv_beg, tv_end, tv_diff;
+       struct pollfd p;
+       char ts[30];
+       long total;
+       uint32_t seq;
+       socklen_t optlen;
+       int opt, len;
+
+       if (data_size < 0)
+               data_size = imtu;
+
+       if (defer_setup) {
+               len = read(sk, buf, sizeof(buf));
+               if (len < 0)
+                       syslog(LOG_ERR, "Initial read error: %s (%d)",
+                                               strerror(errno), errno);
+               else
+                       syslog(LOG_INFO, "Initial bytes %d", len);
+       }
+
+       if (recv_delay)
+               usleep(recv_delay);
+
+       syslog(LOG_INFO, "Receiving ...");
+
+       memset(ts, 0, sizeof(ts));
+
+       p.fd = sk;
+       p.events = POLLIN | POLLERR | POLLHUP;
+
+       seq = 0;
+       while (1) {
+               gettimeofday(&tv_beg, NULL);
+               total = 0;
+               while (total < data_size) {
+                       uint32_t sq;
+                       uint16_t l;
+                       int i;
+
+                       p.revents = 0;
+                       if (poll(&p, 1, -1) <= 0)
+                               return;
+
+                       if (p.revents & (POLLERR | POLLHUP))
+                               return;
+
+                       len = recv(sk, buf, data_size, 0);
+                       if (len < 0) {
+                               if (reliable && (errno == ECOMM)) {
+                                       syslog(LOG_INFO, "L2CAP Error ECOMM - clearing error and continuing.\n");
+                                       optlen = sizeof(opt);
+                                       if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0) {
+                                               syslog(LOG_ERR, "Couldn't getsockopt(SO_ERROR): %s (%d)",
+                                                       strerror(errno), errno);
+                                               return;
+                                       }
+                                       continue;
+                               } else {
+                                       syslog(LOG_ERR, "Read failed: %s (%d)",
+                                               strerror(errno), errno);
+                               }
+                       }
+
+                       if (len < 6)
+                               break;
+
+                       if (timestamp) {
+                               struct timeval tv;
+
+                               if (ioctl(sk, SIOCGSTAMP, &tv) < 0) {
+                                       timestamp = 0;
+                                       memset(ts, 0, sizeof(ts));
+                               } else {
+                                       sprintf(ts, "[%ld.%ld] ",
+                                                       tv.tv_sec, tv.tv_usec);
+                               }
+                       }
+
+                       /* Check sequence */
+                       sq = btohl(*(uint32_t *) buf);
+                       if (seq != sq) {
+                               syslog(LOG_INFO, "seq missmatch: %d -> %d", seq, sq);
+                               seq = sq;
+                       }
+                       seq++;
+
+                       /* Check length */
+                       l = btohs(*(uint16_t *) (buf + 4));
+                       if (len != l) {
+                               syslog(LOG_INFO, "size missmatch: %d -> %d", len, l);
+                               continue;
+                       }
+
+                       /* Verify data */
+                       for (i = 6; i < len; i++) {
+                               if (buf[i] != 0x7f)
+                                       syslog(LOG_INFO, "data missmatch: byte %d 0x%2.2x", i, buf[i]);
+                       }
+
+                       total += len;
+               }
+               gettimeofday(&tv_end, NULL);
+
+               timersub(&tv_end, &tv_beg, &tv_diff);
+
+               syslog(LOG_INFO,"%s%ld bytes in %.2f sec, %.2f kB/s", ts, total,
+                       tv2fl(tv_diff), (float)(total / tv2fl(tv_diff) ) / 1024.0);
+       }
+}
+
+static void do_send(int sk)
+{
+       uint32_t seq;
+       int i, fd, len, buflen, size, sent;
+
+       syslog(LOG_INFO, "Sending ...");
+
+       if (data_size < 0)
+               data_size = omtu;
+
+       if (filename) {
+               fd = open(filename, O_RDONLY);
+               if (fd < 0) {
+                       syslog(LOG_ERR, "Open failed: %s (%d)",
+                                                       strerror(errno), errno);
+                       exit(1);
+               }
+
+               sent = 0;
+               size = read(fd, buf, data_size);
+               while (size > 0) {
+                       buflen = (size > omtu) ? omtu : size;
+
+                       len = send(sk, buf + sent, buflen, 0);
+
+                       sent += len;
+                       size -= len;
+               }
+               return;
+       } else {
+               for (i = 6; i < data_size; i++)
+                       buf[i] = 0x7f;
+       }
+
+       seq = 0;
+       while ((num_frames == -1) || (num_frames-- > 0)) {
+               *(uint32_t *) buf = htobl(seq);
+               *(uint16_t *) (buf + 4) = htobs(data_size);
+               seq++;
+
+               sent = 0;
+               size = data_size;
+               while (size > 0) {
+                       buflen = (size > omtu) ? omtu : size;
+
+                       len = send(sk, buf, buflen, 0);
+                       if (len < 0 || len != buflen) {
+                               syslog(LOG_ERR, "Send failed: %s (%d)",
+                                                       strerror(errno), errno);
+                               exit(1);
+                       }
+
+                       sent += len;
+                       size -= len;
+               }
+
+               if (num_frames && send_delay && count && !(seq % count))
+                       usleep(send_delay);
+       }
+}
+
+static void send_mode(int sk)
+{
+       do_send(sk);
+
+       syslog(LOG_INFO, "Closing channel ...");
+       if (shutdown(sk, SHUT_RDWR) < 0)
+               syslog(LOG_INFO, "Close failed: %m");
+       else
+               syslog(LOG_INFO, "Done");
+}
+
+static void senddump_mode(int sk)
+{
+       do_send(sk);
+
+       dump_mode(sk);
+}
+
+static void send_and_recv_mode(int sk)
+{
+       int flags;
+
+       if ((flags = fcntl(sk, F_GETFL, 0)) < 0)
+               flags = 0;
+       fcntl(sk, F_SETFL, flags | O_NONBLOCK);
+
+       /* fork for duplex channel */
+       if (fork())
+               send_mode(sk);
+       else
+               recv_mode(sk);
+       return;
+}
+
+static void reconnect_mode(char *svr)
+{
+       while (1) {
+               int sk = do_connect(svr);
+               close(sk);
+       }
+}
+
+static void connect_mode(char *svr)
+{
+       struct pollfd p;
+       int sk;
+
+       if ((sk = do_connect(svr)) < 0)
+               exit(1);
+
+       p.fd = sk;
+       p.events = POLLERR | POLLHUP;
+
+       while (1) {
+               p.revents = 0;
+               if (poll(&p, 1, 500))
+                       break;
+       }
+
+       syslog(LOG_INFO, "Disconnected");
+
+       close(sk);
+}
+
+static void multi_connect_mode(int argc, char *argv[])
+{
+       int i, n, sk;
+
+       while (1) {
+               for (n = 0; n < argc; n++) {
+                       for (i = 0; i < count; i++) {
+                               if (fork())
+                                       continue;
+
+                               /* Child */
+                               sk = do_connect(argv[n]);
+                               usleep(500);
+                               close(sk);
+                               exit(0);
+                       }
+               }
+               sleep(4);
+       }
+}
+
+static void info_request(char *svr)
+{
+       unsigned char buf[48];
+       l2cap_cmd_hdr *cmd = (l2cap_cmd_hdr *) buf;
+       l2cap_info_req *req = (l2cap_info_req *) (buf + L2CAP_CMD_HDR_SIZE);
+       l2cap_info_rsp *rsp = (l2cap_info_rsp *) (buf + L2CAP_CMD_HDR_SIZE);
+       uint16_t mtu;
+       uint32_t channels, mask = 0x0000;
+       struct sockaddr_l2 addr;
+       int sk, err;
+
+       sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+       if (sk < 0) {
+               perror("Can't create socket");
+               return;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, &bdaddr);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Can't bind socket");
+               goto failed;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       str2ba(svr, &addr.l2_bdaddr);
+       addr.l2_bdaddr_type = bdaddr_type;
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
+               perror("Can't connect socket");
+               goto failed;
+       }
+
+       memset(buf, 0, sizeof(buf));
+       cmd->code  = L2CAP_INFO_REQ;
+       cmd->ident = 141;
+       cmd->len   = htobs(2);
+       req->type  = htobs(0x0001);
+
+       if (send(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_REQ_SIZE, 0) < 0) {
+               perror("Can't send info request");
+               goto failed;
+       }
+
+       err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + 2, 0);
+       if (err < 0) {
+               perror("Can't receive info response");
+               goto failed;
+       }
+
+       switch (btohs(rsp->result)) {
+       case 0x0000:
+               memcpy(&mtu, rsp->data, sizeof(mtu));
+               printf("Connectionless MTU size is %d\n", btohs(mtu));
+               break;
+       case 0x0001:
+               printf("Connectionless MTU is not supported\n");
+               break;
+       }
+
+       memset(buf, 0, sizeof(buf));
+       cmd->code  = L2CAP_INFO_REQ;
+       cmd->ident = 142;
+       cmd->len   = htobs(2);
+       req->type  = htobs(0x0002);
+
+       if (send(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_REQ_SIZE, 0) < 0) {
+               perror("Can't send info request");
+               goto failed;
+       }
+
+       err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + 4, 0);
+       if (err < 0) {
+               perror("Can't receive info response");
+               goto failed;
+       }
+
+       switch (btohs(rsp->result)) {
+       case 0x0000:
+               memcpy(&mask, rsp->data, sizeof(mask));
+               printf("Extended feature mask is 0x%04x\n", btohl(mask));
+               if (mask & L2CAP_FEAT_FLOWCTL)
+                       printf("  Flow control mode\n");
+               if (mask & L2CAP_FEAT_RETRANS)
+                       printf("  Retransmission mode\n");
+               if (mask & L2CAP_FEAT_BIDIR_QOS)
+                       printf("  Bi-directional QoS\n");
+               if (mask & L2CAP_FEAT_ERTM)
+                       printf("  Enhanced Retransmission mode\n");
+               if (mask & L2CAP_FEAT_STREAMING)
+                       printf("  Streaming mode\n");
+               if (mask & L2CAP_FEAT_FCS)
+                       printf("  FCS Option\n");
+               if (mask & L2CAP_FEAT_EXT_FLOW)
+                       printf("  Extended Flow Specification\n");
+               if (mask & L2CAP_FEAT_FIXED_CHAN)
+                       printf("  Fixed Channels\n");
+               if (mask & L2CAP_FEAT_EXT_WINDOW)
+                       printf("  Extended Window Size\n");
+               if (mask & L2CAP_FEAT_UCD)
+                       printf("  Unicast Connectionless Data Reception\n");
+               break;
+       case 0x0001:
+               printf("Extended feature mask is not supported\n");
+               break;
+       }
+
+       if (!(mask & 0x80))
+               goto failed;
+
+       memset(buf, 0, sizeof(buf));
+       cmd->code  = L2CAP_INFO_REQ;
+       cmd->ident = 143;
+       cmd->len   = htobs(2);
+       req->type  = htobs(0x0003);
+
+       if (send(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_REQ_SIZE, 0) < 0) {
+               perror("Can't send info request");
+               goto failed;
+       }
+
+       err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + 8, 0);
+       if (err < 0) {
+               perror("Can't receive info response");
+               goto failed;
+       }
+
+       switch (btohs(rsp->result)) {
+       case 0x0000:
+               memcpy(&channels, rsp->data, sizeof(channels));
+               printf("Fixed channels list is 0x%04x\n", btohl(channels));
+               break;
+       case 0x0001:
+               printf("Fixed channels list is not supported\n");
+               break;
+       }
+
+failed:
+       close(sk);
+}
+
+static void do_pairing(char *svr)
+{
+       struct sockaddr_l2 addr;
+       int sk, opt;
+
+       sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+       if (sk < 0) {
+               perror("Can't create socket");
+               return;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, &bdaddr);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Can't bind socket");
+               goto failed;
+       }
+
+       if (secure)
+               opt = L2CAP_LM_SECURE;
+       else
+               opt = L2CAP_LM_ENCRYPT;
+
+       if (setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+               perror("Can't set link mode");
+               goto failed;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       str2ba(svr, &addr.l2_bdaddr);
+       addr.l2_bdaddr_type = bdaddr_type;
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
+               perror("Can't connect socket");
+               goto failed;
+       }
+
+       printf("Pairing successful\n");
+
+failed:
+       close(sk);
+}
+
+static void usage(void)
+{
+       printf("l2test - L2CAP testing\n"
+               "Usage:\n");
+       printf("\tl2test <mode> [options] [bdaddr]\n");
+       printf("Modes:\n"
+               "\t-r listen and receive\n"
+               "\t-w listen and send\n"
+               "\t-d listen and dump incoming data\n"
+               "\t-x listen, then send, then dump incoming data\n"
+               "\t-t listen, then send and receive at the same time\n"
+               "\t-q connect, then send and receive at the same time\n"
+               "\t-s connect and send\n"
+               "\t-u connect and receive\n"
+               "\t-n connect and be silent\n"
+               "\t-y connect, then send, then dump incoming data\n"
+               "\t-c connect, disconnect, connect, ...\n"
+               "\t-m multiple connects\n"
+               "\t-p trigger dedicated bonding\n"
+               "\t-z information request\n");
+
+       printf("Options:\n"
+               "\t[-b bytes] [-i device] [-P psm] [-J cid]\n"
+               "\t[-I imtu] [-O omtu]\n"
+               "\t[-L seconds] enable SO_LINGER\n"
+               "\t[-W seconds] enable deferred setup\n"
+               "\t[-B filename] use data packets from file\n"
+               "\t[-N num] send num frames (default = infinite)\n"
+               "\t[-C num] send num frames before delay (default = 1)\n"
+               "\t[-D milliseconds] delay after sending num frames (default = 0)\n"
+               "\t[-K milliseconds] delay before receiving (default = 0)\n"
+               "\t[-X mode] l2cap mode (help for list, default = basic)\n"
+               "\t[-a policy] chan policy (help for list, default = bredr)\n"
+               "\t[-F fcs] use CRC16 check (default = 1)\n"
+               "\t[-Q num] Max Transmit value (default = 3)\n"
+               "\t[-Z size] Transmission Window size (default = 63)\n"
+               "\t[-Y priority] socket priority\n"
+               "\t[-H size] Maximum receive buffer size\n"
+               "\t[-R] reliable mode\n"
+               "\t[-G] use connectionless channel (datagram)\n"
+               "\t[-U] use sock stream\n"
+               "\t[-A] request authentication\n"
+               "\t[-E] request encryption\n"
+               "\t[-S] secure connection\n"
+               "\t[-M] become master\n"
+               "\t[-T] enable timestamps\n"
+               "\t[-V type] address type (help for list, default = bredr)\n");
+}
+
+int main(int argc, char *argv[])
+{
+       struct sigaction sa;
+       int opt, sk, mode = RECV, need_addr = 0;
+
+       bacpy(&bdaddr, BDADDR_ANY);
+
+       while ((opt = getopt(argc, argv, "rdscuwmntqxyzpb:a:"
+               "i:P:I:O:J:B:N:L:W:C:D:X:F:Q:Z:Y:H:K:V:RUGAESMT")) != EOF) {
+               switch (opt) {
+               case 'r':
+                       mode = RECV;
+                       break;
+
+               case 's':
+                       mode = SEND;
+                       need_addr = 1;
+                       break;
+
+               case 'w':
+                       mode = LSEND;
+                       break;
+
+               case 'u':
+                       mode = CRECV;
+                       need_addr = 1;
+                       break;
+
+               case 'd':
+                       mode = DUMP;
+                       break;
+
+               case 'c':
+                       mode = RECONNECT;
+                       need_addr = 1;
+                       break;
+
+               case 'n':
+                       mode = CONNECT;
+                       need_addr = 1;
+                       break;
+
+               case 'm':
+                       mode = MULTY;
+                       need_addr = 1;
+                       break;
+
+               case 't':
+                       mode = LSENDRECV;
+                       break;
+
+               case 'q':
+                       mode = CSENDRECV;
+                       need_addr = 1;
+                       break;
+
+               case 'x':
+                       mode = LSENDDUMP;
+                       break;
+
+               case 'y':
+                       mode = SENDDUMP;
+                       break;
+
+               case 'z':
+                       mode = INFOREQ;
+                       need_addr = 1;
+                       break;
+
+               case 'p':
+                       mode = PAIRING;
+                       need_addr = 1;
+                       break;
+
+               case 'b':
+                       data_size = atoi(optarg);
+                       break;
+
+               case 'i':
+                       if (!strncasecmp(optarg, "hci", 3))
+                               hci_devba(atoi(optarg + 3), &bdaddr);
+                       else
+                               str2ba(optarg, &bdaddr);
+                       break;
+
+               case 'P':
+                       psm = atoi(optarg);
+                       break;
+
+               case 'I':
+                       imtu = atoi(optarg);
+                       break;
+
+               case 'O':
+                       omtu = atoi(optarg);
+                       break;
+
+               case 'L':
+                       linger = atoi(optarg);
+                       break;
+
+               case 'W':
+                       defer_setup = atoi(optarg);
+                       break;
+
+               case 'B':
+                       filename = strdup(optarg);
+                       break;
+
+               case 'N':
+                       num_frames = atoi(optarg);
+                       break;
+
+               case 'C':
+                       count = atoi(optarg);
+                       break;
+
+               case 'D':
+                       send_delay = atoi(optarg) * 1000;
+                       break;
+
+               case 'K':
+                       recv_delay = atoi(optarg) * 1000;
+                       break;
+
+               case 'X':
+                       rfcmode = get_lookup_flag(l2cap_modes, optarg);
+
+                       if (rfcmode == -1) {
+                               print_lookup_values(l2cap_modes,
+                                               "List L2CAP modes:");
+                               exit(1);
+                       }
+
+                       break;
+
+               case 'a':
+                       chan_policy = get_lookup_flag(chan_policies, optarg);
+
+                       if (chan_policy == -1) {
+                               print_lookup_values(chan_policies,
+                                               "List L2CAP chan policies:");
+                               exit(1);
+                       }
+
+                       break;
+
+               case 'Y':
+                       priority = atoi(optarg);
+                       break;
+
+               case 'F':
+                       fcs = atoi(optarg);
+                       break;
+
+               case 'R':
+                       reliable = 1;
+                       break;
+
+               case 'M':
+                       master = 1;
+                       break;
+
+               case 'A':
+                       auth = 1;
+                       break;
+
+               case 'E':
+                       encrypt = 1;
+                       break;
+
+               case 'S':
+                       secure = 1;
+                       break;
+
+               case 'G':
+                       socktype = SOCK_DGRAM;
+                       break;
+
+               case 'U':
+                       socktype = SOCK_STREAM;
+                       break;
+
+               case 'T':
+                       timestamp = 1;
+                       break;
+
+               case 'Q':
+                       max_transmit = atoi(optarg);
+                       break;
+
+               case 'Z':
+                       txwin_size = atoi(optarg);
+                       break;
+
+               case 'J':
+                       cid = atoi(optarg);
+                       break;
+
+               case 'H':
+                       rcvbuf = atoi(optarg);
+                       break;
+
+               case 'V':
+                       bdaddr_type = get_lookup_flag(bdaddr_types, optarg);
+
+                       if (bdaddr_type == -1) {
+                               print_lookup_values(bdaddr_types,
+                                               "List Address types:");
+                               exit(1);
+                       }
+
+                       break;
+
+               default:
+                       usage();
+                       exit(1);
+               }
+       }
+
+       if (need_addr && !(argc - optind)) {
+               usage();
+               exit(1);
+       }
+
+       if (data_size < 0)
+               buffer_size = (omtu > imtu) ? omtu : imtu;
+       else
+               buffer_size = data_size;
+
+       if (!(buf = malloc(buffer_size))) {
+               perror("Can't allocate data buffer");
+               exit(1);
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = SIG_IGN;
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sigaction(SIGCHLD, &sa, NULL);
+
+       openlog("l2test", LOG_PERROR | LOG_PID, LOG_LOCAL0);
+
+       switch (mode) {
+               case RECV:
+                       do_listen(recv_mode);
+                       break;
+
+               case CRECV:
+                       sk = do_connect(argv[optind]);
+                       if (sk < 0)
+                               exit(1);
+                       recv_mode(sk);
+                       break;
+
+               case DUMP:
+                       do_listen(dump_mode);
+                       break;
+
+               case SEND:
+                       sk = do_connect(argv[optind]);
+                       if (sk < 0)
+                               exit(1);
+                       send_mode(sk);
+                       break;
+
+               case LSEND:
+                       do_listen(send_mode);
+                       break;
+
+               case RECONNECT:
+                       reconnect_mode(argv[optind]);
+                       break;
+
+               case MULTY:
+                       multi_connect_mode(argc - optind, argv + optind);
+                       break;
+
+               case CONNECT:
+                       connect_mode(argv[optind]);
+                       break;
+
+               case SENDDUMP:
+                       sk = do_connect(argv[optind]);
+                       if (sk < 0)
+                               exit(1);
+                       senddump_mode(sk);
+                       break;
+
+               case LSENDDUMP:
+                       do_listen(senddump_mode);
+                       break;
+
+               case LSENDRECV:
+                       do_listen(send_and_recv_mode);
+                       break;
+
+               case CSENDRECV:
+                       sk = do_connect(argv[optind]);
+                       if (sk < 0)
+                               exit(1);
+
+                       send_and_recv_mode(sk);
+                       break;
+
+               case INFOREQ:
+                       info_request(argv[optind]);
+                       exit(0);
+
+               case PAIRING:
+                       do_pairing(argv[optind]);
+                       exit(0);
+       }
+
+       syslog(LOG_INFO, "Exit");
+
+       closelog();
+
+       return 0;
+}
diff --git a/test/list-devices b/test/list-devices
new file mode 100755 (executable)
index 0000000..7ef6511
--- /dev/null
@@ -0,0 +1,95 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                               "org.bluez.Manager")
+
+def extract_objects(object_list):
+       list = ""
+       for object in object_list:
+               val = str(object)
+               list = list + val[val.rfind("/") + 1:] + " "
+       return list
+
+def extract_uuids(uuid_list):
+       list = ""
+       for uuid in uuid_list:
+               if (uuid.endswith("-0000-1000-8000-00805f9b34fb")):
+                       if (uuid.startswith("0000")):
+                               val = "0x" + uuid[4:8]
+                       else:
+                               val = "0x" + uuid[0:8]
+               else:
+                       val = str(uuid)
+               list = list + val + " "
+       return list
+
+adapter_list = manager.ListAdapters()
+
+for i in adapter_list:
+       adapter = dbus.Interface(bus.get_object("org.bluez", i),
+                                                       "org.bluez.Adapter")
+       print("[ " + i + " ]")
+
+       properties  = adapter.GetProperties()
+       for key in properties.keys():
+               value = properties[key]
+               if (key == "Devices"):
+                       list = extract_objects(value)
+                       print("    %s = %s" % (key, list))
+               elif (key == "UUIDs"):
+                       list = extract_uuids(value)
+                       print("    %s = %s" % (key, list))
+               else:
+                       print("    %s = %s" % (key, value))
+
+       try:
+               device_list = properties["Devices"]
+       except:
+               device_list = []
+
+       for n in device_list:
+               device = dbus.Interface(bus.get_object("org.bluez", n),
+                                                       "org.bluez.Device")
+               print("    [ " + n + " ]")
+
+               properties = device.GetProperties()
+               for key in properties.keys():
+                       value = properties[key]
+                       if (key == "Nodes"):
+                               list = extract_objects(value)
+                               print("        %s = %s" % (key, list))
+                       elif (key == "UUIDs"):
+                               list = extract_uuids(value)
+                               print("        %s = %s" % (key, list))
+                       elif (key == "Class"):
+                               print("        %s = 0x%06x" % (key, value))
+                       elif (key == "Vendor"):
+                               print("        %s = 0x%04x" % (key, value))
+                       elif (key == "Product"):
+                               print("        %s = 0x%04x" % (key, value))
+                       elif (key == "Version"):
+                               print("        %s = 0x%04x" % (key, value))
+                       else:
+                               print("        %s = %s" % (key, value))
+
+               try:
+                       node_list = properties["Nodes"]
+               except:
+                       node_list = []
+
+               for x in node_list:
+                       node = dbus.Interface(bus.get_object("org.bluez", x),
+                                                       "org.bluez.Node")
+                       print("        [ " + x + " ]")
+
+                       properties = node.GetProperties()
+                       for key in properties.keys():
+                               print("            %s = %s" % (key, properties[key]))
+
+       print("")
diff --git a/test/lmptest.c b/test/lmptest.c
new file mode 100644 (file)
index 0000000..549ae12
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#if 0
+#define OCF_ERICSSON_SEND_LMP          0x0021
+typedef struct {
+       uint16_t handle;
+       uint8_t  length;
+       uint8_t  data[17];
+} __attribute__ ((packed)) ericsson_send_lmp_cp;
+#define ERICSSON_SEND_LMP_CP_SIZE 20
+
+static int ericsson_send_lmp(int dd, uint16_t handle, uint8_t length, uint8_t *data)
+{
+       struct hci_request rq;
+       ericsson_send_lmp_cp cp;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = htobs(handle);
+       cp.length = length;
+       memcpy(cp.data, data, length);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = OCF_ERICSSON_SEND_LMP;
+       rq.cparam = &cp;
+       rq.clen   = ERICSSON_SEND_LMP_CP_SIZE;
+       rq.rparam = NULL;
+       rq.rlen   = 0;
+
+       if (hci_send_req(dd, &rq, 1000) < 0)
+               return -1;
+
+       return 0;
+}
+#endif
+
+#define OCF_ERICSSON_WRITE_EVENTS      0x0043
+typedef struct {
+       uint8_t mask;
+       uint8_t opcode;
+       uint8_t opcode_ext;
+} __attribute__ ((packed)) ericsson_write_events_cp;
+#define ERICSSON_WRITE_EVENTS_CP_SIZE 3
+
+static int ericsson_write_events(int dd, uint8_t mask)
+{
+       struct hci_request rq;
+       ericsson_write_events_cp cp;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.mask = mask;
+       cp.opcode = 0x00;
+       cp.opcode_ext = 0x00;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = OCF_ERICSSON_WRITE_EVENTS;
+       rq.cparam = &cp;
+       rq.clen   = ERICSSON_WRITE_EVENTS_CP_SIZE;
+       rq.rparam = NULL;
+       rq.rlen   = 0;
+
+       if (hci_send_req(dd, &rq, 1000) < 0)
+               return -1;
+
+       return 0;
+}
+
+static void usage(void)
+{
+       printf("lmptest - Utility for testing special LMP functions\n\n");
+       printf("Usage:\n"
+               "\tlmptest [-i <dev>]\n");
+}
+
+static struct option main_options[] = {
+       { "device",     1, 0, 'i' },
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       struct hci_version ver;
+       int dd, opt, dev = 0;
+
+       while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'i':
+                       dev = hci_devid(optarg);
+                       if (dev < 0) {
+                               perror("Invalid device");
+                               exit(1);
+                       }
+                       break;
+
+               case 'h':
+               default:
+                       usage();
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       dd = hci_open_dev(dev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (hci_read_local_version(dd, &ver, 1000) < 0) {
+               fprintf(stderr, "Can't read version for hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               hci_close_dev(dd);
+               exit(1);
+       }
+
+       if (ver.manufacturer != 37 && ver.manufacturer != 48) {
+               fprintf(stderr, "Can't find supported device hci%d: %s (%d)\n",
+                                               dev, strerror(ENOSYS), ENOSYS);
+               hci_close_dev(dd);
+               exit(1);
+       }
+
+       if (ericsson_write_events(dd, 0x03) < 0) {
+               fprintf(stderr, "Can't activate events for hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               hci_close_dev(dd);
+               exit(1);
+       }
+
+       hci_close_dev(dd);
+
+       return 0;
+}
diff --git a/test/monitor-bluetooth b/test/monitor-bluetooth
new file mode 100755 (executable)
index 0000000..4a598e1
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import gobject
+
+import dbus
+import dbus.mainloop.glib
+
+def property_changed(name, value, path, interface):
+       iface = interface[interface.rfind(".") + 1:]
+       val = str(value)
+       print("{%s.PropertyChanged} [%s] %s = %s" % (iface, path, name, val))
+
+def object_signal(value, path, interface, member):
+       iface = interface[interface.rfind(".") + 1:]
+       val = str(value)
+       print("{%s.%s} [%s] Path = %s" % (iface, member, path, val))
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       bus.add_signal_receiver(property_changed, bus_name="org.bluez",
+                                       signal_name = "PropertyChanged",
+                                               path_keyword="path",
+                                               interface_keyword="interface")
+
+       bus.add_signal_receiver(object_signal, bus_name="org.bluez",
+                                       signal_name = "AdapterAdded",
+                                               path_keyword="path",
+                                               member_keyword="member",
+                                               interface_keyword="interface")
+       bus.add_signal_receiver(object_signal, bus_name="org.bluez",
+                                       signal_name = "AdapterRemoved",
+                                               path_keyword="path",
+                                               member_keyword="member",
+                                               interface_keyword="interface")
+       bus.add_signal_receiver(object_signal, bus_name="org.bluez",
+                                       signal_name = "DefaultAdapterChanged",
+                                               path_keyword="path",
+                                               member_keyword="member",
+                                               interface_keyword="interface")
+
+       bus.add_signal_receiver(object_signal, bus_name="org.bluez",
+                                       signal_name = "DeviceCreated",
+                                               path_keyword="path",
+                                               member_keyword="member",
+                                               interface_keyword="interface")
+       bus.add_signal_receiver(object_signal, bus_name="org.bluez",
+                                       signal_name = "DeviceRemoved",
+                                               path_keyword="path",
+                                               member_keyword="member",
+                                               interface_keyword="interface")
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/mpris-player.c b/test/mpris-player.c
new file mode 100644 (file)
index 0000000..a2c4cc6
--- /dev/null
@@ -0,0 +1,993 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <getopt.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+static volatile sig_atomic_t __io_canceled = 0;
+static volatile sig_atomic_t __io_terminated = 0;
+static char *adapter = NULL;
+static DBusConnection *sys = NULL;
+static DBusConnection *session = NULL;
+
+static void sig_term(int sig)
+{
+       __io_canceled = 1;
+}
+
+static DBusMessage *get_all(DBusConnection *conn, const char *name)
+{
+       DBusMessage *msg, *reply;
+       DBusError err;
+       const char *iface = "org.mpris.MediaPlayer2.Player";
+
+       msg = dbus_message_new_method_call(name, "/org/mpris/MediaPlayer2",
+                                       DBUS_INTERFACE_PROPERTIES, "GetAll");
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return NULL;
+       }
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface,
+                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               fprintf(stderr, "Can't get default adapter\n");
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               return NULL;
+       }
+
+       return reply;
+}
+
+static void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+       DBusMessageIter value;
+       char sig[2] = { type, '\0' };
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
+
+       dbus_message_iter_append_basic(&value, type, val);
+
+       dbus_message_iter_close_container(iter, &value);
+}
+
+static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
+                                                               void *val)
+{
+       DBusMessageIter entry;
+
+       if (type == DBUS_TYPE_STRING) {
+               const char *str = *((const char **) val);
+               if (str == NULL)
+                       return;
+       }
+
+       dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+                                                       NULL, &entry);
+
+       dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+       append_variant(&entry, type, val);
+
+       dbus_message_iter_close_container(dict, &entry);
+}
+
+static dbus_bool_t emit_property_changed(DBusConnection *conn,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *name,
+                                       int type, void *value)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter;
+       dbus_bool_t result;
+
+       signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+
+       if (!signal) {
+               fprintf(stderr, "Unable to allocate new %s.PropertyChanged"
+                                                       " signal", interface);
+               return FALSE;
+       }
+
+       dbus_message_iter_init_append(signal, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+       append_variant(&iter, type, value);
+
+       result = dbus_connection_send(conn, signal, NULL);
+       dbus_message_unref(signal);
+
+       return result;
+}
+
+static int parse_property(DBusConnection *conn, const char *path,
+                                               const char *key,
+                                               DBusMessageIter *entry,
+                                               DBusMessageIter *properties)
+{
+       DBusMessageIter var;
+
+       printf("property %s found\n", key);
+
+       if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+               return -EINVAL;
+
+       dbus_message_iter_recurse(entry, &var);
+
+       if (strcasecmp(key, "PlaybackStatus") == 0) {
+               const char *value;
+
+               if (dbus_message_iter_get_arg_type(&var) !=
+                                                       DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               if (properties)
+                       dict_append_entry(properties, "Status",
+                                               DBUS_TYPE_STRING, &value);
+               else
+                       emit_property_changed(sys, path,
+                                       "org.bluez.MediaPlayer", "Status",
+                                       DBUS_TYPE_STRING, &value);
+       } else if (strcasecmp(key, "Position") == 0) {
+               int64_t usec, msec;
+
+               if (dbus_message_iter_get_arg_type(&var) !=
+                                                       DBUS_TYPE_INT64)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&var, &usec);
+               msec = usec / 1000;
+
+               if (properties)
+                       dict_append_entry(properties, "Position",
+                                               DBUS_TYPE_UINT32, &msec);
+               else
+                       emit_property_changed(sys, path,
+                                       "org.bluez.MediaPlayer", "Position",
+                                       DBUS_TYPE_UINT32, &msec);
+       } else if (strcasecmp(key, "Shuffle") == 0) {
+               dbus_bool_t value;
+               const char *str;
+
+               if (dbus_message_iter_get_arg_type(&var) !=
+                                                       DBUS_TYPE_BOOLEAN)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               str = value ? "on" : "off";
+               if (properties)
+                       dict_append_entry(properties, "Shuffle",
+                                               DBUS_TYPE_STRING, &str);
+               else
+                       emit_property_changed(sys, path,
+                                       "org.bluez.MediaPlayer", "Shuffle",
+                                       DBUS_TYPE_UINT32, &str);
+       }
+
+       return 0;
+}
+
+static int parse_properties(DBusConnection *conn, const char *path,
+                                               DBusMessageIter *args,
+                                               DBusMessageIter *properties)
+{
+       DBusMessageIter dict;
+       int ctype;
+
+       ctype = dbus_message_iter_get_arg_type(args);
+       if (ctype != DBUS_TYPE_ARRAY)
+               return -EINVAL;
+
+       dbus_message_iter_recurse(args, &dict);
+
+       while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+                                                       DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key;
+
+               if (ctype != DBUS_TYPE_DICT_ENTRY)
+                       return -EINVAL;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+
+               if (parse_property(conn, path, key, &entry, properties) < 0)
+                       return -EINVAL;
+
+               dbus_message_iter_next(&dict);
+       }
+
+       return 0;
+}
+
+static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
+                                               DBusMessageIter *metadata)
+{
+       DBusMessageIter var;
+
+       printf("metadata %s found\n", key);
+
+       if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+               return -EINVAL;
+
+       dbus_message_iter_recurse(entry, &var);
+
+       if (strcasecmp(key, "xesam:title") == 0) {
+               const char *value;
+
+               if (dbus_message_iter_get_arg_type(&var) !=
+                                                       DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&var, &value);
+               dict_append_entry(metadata, "Title", DBUS_TYPE_STRING,
+                                                               &value);
+       } else if (strcasecmp(key, "xesam:artist") == 0) {
+               const char *value;
+               DBusMessageIter array;
+
+               if (dbus_message_iter_get_arg_type(&var) !=
+                                                       DBUS_TYPE_ARRAY)
+                       return -EINVAL;
+
+               dbus_message_iter_recurse(&var, &array);
+
+               if (dbus_message_iter_get_arg_type(&array) !=
+                                                       DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&array, &value);
+               dict_append_entry(metadata, "Artist", DBUS_TYPE_STRING,
+                                                               &value);
+       } else if (strcasecmp(key, "xesam:album") == 0) {
+               const char *value;
+
+               if (dbus_message_iter_get_arg_type(&var) !=
+                                                       DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&var, &value);
+               dict_append_entry(metadata, "Album", DBUS_TYPE_STRING,
+                                                               &value);
+       } else if (strcasecmp(key, "xesam:genre") == 0) {
+               const char *value;
+               DBusMessageIter array;
+
+               if (dbus_message_iter_get_arg_type(&var) !=
+                                                       DBUS_TYPE_ARRAY)
+                       return -EINVAL;
+
+               dbus_message_iter_recurse(&var, &array);
+
+               if (dbus_message_iter_get_arg_type(&array) !=
+                                                       DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&array, &value);
+               dict_append_entry(metadata, "Genre", DBUS_TYPE_STRING,
+                                                               &value);
+       } else if (strcasecmp(key, "mpris:length") == 0) {
+               int64_t usec, msec;
+
+               if (dbus_message_iter_get_arg_type(&var) !=
+                                                       DBUS_TYPE_INT64)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&var, &usec);
+               msec = usec / 1000;
+
+               dict_append_entry(metadata, "Duration", DBUS_TYPE_UINT32,
+                                                               &msec);
+       } else if (strcasecmp(key, "xesam:trackNumber") == 0) {
+               int32_t value;
+
+               if (dbus_message_iter_get_arg_type(&var) !=
+                                                       DBUS_TYPE_INT32)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&var, &value);
+
+               dict_append_entry(metadata, "Number", DBUS_TYPE_UINT32,
+                                                               &value);
+       }
+
+       return 0;
+}
+
+static int parse_track(DBusMessageIter *args, DBusMessageIter *metadata)
+{
+       DBusMessageIter var, dict;
+       int ctype;
+
+       ctype = dbus_message_iter_get_arg_type(args);
+       if (ctype != DBUS_TYPE_VARIANT)
+               return -EINVAL;
+
+       dbus_message_iter_recurse(args, &var);
+
+       ctype = dbus_message_iter_get_arg_type(&var);
+       if (ctype != DBUS_TYPE_ARRAY)
+               return -EINVAL;
+
+       dbus_message_iter_recurse(&var, &dict);
+
+       while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+                                                       DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key;
+
+               if (ctype != DBUS_TYPE_DICT_ENTRY)
+                       return -EINVAL;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+
+               if (parse_metadata_entry(&entry, key, metadata) < 0)
+                       return -EINVAL;
+
+               dbus_message_iter_next(&dict);
+       }
+
+       return 0;
+}
+
+static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata)
+{
+       DBusMessageIter dict;
+       int ctype;
+
+       ctype = dbus_message_iter_get_arg_type(args);
+       if (ctype != DBUS_TYPE_ARRAY)
+               return -EINVAL;
+
+       dbus_message_iter_recurse(args, &dict);
+
+       while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+                                                       DBUS_TYPE_INVALID) {
+               DBusMessageIter entry;
+               const char *key;
+
+               if (ctype != DBUS_TYPE_DICT_ENTRY)
+                       return -EINVAL;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+
+               if (strcasecmp(key, "Metadata") == 0)
+                       return parse_track(&entry, metadata);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       return -EINVAL;
+}
+
+static char *sender2path(const char *sender)
+{
+       char *path;
+
+       path = g_strconcat("/", sender, NULL);
+       return g_strdelimit(path, ":.", '_');
+}
+
+static DBusHandlerResult player_message(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       if (dbus_message_is_method_call(msg, "org.bluez.MediaPlayer",
+                                                               "Release")) {
+               printf("Release\n");
+               exit(1);
+       }
+
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static const DBusObjectPathVTable player_table = {
+       .message_function = player_message,
+};
+
+static void add_player(DBusConnection *conn, const char *name,
+                                                       const char *sender)
+{
+       DBusMessage *reply = get_all(conn, name);
+       DBusMessage *msg;
+       DBusMessageIter iter, args, properties, metadata;
+       DBusError err;
+       char *path;
+
+       if (!reply)
+               return;
+
+       msg = dbus_message_new_method_call("org.bluez", adapter,
+                                       "org.bluez.Media",
+                                       "RegisterPlayer");
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return;
+       }
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       path = sender2path(sender);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &properties);
+
+       dbus_message_iter_init(reply, &args);
+
+       if (parse_properties(conn, path, &args, &properties) < 0)
+               goto done;
+
+       dbus_message_iter_close_container(&iter, &properties);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
+
+       dbus_message_iter_init(reply, &args);
+
+       if (parse_metadata(&args, &metadata) < 0)
+               goto done;
+
+       dbus_message_iter_close_container(&iter, &metadata);
+
+       dbus_message_unref(reply);
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(sys, msg, -1, &err);
+       if (!reply) {
+               fprintf(stderr, "Can't register player\n");
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               goto done;
+       }
+
+       if (!dbus_connection_register_object_path(sys, path, &player_table,
+                                                               NULL))
+               fprintf(stderr, "Can't register object path for agent\n");
+
+done:
+       if (reply)
+               dbus_message_unref(reply);
+       dbus_message_unref(msg);
+       g_free(path);
+}
+
+static void remove_player(DBusConnection *conn, const char *sender)
+{
+       DBusMessage *msg;
+       char *path;
+
+       msg = dbus_message_new_method_call("org.bluez", adapter,
+                                       "org.bluez.Media",
+                                       "UnregisterPlayer");
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return;
+       }
+
+       path = sender2path(sender);
+       dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
+                                       DBUS_TYPE_INVALID);
+
+       dbus_connection_send(sys, msg, NULL);
+
+       dbus_message_unref(msg);
+       g_free(path);
+}
+
+static DBusHandlerResult properties_changed(DBusConnection *conn,
+                                                       DBusMessage *msg)
+{
+       DBusMessage *signal;
+       DBusMessageIter iter, entry, metadata;
+       const char *iface;
+       char *path;
+
+       dbus_message_iter_init(msg, &iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       dbus_message_iter_get_basic(&iter, &iface);
+
+       printf("PropertiesChanged interface %s\n", iface);
+
+       dbus_message_iter_next(&iter);
+
+       path = sender2path(dbus_message_get_sender(msg));
+       parse_properties(conn, path, &iter, NULL);
+
+       signal = dbus_message_new_signal(path, "org.bluez.MediaPlayer",
+                                                       "TrackChanged");
+       if (!signal) {
+               fprintf(stderr, "Unable to allocate new PropertyChanged"
+                                                       " signal\n");
+               goto err;
+       }
+
+       dbus_message_iter_init_append(signal, &entry);
+
+       dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
+
+       dbus_message_iter_init(msg, &iter);
+       dbus_message_iter_next(&iter);
+
+       if (parse_metadata(&iter, &metadata) < 0)
+               goto err;
+
+       dbus_message_iter_close_container(&entry, &metadata);
+
+       dbus_connection_send(sys, signal, NULL);
+       dbus_message_unref(signal);
+       g_free(path);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+
+err:
+       if (signal)
+               dbus_message_unref(signal);
+       g_free(path);
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult session_filter(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       const char *name, *old, *new;
+
+       if (dbus_message_is_signal(msg, DBUS_INTERFACE_PROPERTIES,
+                                               "PropertiesChanged"))
+               return properties_changed(conn, msg);
+
+       if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
+                                               "NameOwnerChanged"))
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_STRING, &name,
+                                       DBUS_TYPE_STRING, &old,
+                                       DBUS_TYPE_STRING, &new,
+                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (!g_str_has_prefix(name, "org.mpris"))
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (*new == '\0') {
+               printf("player %s at %s disappear\n", name, old);
+               remove_player(conn, old);
+       } else {
+               printf("player %s at %s found\n", name, new);
+               add_player(conn, name, new);
+       }
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult system_filter(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
+{
+       const char *name, *old, *new;
+
+       if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
+                                               "NameOwnerChanged"))
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (!dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_STRING, &name,
+                                       DBUS_TYPE_STRING, &old,
+                                       DBUS_TYPE_STRING, &new,
+                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+
+       if (!strcmp(name, "org.bluez") && *new == '\0') {
+               fprintf(stderr, "bluetoothd disconnected\n");
+               __io_terminated = 1;
+       }
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static char *get_default_adapter(DBusConnection *conn)
+{
+       DBusMessage *msg, *reply;
+       DBusError err;
+       const char *reply_path;
+       char *path;
+
+       msg = dbus_message_new_method_call("org.bluez", "/",
+                                       "org.bluez.Manager", "DefaultAdapter");
+
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return NULL;
+       }
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               fprintf(stderr, "Can't get default adapter\n");
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               return NULL;
+       }
+
+       if (!dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_OBJECT_PATH, &reply_path,
+                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Can't get reply arguments\n");
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       path = strdup(reply_path);
+
+       dbus_message_unref(reply);
+
+       dbus_connection_flush(conn);
+
+       return path;
+}
+
+static char *get_adapter(DBusConnection *conn, const char *adapter)
+{
+       DBusMessage *msg, *reply;
+       DBusError err;
+       const char *reply_path;
+       char *path;
+
+       if (!adapter)
+               return get_default_adapter(conn);
+
+       msg = dbus_message_new_method_call("org.bluez", "/",
+                                       "org.bluez.Manager", "FindAdapter");
+
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return NULL;
+       }
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &adapter,
+                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               fprintf(stderr, "Can't find adapter %s\n", adapter);
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               return NULL;
+       }
+
+       if (!dbus_message_get_args(reply, &err,
+                                       DBUS_TYPE_OBJECT_PATH, &reply_path,
+                                       DBUS_TYPE_INVALID)) {
+               fprintf(stderr, "Can't get reply arguments\n");
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       path = strdup(reply_path);
+
+       dbus_message_unref(reply);
+
+       dbus_connection_flush(conn);
+
+       return path;
+}
+
+static char *get_name_owner(DBusConnection *conn, const char *name)
+{
+       DBusMessage *msg, *reply;
+       DBusError err;
+       char *owner;
+
+       msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+                                       DBUS_INTERFACE_DBUS, "GetNameOwner");
+
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return NULL;
+       }
+
+       dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
+                                                       DBUS_TYPE_INVALID);
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               fprintf(stderr, "Can't find adapter %s\n", adapter);
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               return NULL;
+       }
+
+       if (!dbus_message_get_args(reply, NULL,
+                                       DBUS_TYPE_STRING, &owner,
+                                       DBUS_TYPE_INVALID)) {
+               dbus_message_unref(reply);
+               return NULL;
+       }
+
+       owner = g_strdup(owner);
+
+       dbus_message_unref(reply);
+
+       dbus_connection_flush(conn);
+
+       return owner;
+}
+
+static void parse_list_names(DBusConnection *conn, DBusMessageIter *args)
+{
+       DBusMessageIter array;
+       int ctype;
+
+       ctype = dbus_message_iter_get_arg_type(args);
+       if (ctype != DBUS_TYPE_ARRAY)
+               return;
+
+       dbus_message_iter_recurse(args, &array);
+
+       while ((ctype = dbus_message_iter_get_arg_type(&array)) !=
+                                                       DBUS_TYPE_INVALID) {
+               const char *name;
+               char *owner;
+
+               if (ctype != DBUS_TYPE_STRING)
+                       goto next;
+
+               dbus_message_iter_get_basic(&array, &name);
+
+               if (!g_str_has_prefix(name, "org.mpris"))
+                       goto next;
+
+               owner = get_name_owner(conn, name);
+
+               if (owner == NULL)
+                       goto next;
+
+               add_player(conn, name, owner);
+
+               g_free(owner);
+next:
+               dbus_message_iter_next(&array);
+       }
+}
+
+static void list_names(DBusConnection *conn)
+{
+       DBusMessage *msg, *reply;
+       DBusMessageIter iter;
+       DBusError err;
+
+       msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+                                       DBUS_INTERFACE_DBUS, "ListNames");
+
+       if (!msg) {
+               fprintf(stderr, "Can't allocate new method call\n");
+               return;
+       }
+
+       dbus_error_init(&err);
+
+       reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+       dbus_message_unref(msg);
+
+       if (!reply) {
+               fprintf(stderr, "Can't find adapter %s\n", adapter);
+               if (dbus_error_is_set(&err)) {
+                       fprintf(stderr, "%s\n", err.message);
+                       dbus_error_free(&err);
+               }
+               return;
+       }
+
+       dbus_message_iter_init(reply, &iter);
+
+       parse_list_names(conn, &iter);
+
+       dbus_message_unref(reply);
+
+       dbus_connection_flush(conn);
+}
+
+static void usage(void)
+{
+       printf("Bluetooth player ver %s\n\n", VERSION);
+
+       printf("Usage:\n"
+               "\tplayer [--adapter adapter id]\n"
+               "\n");
+}
+
+static struct option main_options[] = {
+       { "adapter",    1, 0, 'a' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       struct sigaction sa;
+       char *adapter_id = NULL;
+       char match[128];
+       int opt;
+
+       while ((opt = getopt_long(argc, argv, "+a,h", main_options, NULL)) != EOF) {
+               switch(opt) {
+               case '1':
+                       adapter_id = optarg;
+                       break;
+               case 'h':
+                       usage();
+                       exit(0);
+               default:
+                       exit(1);
+               }
+       }
+
+       sys = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (!sys) {
+               fprintf(stderr, "Can't get on system bus");
+               exit(1);
+       }
+
+       adapter = get_adapter(sys, adapter_id);
+       if (!adapter)
+               exit(1);
+
+       if (!dbus_connection_add_filter(sys, system_filter, NULL, NULL)) {
+               fprintf(stderr, "Can't add signal filter");
+               exit(1);
+       }
+
+       snprintf(match, sizeof(match),
+                       "interface=%s,member=NameOwnerChanged,arg0=%s",
+                       DBUS_INTERFACE_DBUS, "org.bluez");
+
+       dbus_bus_add_match(sys, match, NULL);
+
+       session = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+       if (!session) {
+               fprintf(stderr, "Can't get on session bus");
+               exit(1);
+       }
+
+       if (!dbus_connection_add_filter(session, session_filter, NULL, NULL)) {
+               fprintf(stderr, "Can't add signal filter");
+               exit(1);
+       }
+
+       snprintf(match, sizeof(match),
+                       "interface=%s,member=NameOwnerChanged",
+                       DBUS_INTERFACE_DBUS);
+
+       dbus_bus_add_match(session, match, NULL);
+
+       snprintf(match, sizeof(match),
+                       "interface=%s,member=PropertiesChanged,arg0=%s",
+                       DBUS_INTERFACE_PROPERTIES,
+                       "org.mpris.MediaPlayer2.Player");
+
+       list_names(session);
+
+       dbus_bus_add_match(session, match, NULL);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       while (!__io_canceled && !__io_terminated) {
+               if (dbus_connection_read_write_dispatch(sys, 500) != TRUE)
+                       break;
+               if (dbus_connection_read_write_dispatch(session, 500) != TRUE)
+                       break;
+       }
+
+       dbus_connection_unref(sys);
+
+       return 0;
+}
diff --git a/test/rctest.1 b/test/rctest.1
new file mode 100644 (file)
index 0000000..dfedbef
--- /dev/null
@@ -0,0 +1,90 @@
+.TH RCTEST 1 "Jul 6 2009" BlueZ ""
+.SH NAME
+rctest \- RFCOMM testing
+.SH SYNOPSIS
+.B rctest
+<\fImode\fR> [\fIoptions\fR] [\fIbdaddr\fR]
+
+.SH DESCRIPTION
+.LP
+.B
+rctest
+is used to test RFCOMM communications on the BlueZ stack
+
+.SH MODES
+.TP
+.B -r
+listen and receive
+.TP
+.B -w
+listen and send
+.TP
+.B -d
+listen and dump incoming data
+.TP
+.B -s
+connect and send
+.TP
+.B -u
+connect and receive
+.TP
+.B -n
+connect and be silent
+.TP
+.B -c
+connect, disconnect, connect, ...
+.TP
+.B -m
+multiple connects
+
+.SH OPTIONS
+.TP
+.BI -b\  bytes
+send/receive \fIbytes\fR bytes
+.TP
+.BI -i\  device
+select the specified \fIdevice\fR
+.TP
+.BI -P\  channel
+select the specified \fIchannel\fR
+.TP
+.BI -U\  uuid
+select the specified \fIuuid\fR
+.TP
+.BI -L\  seconds
+enable SO_LINGER options for \fIseconds\fR
+.TP
+.BI -W\  seconds
+enable deferred setup for \fIseconds\fR
+.TP
+.BI -B\  filename
+use data packets from \fIfilename\fR
+.TP
+.BI -N\  num
+send \fInum\fR frames
+.TP
+.BI -C\  num
+send \fInum\fR frames before delay (default: 1)
+.TP
+.BI -D\  milliseconds
+delay \fImilliseconds\fR after sending \fInum\fR frames (default: 0)
+.TP
+.B -A
+request authentication
+.TP
+.B -E
+request encryption
+.TP
+.B -S
+secure connection
+.TP
+.B -M
+become master
+.TP
+.B -T
+enable timestamps
+
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org> and Maxim Krasnyansky
+<maxk@qualcomm.com>, man page by Filippo Giunchedi <filippo@debian.org>
+.PP
diff --git a/test/rctest.c b/test/rctest.c
new file mode 100644 (file)
index 0000000..4d7c90a
--- /dev/null
@@ -0,0 +1,814 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+/* Test modes */
+enum {
+       SEND,
+       RECV,
+       RECONNECT,
+       MULTY,
+       DUMP,
+       CONNECT,
+       CRECV,
+       LSEND
+};
+
+static unsigned char *buf;
+
+/* Default data size */
+static long data_size = 127;
+static long num_frames = -1;
+
+/* Default number of consecutive frames before the delay */
+static int count = 1;
+
+/* Default delay after sending count number of frames */
+static unsigned long delay = 0;
+
+/* Default addr and channel */
+static bdaddr_t bdaddr;
+static uint16_t uuid = 0x0000;
+static uint8_t channel = 10;
+
+static char *filename = NULL;
+
+static int master = 0;
+static int auth = 0;
+static int encrypt = 0;
+static int secure = 0;
+static int socktype = SOCK_STREAM;
+static int linger = 0;
+static int timestamp = 0;
+static int defer_setup = 0;
+static int priority = -1;
+
+static float tv2fl(struct timeval tv)
+{
+       return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static uint8_t get_channel(const char *svr, uint16_t uuid)
+{
+       sdp_session_t *sdp;
+       sdp_list_t *srch, *attrs, *rsp;
+       uuid_t svclass;
+       uint16_t attr;
+       bdaddr_t dst;
+       uint8_t channel = 0;
+       int err;
+
+       str2ba(svr, &dst);
+
+       sdp = sdp_connect(&bdaddr, &dst, SDP_RETRY_IF_BUSY);
+       if (!sdp)
+               return 0;
+
+       sdp_uuid16_create(&svclass, uuid);
+       srch = sdp_list_append(NULL, &svclass);
+
+       attr = SDP_ATTR_PROTO_DESC_LIST;
+       attrs = sdp_list_append(NULL, &attr);
+
+       err = sdp_service_search_attr_req(sdp, srch,
+                                       SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+       if (err)
+               goto done;
+
+       for (; rsp; rsp = rsp->next) {
+               sdp_record_t *rec = (sdp_record_t *) rsp->data;
+               sdp_list_t *protos;
+
+               if (!sdp_get_access_protos(rec, &protos)) {
+                       channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+                       if (channel > 0)
+                               break;
+               }
+       }
+
+done:
+       sdp_close(sdp);
+
+       return channel;
+}
+
+static int do_connect(const char *svr)
+{
+       struct sockaddr_rc addr;
+       struct rfcomm_conninfo conn;
+       socklen_t optlen;
+       int sk, opt;
+
+       if (uuid != 0x0000)
+               channel = get_channel(svr, uuid);
+
+       if (channel == 0) {
+               syslog(LOG_ERR, "Can't get channel number");
+               return -1;
+       }
+
+       /* Create socket */
+       sk = socket(PF_BLUETOOTH, socktype, BTPROTO_RFCOMM);
+       if (sk < 0) {
+               syslog(LOG_ERR, "Can't create socket: %s (%d)",
+                                                       strerror(errno), errno);
+               return -1;
+       }
+
+       /* Bind to local address */
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, &bdaddr);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+#if 0
+       /* Enable SO_TIMESTAMP */
+       if (timestamp) {
+               int t = 1;
+
+               if (setsockopt(sk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+                       syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+       }
+#endif
+
+       /* Enable SO_LINGER */
+       if (linger) {
+               struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+               if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+                       syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+       }
+
+       /* Set link mode */
+       opt = 0;
+       if (master)
+               opt |= RFCOMM_LM_MASTER;
+       if (auth)
+               opt |= RFCOMM_LM_AUTH;
+       if (encrypt)
+               opt |= RFCOMM_LM_ENCRYPT;
+       if (secure)
+               opt |= RFCOMM_LM_SECURE;
+
+       if (opt && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0) {
+               syslog(LOG_ERR, "Can't set RFCOMM link mode: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Connect to remote device */
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       str2ba(svr, &addr.rc_bdaddr);
+       addr.rc_channel = channel;
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               syslog(LOG_ERR, "Can't connect: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Get connection information */
+       memset(&conn, 0, sizeof(conn));
+       optlen = sizeof(conn);
+
+       if (getsockopt(sk, SOL_RFCOMM, RFCOMM_CONNINFO, &conn, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get RFCOMM connection information: %s (%d)",
+                                                       strerror(errno), errno);
+               //goto error;
+       }
+
+       if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY, &priority,
+                                               sizeof(priority)) < 0) {
+               syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       if (getsockopt(sk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x, "
+                       "priority %d]", conn.hci_handle, conn.dev_class[2],
+                       conn.dev_class[1], conn.dev_class[0], opt);
+
+       return sk;
+
+error:
+       close(sk);
+       return -1;
+}
+
+static void do_listen(void (*handler)(int sk))
+{
+       struct sockaddr_rc addr;
+       struct rfcomm_conninfo conn;
+       socklen_t optlen;
+       int sk, nsk, opt;
+       char ba[18];
+
+       /* Create socket */
+       sk = socket(PF_BLUETOOTH, socktype, BTPROTO_RFCOMM);
+       if (sk < 0) {
+               syslog(LOG_ERR, "Can't create socket: %s (%d)",
+                                                       strerror(errno), errno);
+               exit(1);
+       }
+
+       /* Bind to local address */
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, &bdaddr);
+       addr.rc_channel = channel;
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Set link mode */
+       opt = 0;
+       if (master)
+               opt |= RFCOMM_LM_MASTER;
+       if (auth)
+               opt |= RFCOMM_LM_AUTH;
+       if (encrypt)
+               opt |= RFCOMM_LM_ENCRYPT;
+       if (secure)
+               opt |= RFCOMM_LM_SECURE;
+
+       if (opt && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0) {
+               syslog(LOG_ERR, "Can't set RFCOMM link mode: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Enable deferred setup */
+       opt = defer_setup;
+
+       if (opt && setsockopt(sk, SOL_BLUETOOTH, BT_DEFER_SETUP,
+                                               &opt, sizeof(opt)) < 0) {
+               syslog(LOG_ERR, "Can't enable deferred setup : %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Listen for connections */
+       if (listen(sk, 10)) {
+               syslog(LOG_ERR,"Can not listen on the socket: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Check for socket address */
+       memset(&addr, 0, sizeof(addr));
+       optlen = sizeof(addr);
+
+       if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get socket name: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       channel = addr.rc_channel;
+
+       syslog(LOG_INFO, "Waiting for connection on channel %d ...", channel);
+
+       while (1) {
+               memset(&addr, 0, sizeof(addr));
+               optlen = sizeof(addr);
+
+               nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+               if (nsk < 0) {
+                       syslog(LOG_ERR,"Accept failed: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+               if (fork()) {
+                       /* Parent */
+                       close(nsk);
+                       continue;
+               }
+               /* Child */
+               close(sk);
+
+               /* Get connection information */
+               memset(&conn, 0, sizeof(conn));
+               optlen = sizeof(conn);
+
+               if (getsockopt(nsk, SOL_RFCOMM, RFCOMM_CONNINFO, &conn, &optlen) < 0) {
+                       syslog(LOG_ERR, "Can't get RFCOMM connection information: %s (%d)",
+                                                       strerror(errno), errno);
+                       //close(nsk);
+                       //goto error;
+               }
+
+               if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY,
+                                       &priority, sizeof(priority)) < 0) {
+                       syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+                                               strerror(errno), errno);
+                       close(nsk);
+                       goto error;
+               }
+
+               optlen = sizeof(priority);
+               if (getsockopt(nsk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+                       syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+
+               ba2str(&addr.rc_bdaddr, ba);
+               syslog(LOG_INFO, "Connect from %s [handle %d, "
+                               "class 0x%02x%02x%02x, priority %d]",
+                               ba, conn.hci_handle, conn.dev_class[2],
+                               conn.dev_class[1], conn.dev_class[0], opt);
+
+#if 0
+               /* Enable SO_TIMESTAMP */
+               if (timestamp) {
+                       int t = 1;
+
+                       if (setsockopt(nsk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+                               syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+                                                       strerror(errno), errno);
+                               goto error;
+                       }
+               }
+#endif
+
+               /* Enable SO_LINGER */
+               if (linger) {
+                       struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+                       if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+                               syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+                                                       strerror(errno), errno);
+                               close(nsk);
+                               goto error;
+                       }
+               }
+
+               /* Handle deferred setup */
+               if (defer_setup) {
+                       syslog(LOG_INFO, "Waiting for %d seconds",
+                                                       abs(defer_setup) - 1);
+                       sleep(abs(defer_setup) - 1);
+
+                       if (defer_setup < 0) {
+                               close(nsk);
+                               goto error;
+                       }
+               }
+
+               handler(nsk);
+
+               syslog(LOG_INFO, "Disconnect: %m");
+               exit(0);
+       }
+
+       return;
+
+error:
+       close(sk);
+       exit(1);
+}
+
+static void dump_mode(int sk)
+{
+       int len;
+
+       syslog(LOG_INFO, "Receiving ...");
+       while ((len = read(sk, buf, data_size)) > 0)
+               syslog(LOG_INFO, "Recevied %d bytes", len);
+}
+
+static void recv_mode(int sk)
+{
+       struct timeval tv_beg, tv_end, tv_diff;
+       char ts[30];
+       long total;
+
+       syslog(LOG_INFO, "Receiving ...");
+
+       memset(ts, 0, sizeof(ts));
+
+       while (1) {
+               gettimeofday(&tv_beg,NULL);
+               total = 0;
+               while (total < data_size) {
+                       //uint32_t sq;
+                       //uint16_t l;
+                       int r;
+
+                       if ((r = recv(sk, buf, data_size, 0)) < 0) {
+                               if (r < 0)
+                                       syslog(LOG_ERR, "Read failed: %s (%d)",
+                                                       strerror(errno), errno);
+                               return;
+                       }
+
+                       if (timestamp) {
+                               struct timeval tv;
+
+                               if (ioctl(sk, SIOCGSTAMP, &tv) < 0) {
+                                       timestamp = 0;
+                                       memset(ts, 0, sizeof(ts));
+                               } else {
+                                       sprintf(ts, "[%ld.%ld] ",
+                                                       tv.tv_sec, tv.tv_usec);
+                               }
+                       }
+
+#if 0
+                       /* Check sequence */
+                       sq = btohl(*(uint32_t *) buf);
+                       if (seq != sq) {
+                               syslog(LOG_INFO, "seq missmatch: %d -> %d", seq, sq);
+                               seq = sq;
+                       }
+                       seq++;
+
+                       /* Check length */
+                       l = btohs(*(uint16_t *) (buf + 4));
+                       if (r != l) {
+                               syslog(LOG_INFO, "size missmatch: %d -> %d", r, l);
+                               continue;
+                       }
+
+                       /* Verify data */
+                       for (i = 6; i < r; i++) {
+                               if (buf[i] != 0x7f)
+                                       syslog(LOG_INFO, "data missmatch: byte %d 0x%2.2x", i, buf[i]);
+                       }
+#endif
+                       total += r;
+               }
+               gettimeofday(&tv_end,NULL);
+
+               timersub(&tv_end,&tv_beg,&tv_diff);
+
+               syslog(LOG_INFO,"%s%ld bytes in %.2f sec, %.2f kB/s", ts, total,
+                       tv2fl(tv_diff), (float)(total / tv2fl(tv_diff) ) / 1024.0);
+       }
+}
+
+static void do_send(int sk)
+{
+       uint32_t seq;
+       int i, fd, len;
+
+       syslog(LOG_INFO,"Sending ...");
+
+       if (filename) {
+               fd = open(filename, O_RDONLY);
+               if (fd < 0) {
+                       syslog(LOG_ERR, "Open failed: %s (%d)",
+                                                       strerror(errno), errno);
+                       exit(1);
+               }
+               len = read(fd, buf, data_size);
+               send(sk, buf, len, 0);
+               return;
+       } else {
+               for (i = 6; i < data_size; i++)
+                       buf[i] = 0x7f;
+       }
+
+       seq = 0;
+       while ((num_frames == -1) || (num_frames-- > 0)) {
+               *(uint32_t *) buf = htobl(seq);
+               *(uint16_t *) (buf + 4) = htobs(data_size);
+               seq++;
+
+               if (send(sk, buf, data_size, 0) <= 0) {
+                       syslog(LOG_ERR, "Send failed: %s (%d)",
+                                                       strerror(errno), errno);
+                       exit(1);
+               }
+
+               if (num_frames && delay && count && !(seq % count))
+                       usleep(delay);
+       }
+}
+
+static void send_mode(int sk)
+{
+       do_send(sk);
+
+       syslog(LOG_INFO, "Closing channel ...");
+       if (shutdown(sk, SHUT_RDWR) < 0)
+               syslog(LOG_INFO, "Close failed: %m");
+       else
+               syslog(LOG_INFO, "Done");
+}
+
+static void reconnect_mode(char *svr)
+{
+       while(1) {
+               int sk = do_connect(svr);
+               close(sk);
+       }
+}
+
+static void multi_connect_mode(int argc, char *argv[])
+{
+       int i, n, sk;
+
+       while (1) {
+               for (n = 0; n < argc; n++) {
+                       for (i = 0; i < count; i++) {
+                               if (fork())
+                                       continue;
+
+                               /* Child */
+                               sk = do_connect(argv[n]);
+                               usleep(500);
+                               close(sk);
+                               exit(0);
+                       }
+               }
+               sleep(4);
+       }
+}
+
+static void usage(void)
+{
+       printf("rctest - RFCOMM testing\n"
+               "Usage:\n");
+       printf("\trctest <mode> [options] [bdaddr]\n");
+       printf("Modes:\n"
+               "\t-r listen and receive\n"
+               "\t-w listen and send\n"
+               "\t-d listen and dump incoming data\n"
+               "\t-s connect and send\n"
+               "\t-u connect and receive\n"
+               "\t-n connect and be silent\n"
+               "\t-c connect, disconnect, connect, ...\n"
+               "\t-m multiple connects\n");
+
+       printf("Options:\n"
+               "\t[-b bytes] [-i device] [-P channel] [-U uuid]\n"
+               "\t[-L seconds] enabled SO_LINGER option\n"
+               "\t[-W seconds] enable deferred setup\n"
+               "\t[-B filename] use data packets from file\n"
+               "\t[-N num] number of frames to send\n"
+               "\t[-C num] send num frames before delay (default = 1)\n"
+               "\t[-D milliseconds] delay after sending num frames (default = 0)\n"
+               "\t[-Y priority] socket priority\n"
+               "\t[-A] request authentication\n"
+               "\t[-E] request encryption\n"
+               "\t[-S] secure connection\n"
+               "\t[-M] become master\n"
+               "\t[-T] enable timestamps\n");
+}
+
+int main(int argc, char *argv[])
+{
+       struct sigaction sa;
+       int opt, sk, mode = RECV, need_addr = 0;
+
+       bacpy(&bdaddr, BDADDR_ANY);
+
+       while ((opt=getopt(argc,argv,"rdscuwmnb:i:P:U:B:N:MAESL:W:C:D:Y:T")) != EOF) {
+               switch (opt) {
+               case 'r':
+                       mode = RECV;
+                       break;
+
+               case 's':
+                       mode = SEND;
+                       need_addr = 1;
+                       break;
+
+               case 'w':
+                       mode = LSEND;
+                       break;
+
+               case 'u':
+                       mode = CRECV;
+                       need_addr = 1;
+                       break;
+
+               case 'd':
+                       mode = DUMP;
+                       break;
+
+               case 'c':
+                       mode = RECONNECT;
+                       need_addr = 1;
+                       break;
+
+               case 'n':
+                       mode = CONNECT;
+                       need_addr = 1;
+                       break;
+
+               case 'm':
+                       mode = MULTY;
+                       need_addr = 1;
+                       break;
+
+               case 'b':
+                       data_size = atoi(optarg);
+                       break;
+
+               case 'i':
+                       if (!strncasecmp(optarg, "hci", 3))
+                               hci_devba(atoi(optarg + 3), &bdaddr);
+                       else
+                               str2ba(optarg, &bdaddr);
+                       break;
+
+               case 'P':
+                       channel = atoi(optarg);
+                       break;
+
+               case 'U':
+                       if (!strcasecmp(optarg, "spp"))
+                               uuid = SERIAL_PORT_SVCLASS_ID;
+                       else if (!strncasecmp(optarg, "0x", 2))
+                               uuid = strtoul(optarg + 2, NULL, 16);
+                       else
+                               uuid = atoi(optarg);
+                       break;
+
+               case 'M':
+                       master = 1;
+                       break;
+
+               case 'A':
+                       auth = 1;
+                       break;
+
+               case 'E':
+                       encrypt = 1;
+                       break;
+
+               case 'S':
+                       secure = 1;
+                       break;
+
+               case 'L':
+                       linger = atoi(optarg);
+                       break;
+
+               case 'W':
+                       defer_setup = atoi(optarg);
+                       break;
+
+               case 'B':
+                       filename = strdup(optarg);
+                       break;
+
+               case 'N':
+                       num_frames = atoi(optarg);
+                       break;
+
+               case 'C':
+                       count = atoi(optarg);
+                       break;
+
+               case 'D':
+                       delay = atoi(optarg) * 1000;
+                       break;
+
+               case 'Y':
+                       priority = atoi(optarg);
+                       break;
+
+               case 'T':
+                       timestamp = 1;
+                       break;
+
+               default:
+                       usage();
+                       exit(1);
+               }
+       }
+
+       if (need_addr && !(argc - optind)) {
+               usage();
+               exit(1);
+       }
+
+       if (!(buf = malloc(data_size))) {
+               perror("Can't allocate data buffer");
+               exit(1);
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = SIG_IGN;
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sigaction(SIGCHLD, &sa, NULL);
+
+       openlog("rctest", LOG_PERROR | LOG_PID, LOG_LOCAL0);
+
+       switch (mode) {
+               case RECV:
+                       do_listen(recv_mode);
+                       break;
+
+               case CRECV:
+                       sk = do_connect(argv[optind]);
+                       if (sk < 0)
+                               exit(1);
+                       recv_mode(sk);
+                       break;
+
+               case DUMP:
+                       do_listen(dump_mode);
+                       break;
+
+               case SEND:
+                       sk = do_connect(argv[optind]);
+                       if (sk < 0)
+                               exit(1);
+                       send_mode(sk);
+                       break;
+
+               case LSEND:
+                       do_listen(send_mode);
+                       break;
+
+               case RECONNECT:
+                       reconnect_mode(argv[optind]);
+                       break;
+
+               case MULTY:
+                       multi_connect_mode(argc - optind, argv + optind);
+                       break;
+
+               case CONNECT:
+                       sk = do_connect(argv[optind]);
+                       if (sk < 0)
+                               exit(1);
+                       dump_mode(sk);
+                       break;
+       }
+
+       syslog(LOG_INFO, "Exit");
+
+       closelog();
+
+       return 0;
+}
diff --git a/test/sap-client b/test/sap-client
new file mode 100644 (file)
index 0000000..413424c
--- /dev/null
@@ -0,0 +1,943 @@
+""" Copyright (C) 2010-2011 ST-Ericsson SA """
+
+""" Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson. """
+
+""" This program is free software; you can redistribute it and/or modify """
+""" it under the terms of the GNU General Public License as published by """
+""" the Free Software Foundation; either version 2 of the License, or """
+""" (at your option) any later version. """
+
+""" This program is distributed in the hope that it will be useful, """
+""" but WITHOUT ANY WARRANTY; without even the implied warranty of """
+""" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the """
+""" GNU General Public License for more details. """
+
+""" You should have received a copy of the GNU General Public License """
+""" along with this program; if not, write to the Free Software """
+""" Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """
+
+from array import array
+from bluetooth import *
+import time
+import re
+
+class SAPParam:
+    """ SAP Parameter Class """
+
+    MaxMsgSize = 0x00
+    ConnectionStatus = 0x01
+    ResultCode = 0x02
+    DisconnectionType = 0x03
+    CommandAPDU = 0x04
+    ResponseAPDU = 0x05
+    ATR = 0x06
+    CardReaderStatus = 0x07
+    StatusChange = 0x08
+    TransportProtocol = 0x09
+    CommandAPDU7816 = 0x10
+
+    def __init__(self, name, id, value = None):
+        self.name = name
+        self.id = id
+        self.value = value
+
+    def _padding(self,  buf):
+        pad = array('B')
+        while ( (len(buf) + len(pad)) % 4 ) != 0:
+            pad.append(0)
+        return pad
+
+    def _basicCheck(self,  buf):
+        if len(buf) < 4 or (len(buf) % 4) != 0 or buf[1] != 0:
+                return (-1,  -1)
+        if buf[0] != self.id:
+            return (-1,  -1)
+        plen = buf[2] * 256 + buf[3] + 4
+        if plen > len(buf):
+            return (-1,  -1)
+        pad = plen
+        while (pad % 4) != 0:
+            if buf[pad] != 0:
+                return (-1,  -1)
+            pad+=1
+        return (plen,  pad)
+
+    def getID(self):
+        return self.id
+
+    def getValue(self):
+        return self.value
+
+    def getContent(self):
+        return "%s(id=0x%.2X), value=%s \n" %  (self.name,  self.id, self.value)
+
+    def serialize(self):
+        a = array('B', '\00\00\00\00')
+        a[0] = self.id
+        a[1] = 0       # reserved
+        a[2] = 0       # length
+        a[3] = 1       # length
+        a.append(self.value)
+        a.extend(self._padding(a))
+        return a
+
+    def deserialize(self,  buf):
+        p = self._basicCheck(buf)
+        if p[0] == -1:
+            return -1
+        self.id = buf[0]
+        self.value = buf[4]
+        return p[1]
+
+
+class SAPParam_MaxMsgSize(SAPParam):
+    """MaxMsgSize Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"MaxMsgSize",  SAPParam.MaxMsgSize, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value > 0xFFFF:
+             self.value = 0xFFFF
+
+    def serialize(self):
+        a = array('B', '\00\00\00\00')
+        a[0] = self.id
+        a[3] = 2
+        a.append(self.value / 256)
+        a.append(self.value % 256)
+        a.extend(self._padding(a))
+        return a
+
+    def deserialize(self,  buf):
+        p = self._basicCheck(buf)
+        if p[0] == -1 :
+            return -1
+        self.value = buf[4] * 256 + buf[5]
+        return p[1]
+
+class SAPParam_CommandAPDU(SAPParam):
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "CommandAPDU",  SAPParam.CommandAPDU, array('B'))
+        else:
+            SAPParam.__init__(self, "CommandAPDU",  SAPParam.CommandAPDU, array('B', value))
+
+    def serialize(self):
+        a = array('B', '\00\00\00\00')
+        a[0] = self.id
+        plen = len(self.value)
+        a[2] = plen / 256
+        a[3] = plen % 256
+        a.extend(self.value)
+        a.extend(self._padding(a))
+        return a
+
+    def deserialize(self,  buf):
+        p = self._basicCheck(buf)
+        if p[0] == -1:
+            return -1
+        self.value = buf[4:p[0]]
+        return p[1]
+
+class SAPParam_ResponseAPDU(SAPParam_CommandAPDU):
+    """ResponseAPDU Param """
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "ResponseAPDU",  SAPParam.ResponseAPDU, array('B'))
+        else:
+            SAPParam.__init__(self, "ResponseAPDU",  SAPParam.ResponseAPDU, array('B', value))
+
+class SAPParam_ATR(SAPParam_CommandAPDU):
+    """ATR Param """
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "ATR",  SAPParam.ATR, array('B'))
+        else:
+            SAPParam.__init__(self, "ATR",  SAPParam.ATR, array('B', value))
+
+class SAPParam_CommandAPDU7816(SAPParam_CommandAPDU):
+    """Command APDU7816 Param."""
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "CommandAPDU7816",  SAPParam.CommandAPDU7816, array('B'))
+        else:
+            SAPParam.__init__(self, "CommandAPDU7816",  SAPParam.CommandAPDU7816, array('B', value))
+
+
+class SAPParam_ConnectionStatus(SAPParam):
+    """Connection status Param."""
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"ConnectionStatus",  SAPParam.ConnectionStatus, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01,  0x02,  0x03,  0x04):
+            print "Warning. ConnectionStatus value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_ResultCode(SAPParam):
+    """ Result Code Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"ResultCode",  SAPParam.ResultCode, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01,  0x02,  0x03,  0x04,  0x05,  0x06,  0x07):
+            print "Warning. ResultCode value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_DisconnectionType(SAPParam):
+    """Disconnection Type Param."""
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"DisconnectionType",  SAPParam.DisconnectionType, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01):
+            print "Warning. DisconnectionType value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_CardReaderStatus(SAPParam_CommandAPDU):
+    """Card reader Status Param."""
+
+    def __init__(self,  value = None):
+        if value is None:
+            SAPParam.__init__(self, "CardReaderStatus",  SAPParam.CardReaderStatus, array('B'))
+        else:
+            SAPParam.__init__(self, "CardReaderStatus",  SAPParam.CardReaderStatus, array('B', value))
+
+class SAPParam_StatusChange(SAPParam):
+    """Status Change Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"StatusChange",  SAPParam.StatusChange, value)
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01,  0x02,  0x03,  0x04,  0x05):
+            print "Warning. StatusChange value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPParam_TransportProtocol(SAPParam):
+    """Transport Protocol Param """
+
+    def __init__(self,  value = None):
+        SAPParam.__init__(self,"TransportProtocol",  SAPParam.TransportProtocol, value)
+        self.__validate()
+
+    def __validate(self):
+        if self.value is not None and self.value not in (0x00,  0x01):
+            print "Warning. TransportProtoco value in reserved range (0x%x)" % self.value
+
+    def deserialize(self,  buf):
+        ret = SAPParam.deserialize(self, buf)
+        if ret == -1:
+            return -1
+        self.__validate()
+        return ret
+
+class SAPMessage:
+
+    CONNECT_REQ = 0x00
+    CONNECT_RESP = 0x01
+    DISCONNECT_REQ = 0x02
+    DISCONNECT_RESP =0x03
+    DISCONNECT_IND = 0x04
+    TRANSFER_APDU_REQ = 0x05
+    TRANSFER_APDU_RESP = 0x06
+    TRANSFER_ATR_REQ = 0x07
+    TRANSFER_ATR_RESP = 0x08
+    POWER_SIM_OFF_REQ = 0x09
+    POWER_SIM_OFF_RESP = 0x0A
+    POWER_SIM_ON_REQ = 0x0B
+    POWER_SIM_ON_RESP = 0x0C
+    RESET_SIM_REQ = 0x0D
+    RESET_SIM_RESP = 0x0E
+    TRANSFER_CARD_READER_STATUS_REQ = 0x0F
+    TRANSFER_CARD_READER_STATUS_RESP = 0x10
+    STATUS_IND = 0x11
+    ERROR_RESP = 0x12
+    SET_TRANSPORT_PROTOCOL_REQ = 0x13
+    SET_TRANSPORT_PROTOCOL_RESP = 0x14
+
+    def __init__(self,  name,  id):
+        self.name = name
+        self.id = id
+        self.params = []
+        self.buf = array('B')
+
+    def _basicCheck(self,  buf):
+        if len(buf) < 4 or (len(buf) % 4) != 0 :
+            return False
+
+        if buf[0] != self.id:
+            return False
+
+        return True
+
+    def getID(self):
+        return self.id
+
+    def getContent(self):
+        s = "%s(id=0x%.2X) " % (self.name,  self.id)
+        if len( self.buf): s = s + "[%s]" % re.sub("(.{2})", "0x\\1 " , self.buf.tostring().encode("hex").upper(), re.DOTALL)
+        s = s + "\n\t"
+        for p in self.params:
+            s = s + "\t" + p.getContent()
+        return s
+
+    def getParams(self):
+        return self.params
+
+    def addParam(self,  param):
+        self.params.append(param)
+
+    def serialize(self):
+        ret = array('B', '\00\00\00\00')
+        ret[0] = self.id
+        ret[1] = len(self.params)
+        ret[2] = 0     # reserved
+        ret[3] = 0     # reserved
+        for p in self.params:
+            ret.extend(p.serialize())
+
+        self.buf = ret
+        return ret
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        return len(buf) == 4 and buf[1] == 0 and self._basicCheck(buf)
+
+
+class SAPMessage_CONNECT_REQ(SAPMessage):
+    def __init__(self,  MaxMsgSize = None):
+        SAPMessage.__init__(self,"CONNECT_REQ",  SAPMessage.CONNECT_REQ)
+        if MaxMsgSize is not None:
+            self.addParam(SAPParam_MaxMsgSize(MaxMsgSize))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.MaxMsgSize:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_MaxMsgSize()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_CONNECT_RESP(SAPMessage):
+    def __init__(self,  ConnectionStatus = None,  MaxMsgSize = None):
+        SAPMessage.__init__(self,"CONNECT_RESP",  SAPMessage.CONNECT_RESP)
+        if ConnectionStatus is not None:
+            self.addParam(SAPParam_ConnectionStatus(ConnectionStatus))
+            if MaxMsgSize is not None:
+                self.addParam(SAPParam_MaxMsgSize(MaxMsgSize))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ConnectionStatus:
+                if self.params[0].getValue() ==  0x02:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ConnectionStatus()
+            r = p.deserialize(buf[4:])
+            if  r != -1:
+                self.addParam(p)
+                if buf[1] == 2:
+                    p = SAPParam_MaxMsgSize()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_DISCONNECT_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"DISCONNECT_REQ",  SAPMessage.DISCONNECT_REQ)
+
+class SAPMessage_DISCONNECT_RESP(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"DISCONNECT_RESP",  SAPMessage.DISCONNECT_RESP)
+
+class SAPMessage_DISCONNECT_IND(SAPMessage):
+    def __init__(self,  Type = None):
+        SAPMessage.__init__(self,"DISCONNECT_IND",  SAPMessage.DISCONNECT_IND)
+        if Type is not None:
+            self.addParam(SAPParam_DisconnectionType(Type))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.DisconnectionType:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_DisconnectionType()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+
+class SAPMessage_TRANSFER_APDU_REQ(SAPMessage):
+    def __init__(self,  APDU = None,  T = False):
+        SAPMessage.__init__(self,"TRANSFER_APDU_REQ",  SAPMessage.TRANSFER_APDU_REQ)
+        if APDU is not None:
+            if T :
+                self.addParam(SAPParam_CommandAPDU(APDU))
+            else:
+                self.addParam(SAPParam_CommandAPDU7816(APDU))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.CommandAPDU or self.params[0].getID() == SAPParam.CommandAPDU7816:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+
+            p = SAPParam_CommandAPDU()
+            p2 = SAPParam_CommandAPDU7816()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+            elif p2.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p2)
+                return self._validate()
+
+        return False
+
+class SAPMessage_TRANSFER_APDU_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None,  Response = None):
+        SAPMessage.__init__(self,"TRANSFER_APDU_RESP",  SAPMessage.TRANSFER_APDU_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+            if Response is not None:
+                self.addParam(SAPParam_ResponseAPDU(Response))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ResultCode:
+                if self.params[0].getValue() == 0x00:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ResultCode()
+            r = p.deserialize(buf[4:])
+            if  r != -1:
+                self.addParam(p)
+                if buf[1] == 2:
+                    p = SAPParam_ResponseAPDU()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_TRANSFER_ATR_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"TRANSFER_ATR_REQ",  SAPMessage.TRANSFER_ATR_REQ)
+
+class SAPMessage_TRANSFER_ATR_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None,  ATR = None):
+        SAPMessage.__init__(self,"TRANSFER_ATR_RESP",  SAPMessage.TRANSFER_ATR_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+            if ATR is not None:
+                self.addParam(SAPParam_ATR(ATR))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ResultCode:
+                if self.params[0].getValue() == 0x00:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+
+            p = SAPParam_ResultCode()
+            r = p.deserialize(buf[4:])
+
+            if  r != -1:
+
+                self.addParam(p)
+                if buf[1] == 2:
+
+                    p = SAPParam_ATR()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_POWER_SIM_OFF_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"POWER_SIM_OFF_REQ",  SAPMessage.POWER_SIM_OFF_REQ)
+
+class SAPMessage_POWER_SIM_OFF_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"POWER_SIM_OFF_RESP",  SAPMessage.POWER_SIM_OFF_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.ResultCode:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ResultCode()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_POWER_SIM_ON_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"POWER_SIM_ON_REQ",  SAPMessage.POWER_SIM_ON_REQ)
+
+class SAPMessage_POWER_SIM_ON_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"POWER_SIM_ON_RESP",  SAPMessage.POWER_SIM_ON_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+class SAPMessage_RESET_SIM_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"RESET_SIM_REQ",  SAPMessage.RESET_SIM_REQ)
+
+class SAPMessage_RESET_SIM_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"RESET_SIM_RESP",  SAPMessage.RESET_SIM_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+class SAPMessage_STATUS_IND(SAPMessage):
+    def __init__(self,  StatusChange = None):
+        SAPMessage.__init__(self,"STATUS_IND",  SAPMessage.STATUS_IND)
+        if StatusChange is not None:
+            self.addParam(SAPParam_StatusChange(StatusChange))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.StatusChange:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_StatusChange()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_TRANSFER_CARD_READER_STATUS_REQ(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"TRANSFER_CARD_READER_STATUS_REQ",  SAPMessage.TRANSFER_CARD_READER_STATUS_REQ)
+
+class SAPMessage_TRANSFER_CARD_READER_STATUS_RESP(SAPMessage):
+    def __init__(self,  ResultCode = None,  Status = None):
+        SAPMessage.__init__(self,"TRANSFER_CARD_READER_STATUS_RESP",  SAPMessage.TRANSFER_CARD_READER_STATUS_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+            if Status is not None:
+                self.addParam(SAPParam_CardReaderStatus(Status))
+
+    def _validate(self):
+        if len(self.params) > 0:
+            if self.params[0] .getID() == SAPParam.ResultCode:
+                if self.params[0].getValue() == 0x00:
+                    if len(self.params) == 2:
+                        return True
+                else:
+                    if len(self.params) == 1:
+                        return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_ResultCode()
+            r = p.deserialize(buf[4:])
+            if  r != -1:
+                self.addParam(p)
+                if buf[1] == 2:
+                    p = SAPParam_CardReaderStatus()
+                    r = p.deserialize(buf[4+r:])
+                    if r != -1:
+                        self.addParam(p)
+
+                return self._validate()
+
+        return False
+
+class SAPMessage_ERROR_RESP(SAPMessage):
+    def __init__(self):
+        SAPMessage.__init__(self,"ERROR_RESP",  SAPMessage.ERROR_RESP)
+
+
+class SAPMessage_SET_TRANSPORT_PROTOCOL_REQ(SAPMessage):
+    def __init__(self,  protocol = None):
+        SAPMessage.__init__(self,"SET_TRANSPORT_PROTOCOL_REQ",  SAPMessage.SET_TRANSPORT_PROTOCOL_REQ)
+        if protocol is not None:
+            self.addParam(SAPParam_TransportProtocol(protocol))
+
+    def _validate(self):
+        if len(self.params) == 1:
+            if self.params[0].getID() == SAPParam.TransportProtocol:
+                return True
+        return False
+
+    def deserialize(self,  buf):
+        self.buf = buf
+        self.params[:] = []
+        if SAPMessage._basicCheck(self,  buf):
+            p = SAPParam_TransportProtocol()
+            if p.deserialize(buf[4:]) == len(buf[4:]):
+                self.addParam(p)
+                return self._validate()
+
+        return False
+
+class SAPMessage_SET_TRANSPORT_PROTOCOL_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+    def __init__(self,  ResultCode = None):
+        SAPMessage.__init__(self,"SET_TRANSPORT_PROTOCOL_RESP",  SAPMessage.SET_TRANSPORT_PROTOCOL_RESP)
+        if ResultCode is not None:
+            self.addParam(SAPParam_ResultCode(ResultCode))
+
+
+class SAPClient:
+
+    CONNECTED = 1
+    DISCONNECTED = 0
+
+    uuid = "0000112D-0000-1000-8000-00805F9B34FB"
+    bufsize = 1024
+    timeout = 20
+    state = DISCONNECTED
+
+    def __init__(self,  host = None,  port = None):
+        self.sock = None
+
+        if host is None or is_valid_address(host):
+            self.host = host
+        else:
+            raise BluetoothError ("%s is not a valid BT address." % host)
+            self.host = None
+            return
+
+        if port is None:
+            self.__discover()
+        else:
+            self.port = port
+
+        self.__connectRFCOMM()
+
+    def __del__(self):
+        self.__disconnectRFCOMM()
+
+    def __disconnectRFCOMM(self):
+        if self.sock is not None:
+            self.sock.close()
+            self.state = self.DISCONNECTED
+
+    def __discover(self):
+        service_matches = find_service(self.uuid, self.host)
+
+        if len(service_matches) == 0:
+            raise BluetoothError ("No SAP service found")
+            return
+
+        first_match = service_matches[0]
+        self.port = first_match["port"]
+        self.host = first_match["host"]
+
+        print "SAP Service found on %s(%s)" % first_match["name"] % self.host
+
+    def __connectRFCOMM(self):
+        self.sock=BluetoothSocket( RFCOMM )
+        self.sock.connect((self.host, self.port))
+        self.sock.settimeout(self.timeout)
+        self.state = self.CONNECTED
+
+    def __sendMsg(self, msg):
+        if isinstance(msg,  SAPMessage):
+            s = msg.serialize()
+            print "\tTX: " + msg.getContent()
+            return self.sock.send(s.tostring())
+
+    def __rcvMsg(self,  msg):
+        if isinstance(msg,  SAPMessage):
+            print "\tRX Wait: %s(id = 0x%.2x)" % (msg.name, msg.id)
+            data = self.sock.recv(self.bufsize)
+            if data:
+                if msg.deserialize(array('B',data)):
+                    print "\tRX: len(%d) %s" % (len(data), msg.getContent())
+                    return msg
+                else:
+                    print "msg: %s" % array('B',data)
+                    raise BluetoothError ("Message deserialization failed.")
+            else:
+                raise BluetoothError ("Timeout. No data received.")
+
+    def connect(self):
+        self.__connectRFCOMM()
+
+    def disconnect(self):
+        self.__disconnectRFCOMM()
+
+    def isConnected(self):
+        return self.state
+
+    def proc_connect(self):
+        try:
+            self.__sendMsg(SAPMessage_CONNECT_REQ(self.bufsize))
+            params = self.__rcvMsg(SAPMessage_CONNECT_RESP()).getParams()
+
+            if params[0].getValue() in (0x00,  0x04):
+                pass
+            elif params[0].getValue() == 0x02:
+                self.bufsize = params[1].getValue()
+
+                self.__sendMsg(SAPMessage_CONNECT_REQ(self.bufsize))
+                params = self.__rcvMsg(SAPMessage_CONNECT_RESP()).getParams()
+
+                if params[0].getValue() not in (0x00,  0x04):
+                    return False
+            else:
+                return False
+
+            params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+            if params[0].getValue() == 0x00:
+                return False
+            elif params[0].getValue() == 0x01:
+                """OK, Card reset"""
+                return self.proc_transferATR()
+            elif params[0].getValue() == 0x02:
+                """T0 not supported"""
+                if self.proc_transferATR():
+                    return self.proc_setTransportProtocol(1)
+                else:
+                    return False
+            else:
+                return False
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_disconnectByClient(self, timeout=0):
+        try:
+            self.__sendMsg(SAPMessage_DISCONNECT_REQ())
+            self.__rcvMsg(SAPMessage_DISCONNECT_RESP())
+            time.sleep(timeout) # let srv to close rfcomm
+            self.__disconnectRFCOMM()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_disconnectByServer(self, timeout=0):
+        try:
+            params = self.__rcvMsg(SAPMessage_DISCONNECT_IND()).getParams()
+
+            """graceful"""
+            if params[0].getValue() == 0x00:
+                if not self.proc_transferAPDU():
+                    return False
+
+            return self.proc_disconnectByClient(timeout)
+
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_transferAPDU(self,  apdu = "Sample APDU command"):
+        try:
+            self.__sendMsg(SAPMessage_TRANSFER_APDU_REQ(apdu))
+            params = self.__rcvMsg(SAPMessage_TRANSFER_APDU_RESP()).getParams()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_transferATR(self):
+        try:
+            self.__sendMsg(SAPMessage_TRANSFER_ATR_REQ())
+            params = self.__rcvMsg(SAPMessage_TRANSFER_ATR_RESP()).getParams()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_powerSimOff(self):
+        try:
+            self.__sendMsg(SAPMessage_POWER_SIM_OFF_REQ())
+            params = self.__rcvMsg(SAPMessage_POWER_SIM_OFF_RESP()).getParams()
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_powerSimOn(self):
+        try:
+            self.__sendMsg(SAPMessage_POWER_SIM_ON_REQ())
+            params = self.__rcvMsg(SAPMessage_POWER_SIM_ON_RESP()).getParams()
+            if params[0].getValue() == 0x00:
+                return self.proc_transferATR()
+
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_resetSim(self):
+        try:
+            self.__sendMsg(SAPMessage_RESET_SIM_REQ())
+            params = self.__rcvMsg(SAPMessage_RESET_SIM_RESP()).getParams()
+            if params[0].getValue() == 0x00:
+                return self.proc_transferATR()
+
+            return True
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_reportStatus(self):
+        try:
+            params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_transferCardReaderStatus(self):
+        try:
+            self.__sendMsg(SAPMessage_TRANSFER_CARD_READER_STATUS_REQ())
+            params = self.__rcvMsg(SAPMessage_TRANSFER_CARD_READER_STATUS_RESP()).getParams()
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_errorResponse(self):
+        try:
+            """ send malformed message, no mandatory maxmsgsize parameter"""
+            self.__sendMsg(SAPMessage_CONNECT_REQ())
+
+            params = self.__rcvMsg(SAPMessage_ERROR_RESP()).getParams()
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+    def proc_setTransportProtocol(self,  protocol = 0):
+        try:
+            self.__sendMsg(SAPMessage_SET_TRANSPORT_PROTOCOL_REQ(protocol))
+            params = self.__rcvMsg(SAPMessage_SET_TRANSPORT_PROTOCOL_RESP()).getParams()
+
+            if params[0].getValue() == 0x00:
+                params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+                if params[0].getValue() in (0x01,  0x02):
+                    return self.proc_transferATR()
+                else:
+                    return True
+                    """return False ???"""
+            elif params[0].getValue == 0x07:
+                """not supported"""
+                return True
+                """return False ???"""
+            else:
+                return False
+
+        except BluetoothError , e:
+            print "Error. " +str(e)
+            return False
+
+if __name__ == "__main__":
+    pass
diff --git a/test/scotest.c b/test/scotest.c
new file mode 100644 (file)
index 0000000..17bd8a6
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sco.h>
+
+/* Test modes */
+enum {
+       SEND,
+       RECV,
+       RECONNECT,
+       MULTY,
+       DUMP,
+       CONNECT
+};
+
+static unsigned char *buf;
+
+/* Default data size */
+static long data_size = 672;
+
+static bdaddr_t bdaddr;
+
+static float tv2fl(struct timeval tv)
+{
+       return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static int do_connect(char *svr)
+{
+       struct sockaddr_sco addr;
+       struct sco_conninfo conn;
+       socklen_t optlen;
+       int sk;
+
+       /* Create socket */
+       sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+       if (sk < 0) {
+               syslog(LOG_ERR, "Can't create socket: %s (%d)",
+                                                       strerror(errno), errno);
+               return -1;
+       }
+
+       /* Bind to local address */
+       memset(&addr, 0, sizeof(addr));
+       addr.sco_family = AF_BLUETOOTH;
+       bacpy(&addr.sco_bdaddr, &bdaddr);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Connect to remote device */
+       memset(&addr, 0, sizeof(addr));
+       addr.sco_family = AF_BLUETOOTH;
+       str2ba(svr, &addr.sco_bdaddr);
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               syslog(LOG_ERR, "Can't connect: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Get connection information */
+       memset(&conn, 0, sizeof(conn));
+       optlen = sizeof(conn);
+
+       if (getsockopt(sk, SOL_SCO, SCO_CONNINFO, &conn, &optlen) < 0) {
+               syslog(LOG_ERR, "Can't get SCO connection information: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x]",
+               conn.hci_handle,
+               conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+
+       return sk;
+
+error:
+       close(sk);
+       return -1;
+}
+
+static void do_listen(void (*handler)(int sk))
+{
+       struct sockaddr_sco addr;
+       struct sco_conninfo conn;
+       socklen_t optlen;
+       int sk, nsk;
+       char ba[18];
+
+       /* Create socket */
+       sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+       if (sk < 0) {
+               syslog(LOG_ERR, "Can't create socket: %s (%d)",
+                                                       strerror(errno), errno);
+               exit(1);
+       }
+
+       /* Bind to local address */
+       memset(&addr, 0, sizeof(addr));
+       addr.sco_family = AF_BLUETOOTH;
+       bacpy(&addr.sco_bdaddr, &bdaddr);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       /* Listen for connections */
+       if (listen(sk, 10)) {
+               syslog(LOG_ERR,"Can not listen on the socket: %s (%d)",
+                                                       strerror(errno), errno);
+               goto error;
+       }
+
+       syslog(LOG_INFO,"Waiting for connection ...");
+
+       while (1) {
+               memset(&addr, 0, sizeof(addr));
+               optlen = sizeof(addr);
+
+               nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+               if (nsk < 0) {
+                       syslog(LOG_ERR,"Accept failed: %s (%d)",
+                                                       strerror(errno), errno);
+                       goto error;
+               }
+               if (fork()) {
+                       /* Parent */
+                       close(nsk);
+                       continue;
+               }
+               /* Child */
+               close(sk);
+
+               /* Get connection information */
+               memset(&conn, 0, sizeof(conn));
+               optlen = sizeof(conn);
+
+               if (getsockopt(nsk, SOL_SCO, SCO_CONNINFO, &conn, &optlen) < 0) {
+                       syslog(LOG_ERR, "Can't get SCO connection information: %s (%d)",
+                                                       strerror(errno), errno);
+                       close(nsk);
+                       goto error;
+               }
+
+               ba2str(&addr.sco_bdaddr, ba);
+               syslog(LOG_INFO, "Connect from %s [handle %d, class 0x%02x%02x%02x]",
+                       ba, conn.hci_handle,
+                       conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+
+               handler(nsk);
+
+               syslog(LOG_INFO, "Disconnect");
+               exit(0);
+       }
+
+       return;
+
+error:
+       close(sk);
+       exit(1);
+}
+
+static void dump_mode(int sk)
+{
+       int len;
+
+       syslog(LOG_INFO,"Receiving ...");
+       while ((len = read(sk, buf, data_size)) > 0)
+               syslog(LOG_INFO, "Recevied %d bytes", len);
+}
+
+static void recv_mode(int sk)
+{
+       struct timeval tv_beg,tv_end,tv_diff;
+       long total;
+
+       syslog(LOG_INFO, "Receiving ...");
+
+       while (1) {
+               gettimeofday(&tv_beg, NULL);
+               total = 0;
+               while (total < data_size) {
+                       int r;
+                       if ((r = recv(sk, buf, data_size, 0)) <= 0) {
+                               if (r < 0)
+                                       syslog(LOG_ERR, "Read failed: %s (%d)",
+                                                       strerror(errno), errno);
+                               return;
+                       }
+                       total += r;
+               }
+               gettimeofday(&tv_end, NULL);
+
+               timersub(&tv_end, &tv_beg, &tv_diff);
+
+               syslog(LOG_INFO,"%ld bytes in %.2fm speed %.2f kb", total,
+                       tv2fl(tv_diff) / 60.0,
+                       (float)( total / tv2fl(tv_diff) ) / 1024.0 );
+       }
+}
+
+static void send_mode(char *svr)
+{
+       struct sco_options so;
+       socklen_t len;
+       uint32_t seq;
+       int i, sk;
+
+       if ((sk = do_connect(svr)) < 0) {
+               syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+                                                       strerror(errno), errno);
+               exit(1);
+       }
+
+       len = sizeof(so);
+       if (getsockopt(sk, SOL_SCO, SCO_OPTIONS, &so, &len) < 0) {
+               syslog(LOG_ERR, "Can't get SCO options: %s (%d)",
+                                                       strerror(errno), errno);
+               exit(1);
+       }
+
+       syslog(LOG_INFO,"Sending ...");
+
+       for (i = 6; i < so.mtu; i++)
+               buf[i] = 0x7f;
+
+       seq = 0;
+       while (1) {
+               *(uint32_t *) buf = htobl(seq);
+               *(uint16_t *) (buf + 4) = htobs(data_size);
+               seq++;
+
+               if (send(sk, buf, so.mtu, 0) <= 0) {
+                       syslog(LOG_ERR, "Send failed: %s (%d)",
+                                                       strerror(errno), errno);
+                       exit(1);
+               }
+
+               usleep(1);
+       }
+}
+
+static void reconnect_mode(char *svr)
+{
+       while (1) {
+               int sk;
+
+               if ((sk = do_connect(svr)) < 0) {
+                       syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+                                                       strerror(errno), errno);
+                       exit(1);
+               }
+
+               close(sk);
+
+               sleep(5);
+       }
+}
+
+static void multy_connect_mode(char *svr)
+{
+       while (1) {
+               int i, sk;
+
+               for (i = 0; i < 10; i++){
+                       if (fork())
+                               continue;
+
+                       /* Child */
+                       sk = do_connect(svr);
+                       if (sk < 0) {
+                               syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+                                                       strerror(errno), errno);
+                       }
+                       close(sk);
+                       exit(0);
+               }
+
+               sleep(19);
+       }
+}
+
+static void usage(void)
+{
+       printf("scotest - SCO testing\n"
+               "Usage:\n");
+       printf("\tscotest <mode> [-b bytes] [bd_addr]\n");
+       printf("Modes:\n"
+               "\t-d dump (server)\n"
+               "\t-c reconnect (client)\n"
+               "\t-m multiple connects (client)\n"
+               "\t-r receive (server)\n"
+               "\t-s connect and send (client)\n"
+               "\t-n connect and be silent (client)\n");
+}
+
+int main(int argc ,char *argv[])
+{
+       struct sigaction sa;
+       int opt, sk, mode = RECV;
+
+       while ((opt=getopt(argc,argv,"rdscmnb:")) != EOF) {
+               switch(opt) {
+               case 'r':
+                       mode = RECV;
+                       break;
+
+               case 's':
+                       mode = SEND;
+                       break;
+
+               case 'd':
+                       mode = DUMP;
+                       break;
+
+               case 'c':
+                       mode = RECONNECT;
+                       break;
+
+               case 'm':
+                       mode = MULTY;
+                       break;
+
+               case 'n':
+                       mode = CONNECT;
+                       break;
+
+               case 'b':
+                       data_size = atoi(optarg);
+                       break;
+
+               default:
+                       usage();
+                       exit(1);
+               }
+       }
+
+       if (!(argc - optind) && (mode != RECV && mode != DUMP)) {
+               usage();
+               exit(1);
+       }
+
+       if (!(buf = malloc(data_size))) {
+               perror("Can't allocate data buffer");
+               exit(1);
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = SIG_IGN;
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sigaction(SIGCHLD, &sa, NULL);
+
+       openlog("scotest", LOG_PERROR | LOG_PID, LOG_LOCAL0);
+
+       switch( mode ){
+               case RECV:
+                       do_listen(recv_mode);
+                       break;
+
+               case DUMP:
+                       do_listen(dump_mode);
+                       break;
+
+               case SEND:
+                       send_mode(argv[optind]);
+                       break;
+
+               case RECONNECT:
+                       reconnect_mode(argv[optind]);
+                       break;
+
+               case MULTY:
+                       multy_connect_mode(argv[optind]);
+                       break;
+
+               case CONNECT:
+                       sk = do_connect(argv[optind]);
+                       if (sk < 0)
+                               exit(1);
+                       dump_mode(sk);
+                       break;
+       }
+
+       syslog(LOG_INFO, "Exit");
+
+       closelog();
+
+       return 0;
+}
diff --git a/test/sdptest.c b/test/sdptest.c
new file mode 100644 (file)
index 0000000..480a468
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+static volatile sig_atomic_t __io_finished = 0;
+
+static void callback(uint8_t type, uint16_t status,
+                               uint8_t *rsp, size_t size, void *udata)
+{
+       unsigned int i;
+
+       for (i = 0; i < size; i++) {
+               printf("%02x ", rsp[i]);
+               if ((i + 1) % 8 == 0)
+                       printf(" ");
+               if ((i + 1) % 16 == 0)
+                       printf("\n");
+       }
+       printf("\n");
+
+       __io_finished = 1;
+}
+
+static void cmd_search(bdaddr_t *src, bdaddr_t *dst)
+{
+       sdp_session_t *session;
+       sdp_list_t *search, *attrids;
+       uint32_t range = 0x0000ffff;
+       uuid_t uuid;
+
+       session = sdp_connect(src, dst, 0);
+       if (!session) {
+               perror("Can't connect to SDP service");
+               exit(1);
+       }
+
+       sdp_set_notify(session, callback, NULL);
+
+       sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
+
+       search = sdp_list_append(NULL, &uuid);
+
+       attrids = sdp_list_append(NULL, &range);
+
+       //sdp_service_search_attr_async(session, search,
+       //                              SDP_ATTR_REQ_RANGE, attrids);
+
+       sdp_service_search_async(session, search, 0xffff);
+
+       sdp_list_free(attrids, NULL);
+
+       sdp_list_free(search, NULL);
+
+       while (!__io_finished)
+               sdp_process(session);
+
+       sdp_close(session);
+}
+
+static void usage(void)
+{
+       printf("sdptest - Utility for SDP testing\n\n");
+       printf("Usage:\n"
+               "\tsdptest [-i <dev>] <bdaddr>\n");
+}
+
+static struct option main_options[] = {
+       { "device",     1, 0, 'i' },
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       bdaddr_t src, dst;
+       int opt;
+
+       bacpy(&src, BDADDR_ANY);
+
+       while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'i':
+                       if (!strncasecmp(optarg, "hci", 3))
+                               hci_devba(atoi(optarg + 3), &src);
+                       else
+                               str2ba(optarg, &dst);
+                       break;
+
+               case 'h':
+               default:
+                       usage();
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       str2ba(argv[0], &dst);
+
+       cmd_search(&src, &dst);
+
+       return 0;
+}
diff --git a/test/service-did.xml b/test/service-did.xml
new file mode 100644 (file)
index 0000000..52eb68c
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+  <attribute id="0x0001">
+    <sequence>
+      <uuid value="0x1200"/>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0200">
+    <uint16 value="0x0102" name="id"/>
+  </attribute>
+
+  <attribute id="0x0201">
+    <uint16 value="0x0a12" name="vendor"/>
+  </attribute>
+
+  <attribute id="0x0202">
+    <uint16 value="0x4711" name="product"/>
+  </attribute>
+
+  <attribute id="0x0203">
+    <uint16 value="0x0000" name="version"/>
+  </attribute>
+
+  <attribute id="0x0204">
+    <boolean value="true"/>
+  </attribute>
+
+  <attribute id="0x0205">
+    <uint16 value="0x0002" name="source"/>
+  </attribute>
+</record>
diff --git a/test/service-ftp.xml b/test/service-ftp.xml
new file mode 100644 (file)
index 0000000..1bda885
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+  <attribute id="0x0001">
+    <sequence>
+      <uuid value="0x1106"/>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0004">
+    <sequence>
+      <sequence>
+        <uuid value="0x0100"/>
+      </sequence>
+      <sequence>
+        <uuid value="0x0003"/>
+        <uint8 value="23" name="channel"/>
+      </sequence>
+      <sequence>
+        <uuid value="0x0008"/>
+      </sequence>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0009">
+    <sequence>
+      <sequence>
+        <uuid value="0x1106"/>
+        <uint16 value="0x0100" name="version"/>
+      </sequence>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0100">
+    <text value="OBEX File Transfer" name="name"/>
+  </attribute>
+</record>
diff --git a/test/service-opp.xml b/test/service-opp.xml
new file mode 100644 (file)
index 0000000..351b4a4
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+  <attribute id="0x0001">
+    <sequence>
+      <uuid value="0x1105"/>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0004">
+    <sequence>
+      <sequence>
+        <uuid value="0x0100"/>
+      </sequence>
+      <sequence>
+        <uuid value="0x0003"/>
+        <uint8 value="23" name="channel"/>
+      </sequence>
+      <sequence>
+        <uuid value="0x0008"/>
+      </sequence>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0009">
+    <sequence>
+      <sequence>
+        <uuid value="0x1105"/>
+        <uint16 value="0x0100" name="version"/>
+      </sequence>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0100">
+    <text value="OBEX Object Push" name="name"/>
+  </attribute>
+
+  <attribute id="0x0303">
+    <sequence>
+      <uint8 value="0x01"/>
+      <uint8 value="0x01"/>
+      <uint8 value="0x02"/>
+      <uint8 value="0x03"/>
+      <uint8 value="0x04"/>
+      <uint8 value="0x05"/>
+      <uint8 value="0x06"/>
+      <uint8 value="0xff"/>
+    </sequence>
+  </attribute>
+</record>
diff --git a/test/service-record.dtd b/test/service-record.dtd
new file mode 100644 (file)
index 0000000..f53be5d
--- /dev/null
@@ -0,0 +1,66 @@
+<!ELEMENT record (attribute)*>
+
+<!ELEMENT attribute (sequence|alternate|text|url|uuid|boolean|uint8|uint16|uint32|uint64|nil)+>
+<!ATTLIST attribute id CDATA #REQUIRED>
+
+<!ELEMENT sequence (sequence|alternate|text|url|uuid|boolean|uint8|uint16|uint32|uint64|uint128|int8|int16|int32|int64|int128|nil)+>
+
+<!ELEMENT alternate (sequence|alternate|text|url|uuid|boolean|uint8|uint16|uint32|uint64|uint128|int8|int16|int32|int64|int128|nil)+>
+
+<!ELEMENT text EMPTY>
+<!ATTLIST text value CDATA #REQUIRED>
+<!ATTLIST text name CDATA>
+<!ATTLIST text encoding (normal|hex) "normal">
+
+<!ELEMENT url EMPTY>
+<!ATTLIST url value CDATA #REQUIRED>
+<!ATTLIST url name CDATA>
+
+<!ELEMENT uuid EMPTY>
+<!ATTLIST uuid value CDATA #REQUIRED>
+
+<!ELEMENT boolean EMPTY>
+<!ATTLIST boolean value CDATA #REQUIRED>
+<!ATTLIST boolean name CDATA>
+
+<!ELEMENT uint8 EMPTY>
+<!ATTLIST uint8 value CDATA #REQUIRED>
+<!ATTLIST uint8 name CDATA>
+
+<!ELEMENT uint16 EMPTY>
+<!ATTLIST uint16 value CDATA #REQUIRED>
+<!ATTLIST uint16 name CDATA>
+
+<!ELEMENT uint32 EMPTY>
+<!ATTLIST uint32 value CDATA #REQUIRED>
+<!ATTLIST uint32 name CDATA>
+
+<!ELEMENT uint64 EMPTY>
+<!ATTLIST uint64 value CDATA #REQUIRED>
+<!ATTLIST uint64 name CDATA>
+
+<!ELEMENT uint128 EMPTY>
+<!ATTLIST uint128 value CDATA #REQUIRED>
+<!ATTLIST uint128 name CDATA>
+
+<!ELEMENT int8 EMPTY>
+<!ATTLIST int8 value CDATA #REQUIRED>
+<!ATTLIST int8 name CDATA>
+
+<!ELEMENT int16 EMPTY>
+<!ATTLIST int16 value CDATA #REQUIRED>
+<!ATTLIST int16 name CDATA>
+
+<!ELEMENT int32 EMPTY>
+<!ATTLIST int32 value CDATA #REQUIRED>
+<!ATTLIST int32 name CDATA>
+
+<!ELEMENT int64 EMPTY>
+<!ATTLIST int64 value CDATA #REQUIRED>
+<!ATTLIST int64 name CDATA>
+
+<!ELEMENT int128 EMPTY>
+<!ATTLIST int128 value CDATA #REQUIRED>
+<!ATTLIST int128 name CDATA>
+
+<!ELEMENT nil EMPTY>
diff --git a/test/service-spp.xml b/test/service-spp.xml
new file mode 100644 (file)
index 0000000..2b156c3
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<record>
+  <attribute id="0x0001">
+    <sequence>
+      <uuid value="0x1101"/>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0004">
+    <sequence>
+      <sequence>
+        <uuid value="0x0100"/>
+      </sequence>
+      <sequence>
+        <uuid value="0x0003"/>
+        <uint8 value="23" name="channel"/>
+      </sequence>
+    </sequence>
+  </attribute>
+
+  <attribute id="0x0100">
+    <text value="COM5" name="name"/>
+  </attribute>
+</record>
diff --git a/test/simple-agent b/test/simple-agent
new file mode 100755 (executable)
index 0000000..a25eaf0
--- /dev/null
@@ -0,0 +1,144 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+from optparse import OptionParser
+
+def ask(prompt):
+       try:
+               return raw_input(prompt)
+       except:
+               return input(prompt)
+
+class Rejected(dbus.DBusException):
+       _dbus_error_name = "org.bluez.Error.Rejected"
+
+class Agent(dbus.service.Object):
+       exit_on_release = True
+
+       def set_exit_on_release(self, exit_on_release):
+               self.exit_on_release = exit_on_release
+
+       @dbus.service.method("org.bluez.Agent",
+                                       in_signature="", out_signature="")
+       def Release(self):
+               print("Release")
+               if self.exit_on_release:
+                       mainloop.quit()
+
+       @dbus.service.method("org.bluez.Agent",
+                                       in_signature="os", out_signature="")
+       def Authorize(self, device, uuid):
+               print("Authorize (%s, %s)" % (device, uuid))
+               authorize = ask("Authorize connection (yes/no): ")
+               if (authorize == "yes"):
+                       return
+               raise Rejected("Connection rejected by user")
+
+       @dbus.service.method("org.bluez.Agent",
+                                       in_signature="o", out_signature="s")
+       def RequestPinCode(self, device):
+               print("RequestPinCode (%s)" % (device))
+               return ask("Enter PIN Code: ")
+
+       @dbus.service.method("org.bluez.Agent",
+                                       in_signature="o", out_signature="u")
+       def RequestPasskey(self, device):
+               print("RequestPasskey (%s)" % (device))
+               passkey = ask("Enter passkey: ")
+               return dbus.UInt32(passkey)
+
+       @dbus.service.method("org.bluez.Agent",
+                                       in_signature="ou", out_signature="")
+       def DisplayPasskey(self, device, passkey):
+               print("DisplayPasskey (%s, %06d)" % (device, passkey))
+
+       @dbus.service.method("org.bluez.Agent",
+                                       in_signature="os", out_signature="")
+       def DisplayPinCode(self, device, pincode):
+               print("DisplayPinCode (%s, %s)" % (device, pincode))
+
+       @dbus.service.method("org.bluez.Agent",
+                                       in_signature="ou", out_signature="")
+       def RequestConfirmation(self, device, passkey):
+               print("RequestConfirmation (%s, %06d)" % (device, passkey))
+               confirm = ask("Confirm passkey (yes/no): ")
+               if (confirm == "yes"):
+                       return
+               raise Rejected("Passkey doesn't match")
+
+       @dbus.service.method("org.bluez.Agent",
+                                       in_signature="s", out_signature="")
+       def ConfirmModeChange(self, mode):
+               print("ConfirmModeChange (%s)" % (mode))
+               authorize = ask("Authorize mode change (yes/no): ")
+               if (authorize == "yes"):
+                       return
+               raise Rejected("Mode change by user")
+
+       @dbus.service.method("org.bluez.Agent",
+                                       in_signature="", out_signature="")
+       def Cancel(self):
+               print("Cancel")
+
+def create_device_reply(device):
+       print("New device (%s)" % (device))
+       mainloop.quit()
+
+def create_device_error(error):
+       print("Creating device failed: %s" % (error))
+       mainloop.quit()
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                                       "org.bluez.Manager")
+
+       capability = "KeyboardDisplay"
+
+       parser = OptionParser()
+       parser.add_option("-c", "--capability", action="store",
+                                       type="string", dest="capability")
+       (options, args) = parser.parse_args()
+       if options.capability:
+               capability  = options.capability
+
+       if len(args) > 0:
+               path = manager.FindAdapter(args[0])
+       else:
+               path = manager.DefaultAdapter()
+
+       adapter = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Adapter")
+
+       path = "/test/agent"
+       agent = Agent(bus, path)
+
+       mainloop = GObject.MainLoop()
+
+       if len(args) > 1:
+               if len(args) > 2:
+                       device = adapter.FindDevice(args[1])
+                       adapter.RemoveDevice(device)
+
+               agent.set_exit_on_release(False)
+               adapter.CreatePairedDevice(args[1], path, capability,
+                                       timeout=60000,
+                                       reply_handler=create_device_reply,
+                                       error_handler=create_device_error)
+       else:
+               adapter.RegisterAgent(path, capability)
+               print("Agent registered")
+
+       mainloop.run()
+
+       #adapter.UnregisterAgent(path)
+       #print("Agent unregistered")
diff --git a/test/simple-endpoint b/test/simple-endpoint
new file mode 100755 (executable)
index 0000000..20c8159
--- /dev/null
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import gobject
+
+A2DP_SOURCE_UUID = "0000110A-0000-1000-8000-00805F9B34FB"
+A2DP_SINK_UUID = "0000110B-0000-1000-8000-00805F9B34FB"
+HFP_AG_UUID = "0000111F-0000-1000-8000-00805F9B34FB"
+HSP_AG_UUID = "00001112-0000-1000-8000-00805F9B34FB"
+
+SBC_CODEC = dbus.Byte(0x00)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 16Khz 32Khz 44.1Khz 48Khz
+#Subbands: 4 8
+#Blocks: 4 8 12 16
+#Bitpool Range: 2-64
+SBC_CAPABILITIES = dbus.Array([dbus.Byte(0xff), dbus.Byte(0xff), dbus.Byte(2), dbus.Byte(64)])
+# JointStereo 44.1Khz Subbands: Blocks: 16 Bitpool Range: 2-32
+SBC_CONFIGURATION = dbus.Array([dbus.Byte(0x21), dbus.Byte(0x15), dbus.Byte(2), dbus.Byte(32)])
+
+MP3_CODEC = dbus.Byte(0x01)
+#Channel Modes: Mono DualChannel Stereo JointStereo
+#Frequencies: 32Khz 44.1Khz 48Khz
+#CRC: YES
+#Layer: 3
+#Bit Rate: All except Free format
+#VBR: Yes
+#Payload Format: RFC-2250
+MP3_CAPABILITIES = dbus.Array([dbus.Byte(0x3f), dbus.Byte(0x07), dbus.Byte(0xff), dbus.Byte(0xfe)])
+# JointStereo 44.1Khz Layer: 3 Bit Rate: VBR Format: RFC-2250
+MP3_CONFIGURATION = dbus.Array([dbus.Byte(0x21), dbus.Byte(0x02), dbus.Byte(0x00), dbus.Byte(0x80)])
+
+PCM_CODEC = dbus.Byte(0x00)
+PCM_CONFIGURATION = dbus.Array([], signature="ay")
+
+class Rejected(dbus.DBusException):
+       _dbus_error_name = "org.bluez.Error.Rejected"
+
+class Endpoint(dbus.service.Object):
+       exit_on_release = True
+       configuration = SBC_CONFIGURATION
+
+       def set_exit_on_release(self, exit_on_release):
+               self.exit_on_release = exit_on_release
+
+       def default_configuration(self, configuration):
+               self.configuration = configuration
+
+       @dbus.service.method("org.bluez.MediaEndpoint",
+                                       in_signature="", out_signature="")
+       def Release(self):
+               print("Release")
+               if self.exit_on_release:
+                       mainloop.quit()
+
+       @dbus.service.method("org.bluez.MediaEndpoint",
+                                       in_signature="", out_signature="")
+       def ClearConfiguration(self):
+               print("ClearConfiguration")
+
+       @dbus.service.method("org.bluez.MediaEndpoint",
+                                       in_signature="oay", out_signature="")
+       def SetConfiguration(self, transport, config):
+               print("SetConfiguration (%s, %s)" % (transport, config))
+               return
+
+       @dbus.service.method("org.bluez.MediaEndpoint",
+                                       in_signature="ay", out_signature="ay")
+       def SelectConfiguration(self, caps):
+               print("SelectConfiguration (%s)" % (caps))
+               return self.configuration
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                               "org.bluez.Manager")
+
+       if len(sys.argv) > 1:
+               path = manager.FindAdapter(sys.argv[1])
+       else:
+               path = manager.DefaultAdapter()
+
+       media = dbus.Interface(bus.get_object("org.bluez", path),
+                                               "org.bluez.Media")
+
+       path = "/test/endpoint"
+       endpoint = Endpoint(bus, path)
+       mainloop = gobject.MainLoop()
+
+       properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
+                                       "Codec" : SBC_CODEC,
+                                       "DelayReporting" : True,
+                                       "Capabilities" : SBC_CAPABILITIES })
+
+       if len(sys.argv) > 2:
+               if sys.argv[2] == "sbcsink":
+                       properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+                                                       "Codec" : SBC_CODEC,
+                                                       "DelayReporting" : True,
+                                                       "Capabilities" : SBC_CAPABILITIES })
+               if sys.argv[2] == "mp3source":
+                       properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
+                                                       "Codec" : MP3_CODEC,
+                                                       "Capabilities" : MP3_CAPABILITIES })
+                       endpoint.default_configuration(MP3_CONFIGURATION)
+               if sys.argv[2] == "mp3sink":
+                       properties = dbus.Dictionary({ "UUID" : A2DP_SINK_UUID,
+                                                       "Codec" : MP3_CODEC,
+                                                       "Capabilities" : MP3_CAPABILITIES })
+                       endpoint.default_configuration(MP3_CONFIGURATION)
+               if sys.argv[2] == "hfpag" or sys.argv[2] == "hspag":
+                       properties = dbus.Dictionary({ "UUID" : HFP_AG_UUID,
+                                                       "Codec" : PCM_CODEC,
+                                                       "Capabilities" :  PCM_CONFIGURATION })
+                       endpoint.default_configuration(dbus.Array([]))
+
+       print(properties)
+
+       media.RegisterEndpoint(path, properties)
+
+       mainloop.run()
diff --git a/test/simple-player b/test/simple-player
new file mode 100755 (executable)
index 0000000..70701da
--- /dev/null
@@ -0,0 +1,125 @@
+#!/usr/bin/python
+
+from __future__ import print_function
+import os
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import gobject
+
+class Player(dbus.service.Object):
+       @dbus.service.method("org.bluez.MediaPlayer",
+                                       in_signature="", out_signature="")
+       def Release(self):
+               print("Release")
+               mainloop.quit()
+
+       @dbus.service.method("org.bluez.MediaPlayer",
+                                       in_signature="sv", out_signature="")
+       def SetProperty(self, key, value):
+               print("SetProperty (%s, %s)" % (key, value), file=sys.stderr)
+               return
+
+       @dbus.service.signal("org.bluez.MediaPlayer", signature="sv")
+       def PropertyChanged(self, setting, value):
+               """PropertyChanged(setting, value)
+
+               Send a PropertyChanged signal. 'setting' and 'value' are
+               string parameters as specified in doc/media-api.txt.
+               """
+               pass
+
+       @dbus.service.signal("org.bluez.MediaPlayer", signature="a{sv}")
+       def TrackChanged(self, metadata):
+               """TrackChanged(metadata)
+
+               Send a TrackChanged signal. 'metadata' parameter is a dictionary,
+               with values as defined in doc/media-api.txt.
+               """
+               pass
+
+       def help(self, func):
+               help(self.__class__.__dict__[func])
+
+class InputHandler:
+       commands = { 'TrackChanged': '(metadata)',
+                                       'PropertyChanged': '(key, value)',
+                                       'help': '(cmd)' }
+       def __init__(self, player):
+               self.player = player
+               print('\n\nAvailable commands:')
+               for cmd in self.commands:
+                       print('\t', cmd, self.commands[cmd], sep='')
+
+               print("\nUse python syntax to pass arguments to available methods.\n" \
+                "E.g.: TrackChanged({'Title': 'My title', 'Album': 'my album' })")
+               self.prompt()
+
+       def prompt(self):
+               print('\n>>> ', end='')
+               sys.stdout.flush()
+
+       def handle(self, fd, condition):
+               s = os.read(fd.fileno(), 1024).strip()
+               try:
+                       cmd = s[:s.find('(')]
+                       if not cmd in self.commands:
+                               print("Unknown command ", cmd)
+               except ValueError:
+                       print("Malformed command")
+                       return True
+
+               try:
+                       exec "self.player.%s" % s
+               except Exception as e:
+                       print(e)
+                       pass
+               self.prompt()
+               return True
+
+
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                               "org.bluez.Manager")
+
+       if len(sys.argv) > 1:
+               path = manager.FindAdapter(sys.argv[1])
+       else:
+               path = manager.DefaultAdapter()
+
+       media = dbus.Interface(bus.get_object("org.bluez", path),
+                                               "org.bluez.Media")
+
+       path = "/test/player"
+       player = Player(bus, path)
+       mainloop = gobject.MainLoop()
+
+       properties = dbus.Dictionary({ "Equalizer" : "off",
+                                       "Repeat" : "off",
+                                       "Shuffle" : "off",
+                                       "Scan" : "off",
+                                       "Status" : "playing",
+                                       "Position" : dbus.UInt32(0) }, signature="sv")
+
+       metadata = dbus.Dictionary({ "Title" : "Title",
+                                       "Artist" : "Artist",
+                                       "Album" : "Album",
+                                       "Genre" : "Genre",
+                                       "NumberOfTracks" : dbus.UInt32(10),
+                                       "Number" : dbus.UInt32(1),
+                                       "Duration" : dbus.UInt32(10000) }, signature="sv")
+
+       print('Register media player with:\n\tProperties: %s\n\tMetadata: %s' \
+                                               % (properties, metadata))
+
+       handler = InputHandler(player)
+       gobject.io_add_watch(sys.stdin, gobject.IO_IN, handler.handle)
+
+       media.RegisterPlayer(dbus.ObjectPath(path), properties, metadata)
+
+       mainloop.run()
diff --git a/test/simple-service b/test/simple-service
new file mode 100755 (executable)
index 0000000..ed27d0c
--- /dev/null
@@ -0,0 +1,129 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import time
+import dbus
+
+xml = ' \
+<?xml version="1.0" encoding="UTF-8" ?>        \
+<record>                                       \
+  <attribute id="0x0001">                      \
+    <sequence>                                 \
+      <uuid value="0x1101"/>                   \
+    </sequence>                                        \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0002">                      \
+     <uint32 value="0"/>                       \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0003">                      \
+    <uuid value="00001101-0000-1000-8000-00805f9b34fb"/> \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0004">                      \
+    <sequence>                                 \
+      <sequence>                               \
+        <uuid value="0x0100"/>                 \
+      </sequence>                              \
+      <sequence>                               \
+        <uuid value="0x0003"/>                 \
+        <uint8 value="23"/>                    \
+      </sequence>                              \
+    </sequence>                                        \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0005">                      \
+    <sequence>                                 \
+      <uuid value="0x1002"/>                   \
+    </sequence>                                        \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0006">                      \
+    <sequence>                                 \
+      <uint16 value="0x656e"/>                 \
+      <uint16 value="0x006a"/>                 \
+      <uint16 value="0x0100"/>                 \
+    </sequence>                                        \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0007">                      \
+     <uint32 value="0"/>                       \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0008">                      \
+     <uint8 value="0xff"/>                     \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0009">                      \
+    <sequence>                                 \
+      <sequence>                               \
+        <uuid value="0x1101"/>                 \
+        <uint16 value="0x0100"/>               \
+      </sequence>                              \
+    </sequence>                                        \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x000a">                      \
+    <url value="http://www.bluez.org/"/>       \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x000b">                      \
+    <url value="http://www.bluez.org/"/>       \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x000c">                      \
+    <url value="http://www.bluez.org/"/>       \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0100">                      \
+    <text value="Serial Port"/>                        \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0101">                      \
+    <text value="Serial Port Service"/>                \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0102">                      \
+     <text value="BlueZ"/>                     \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0200">                      \
+    <sequence>                                 \
+      <uint16 value="0x0100"/>                 \
+    </sequence>                                        \
+  </attribute>                                 \
+                                               \
+  <attribute id="0x0201">                      \
+     <uint32 value="0"/>                       \
+  </attribute>                                 \
+</record>                                      \
+'
+
+bus = dbus.SystemBus()
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                               "org.bluez.Manager")
+
+if len(sys.argv) > 1:
+       path = manager.FindAdapter(sys.argv[1])
+else:
+       path = manager.DefaultAdapter()
+
+service = dbus.Interface(bus.get_object("org.bluez", path),
+                                               "org.bluez.Service")
+
+handle = service.AddRecord(xml)
+
+print("Service record with handle 0x%04x added" % (handle))
+
+print("Press CTRL-C to remove service record")
+
+try:
+       time.sleep(1000)
+       print("Terminating session")
+except:
+       pass
+
+service.RemoveRecord(dbus.UInt32(handle))
diff --git a/test/test-adapter b/test/test-adapter
new file mode 100755 (executable)
index 0000000..4e2f029
--- /dev/null
@@ -0,0 +1,139 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import dbus
+import time
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+if (len(args) < 1):
+       print("Usage: %s <command>" % (sys.argv[0]))
+       print("")
+       print("  address")
+       print("  list")
+       print("  name [name]")
+       print("  powered [on/off]")
+       print("  pairable [on/off]")
+       print("  pairabletimeout [timeout]")
+       print("  discoverable [on/off]")
+       print("  discoverabletimeout [timeout]")
+       print("  discovering")
+       sys.exit(1)
+
+if (args[0] == "address"):
+       properties = adapter.GetProperties()
+       print(properties["Address"])
+       sys.exit(0)
+
+if (args[0] == "name"):
+       if (len(args) < 2):
+               properties = adapter.GetProperties()
+               print(properties["Name"])
+       else:
+               adapter.SetProperty("Name", args[1])
+       sys.exit(0)
+
+if (args[0] == "list"):
+       if (len(args) < 2):
+               properties = manager.GetProperties()
+               for adapter_path in properties["Adapters"]:
+                       adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                               "org.bluez.Adapter")
+                       prop = adapter.GetProperties()
+                       print(" [ %s ]" % (adapter_path))
+                       for (key, value) in prop.items():
+                               if (key == "Class"):
+                                       print("    %s = 0x%06x" % (key, value))
+                               else:
+                                       print("    %s = %s" % (key, value))
+                       print()
+       sys.exit(0)
+
+if (args[0] == "powered"):
+       if (len(args) < 2):
+               properties = adapter.GetProperties()
+               print(properties["Powered"])
+       else:
+               if (args[1] == "on"):
+                       value = dbus.Boolean(1)
+               elif (args[1] == "off"):
+                       value = dbus.Boolean(0)
+               else:
+                       value = dbus.Boolean(args[1])
+               adapter.SetProperty("Powered", value)
+       sys.exit(0)
+
+if (args[0] == "pairable"):
+       if (len(args) < 2):
+               properties = adapter.GetProperties()
+               print(properties["Pairable"])
+       else:
+               if (args[1] == "on"):
+                       value = dbus.Boolean(1)
+               elif (args[1] == "off"):
+                       value = dbus.Boolean(0)
+               else:
+                       value = dbus.Boolean(args[1])
+               adapter.SetProperty("Pairable", value)
+       sys.exit(0)
+
+if (args[0] == "pairabletimeout"):
+       if (len(args) < 2):
+               properties = adapter.GetProperties()
+               print(properties["PairableTimeout"])
+       else:
+               timeout = dbus.UInt32(args[1])
+               adapter.SetProperty("PairableTimeout", timeout)
+       sys.exit(0)
+
+if (args[0] == "discoverable"):
+       if (len(args) < 2):
+               properties = adapter.GetProperties()
+               print(properties["Discoverable"])
+       else:
+               if (args[1] == "on"):
+                       value = dbus.Boolean(1)
+               elif (args[1] == "off"):
+                       value = dbus.Boolean(0)
+               else:
+                       value = dbus.Boolean(args[1])
+               adapter.SetProperty("Discoverable", value)
+       sys.exit(0)
+
+if (args[0] == "discoverabletimeout"):
+       if (len(args) < 2):
+               properties = adapter.GetProperties()
+               print(properties["DiscoverableTimeout"])
+       else:
+               timeout = dbus.UInt32(args[1])
+               adapter.SetProperty("DiscoverableTimeout", timeout)
+       sys.exit(0)
+
+if (args[0] == "discovering"):
+       properties = adapter.GetProperties()
+       print(properties["Discovering"])
+       sys.exit(0)
+
+print("Unknown command")
+sys.exit(1)
diff --git a/test/test-attrib b/test/test-attrib
new file mode 100755 (executable)
index 0000000..52b399c
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+# Script for testing the Attribute D-Bus API
+
+import sys
+from optparse import OptionParser, OptionValueError
+from binascii import hexlify, unhexlify
+
+import gobject
+
+import sys
+import dbus
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+bus = dbus.SystemBus()
+mainloop = gobject.MainLoop()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+if (len(args) < 1):
+       print("Usage: %s <command>" % (sys.argv[0]))
+       print("")
+       print("  list")
+       print("  services <address>")
+       print("  discover <service path>")
+       print("  chars <service path>")
+       sys.exit(1)
+
+if (args[0] == "list"):
+       for path in adapter.ListDevices():
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               devprop = device.GetProperties()
+               print("[ %s ]" % devprop["Address"])
+               for path in devprop["Services"]:
+
+                       service = dbus.Interface(bus.get_object("org.bluez", path),
+                                                                        "org.bluez.Characteristic")
+                       srvprop = service.GetProperties()
+                       print(" * %s" % (path))
+                       print(" UUID: %s" % srvprop["UUID"])
+                       print(" Chars: ",)
+                       for char in srvprop["Characteristics"]:
+                               print("%s " % char,)
+                       print()
+                       print()
+               print()
+       sys.exit(0)
+
+if (args[0] == "services"):
+       if (len(args) < 2):
+               print("Need address parameter")
+       else:
+               path = adapter.FindDevice(args[1])
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               properties = device.GetProperties()
+               for path in properties["Services"]:
+                       print(path)
+       sys.exit(0)
+
+if (args[0] == "discover"):
+       if (len(args) < 2):
+               print("Need service path parameter")
+       else:
+               service = dbus.Interface(bus.get_object("org.bluez", args[1]),
+                                                       "org.bluez.Characteristic")
+               for path in service.DiscoverCharacteristics():
+                       print(path)
+       sys.exit(0)
+
+if (args[0] == "chars"):
+       if (len(args) < 2):
+               print("Need service path parameter")
+       else:
+               service = dbus.Interface(bus.get_object("org.bluez", args[1]),
+                                                                "org.bluez.Characteristic")
+               srvprop = service.GetProperties()
+               for path in srvprop["Characteristics"]:
+                       print("[ %s ]" % (path))
+                       char = dbus.Interface(bus.get_object("org.bluez", path),
+                                                                "org.bluez.Characteristic")
+                       charprop = char.GetProperties()
+                       print(" Name: %s" % charprop["Name"])
+                       print(" UUID: %s" % charprop["UUID"])
+                       print()
+               print()
+       sys.exit(0)
+
+print("Unknown command")
+sys.exit(1)
diff --git a/test/test-audio b/test/test-audio
new file mode 100755 (executable)
index 0000000..1ba2042
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+if len(args) < 2:
+       print("""Usage: %s <command>
+
+       connect <bdaddr>
+       disconnect <bdaddr>
+       """ % sys.argv[0])
+       sys.exit(1)
+
+device = adapter.FindDevice(args[1])
+audio = dbus.Interface(bus.get_object("org.bluez", device),
+                               "org.bluez.Audio")
+
+if args[0] == "connect":
+       audio.Connect()
+elif args[0] == "disconnect":
+       audio.Disconnect()
+else:
+       print("Unknown command")
+       sys.exit(1)
diff --git a/test/test-device b/test/test-device
new file mode 100755 (executable)
index 0000000..81a44f8
--- /dev/null
@@ -0,0 +1,209 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
+
+import sys
+import dbus
+import dbus.mainloop.glib
+import re
+from optparse import OptionParser, make_option
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+bus = dbus.SystemBus()
+mainloop = GObject.MainLoop()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+if (len(args) < 1):
+       print("Usage: %s <command>" % (sys.argv[0]))
+       print("")
+       print("  list")
+       print("  services <address>")
+       print("  create <address>")
+       print("  remove <address|path>")
+       print("  disconnect <address>")
+       print("  discover <address> [pattern]")
+       print("  class <address>")
+       print("  name <address>")
+       print("  alias <address> [alias]")
+       print("  trusted <address> [yes/no]")
+       print("  blocked <address> [yes/no]")
+       sys.exit(1)
+
+if (args[0] == "list"):
+       for path in adapter.ListDevices():
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               properties = device.GetProperties()
+               print("%s %s" % (properties["Address"], properties["Alias"]))
+
+       sys.exit(0)
+
+def create_device_reply(device):
+       print("New device (%s)" % device)
+       mainloop.quit()
+       sys.exit(0)
+
+def create_device_error(error):
+       print("Creating device failed: %s" % error)
+       mainloop.quit()
+       sys.exit(1)
+
+if (args[0] == "create"):
+       if (len(args) < 2):
+               print("Need address parameter")
+       else:
+               adapter.CreateDevice(args[1],
+                               reply_handler=create_device_reply,
+                               error_handler=create_device_error)
+       mainloop.run()
+
+if (args[0] == "remove"):
+       if (len(args) < 2):
+               print("Need address or object path parameter")
+       else:
+               try:
+                       path = adapter.FindDevice(args[1])
+               except:
+                       path = args[1]
+               adapter.RemoveDevice(path)
+       sys.exit(0)
+
+if (args[0] == "disconnect"):
+       if (len(args) < 2):
+               print("Need address parameter")
+       else:
+               path = adapter.FindDevice(args[1])
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               device.Disconnect()
+       sys.exit(0)
+
+if (args[0] == "discover"):
+       if (len(args) < 2):
+               print("Need address parameter")
+       else:
+               path = adapter.FindDevice(args[1])
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               if (len(args) < 3):
+                       pattern = ""
+               else:
+                       pattern = args[2]
+               services = device.DiscoverServices(pattern);
+               for key in services.keys():
+                       p = re.compile(">.*?<")
+                       xml = p.sub("><", services[key].replace("\n", ""))
+                       print("[ 0x%5x ]" % (key))
+                       print(xml)
+                       print()
+       sys.exit(0)
+
+if (args[0] == "class"):
+       if (len(args) < 2):
+               print("Need address parameter")
+       else:
+               path = adapter.FindDevice(args[1])
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               properties = device.GetProperties()
+               print("0x%06x" % (properties["Class"]))
+       sys.exit(0)
+
+if (args[0] == "name"):
+       if (len(args) < 2):
+               print("Need address parameter")
+       else:
+               path = adapter.FindDevice(args[1])
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               properties = device.GetProperties()
+               print(properties["Name"])
+       sys.exit(0)
+
+if (args[0] == "alias"):
+       if (len(args) < 2):
+               print("Need address parameter")
+       else:
+               path = adapter.FindDevice(args[1])
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               if (len(args) < 3):
+                       properties = device.GetProperties()
+                       print(properties["Alias"])
+               else:
+                       device.SetProperty("Alias", args[2])
+       sys.exit(0)
+
+if (args[0] == "trusted"):
+       if (len(args) < 2):
+               print("Need address parameter")
+       else:
+               path = adapter.FindDevice(args[1])
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               if (len(args) < 3):
+                       properties = device.GetProperties()
+                       print(properties["Trusted"])
+               else:
+                       if (args[2] == "yes"):
+                               value = dbus.Boolean(1)
+                       elif (args[2] == "no"):
+                               value = dbus.Boolean(0)
+                       else:
+                               value = dbus.Boolean(args[2])
+                       device.SetProperty("Trusted", value)
+       sys.exit(0)
+
+if (args[0] == "blocked"):
+       if (len(args) < 2):
+               print("Need address parameter")
+       else:
+               path = adapter.FindDevice(args[1])
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               if (len(args) < 3):
+                       properties = device.GetProperties()
+                       print(properties["Blocked"])
+               else:
+                       if (args[2] == "yes"):
+                               value = dbus.Boolean(1)
+                       elif (args[2] == "no"):
+                               value = dbus.Boolean(0)
+                       else:
+                               value = dbus.Boolean(args[2])
+                       device.SetProperty("Blocked", value)
+       sys.exit(0)
+
+if (args[0] == "services"):
+       if (len(args) < 2):
+               print("Need address parameter")
+       else:
+               path = adapter.FindDevice(args[1])
+               device = dbus.Interface(bus.get_object("org.bluez", path),
+                                                       "org.bluez.Device")
+               properties = device.GetProperties()
+               for path in properties["Services"]:
+                       print(path)
+       sys.exit(0)
+
+print("Unknown command")
+sys.exit(1)
diff --git a/test/test-discovery b/test/test-discovery
new file mode 100755 (executable)
index 0000000..269c51c
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
+
+import dbus
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+def device_found(address, properties):
+       print("[ " + address + " ]")
+
+       for key in properties.keys():
+               value = properties[key]
+               if type(value) is dbus.String:
+                       value = unicode(value).encode('ascii', 'replace')
+               if (key == "Class"):
+                       print("    %s = 0x%06x" % (key, value))
+               else:
+                       print("    %s = %s" % (key, value))
+
+       print()
+
+def property_changed(name, value):
+       if (name == "Discovering" and not value):
+               mainloop.quit()
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                                       "org.bluez.Manager")
+
+       option_list = [
+                       make_option("-i", "--device", action="store",
+                                       type="string", dest="dev_id"),
+                       ]
+       parser = OptionParser(option_list=option_list)
+
+       (options, args) = parser.parse_args()
+
+       if options.dev_id:
+               adapter_path = manager.FindAdapter(options.dev_id)
+       else:
+               adapter_path = manager.DefaultAdapter()
+
+       adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+       bus.add_signal_receiver(device_found,
+                       dbus_interface = "org.bluez.Adapter",
+                                       signal_name = "DeviceFound")
+
+       bus.add_signal_receiver(property_changed,
+                       dbus_interface = "org.bluez.Adapter",
+                                       signal_name = "PropertyChanged")
+
+       adapter.StartDiscovery()
+
+       mainloop = GObject.MainLoop()
+       mainloop.run()
diff --git a/test/test-health b/test/test-health
new file mode 100755 (executable)
index 0000000..f7d4241
--- /dev/null
@@ -0,0 +1,219 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+# -*- coding: utf-8 -*-
+
+import dbus
+import dbus.service
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+import sys
+
+DBusGMainLoop(set_as_default=True)
+loop = gobject.MainLoop()
+
+bus = dbus.SystemBus()
+
+def sig_received(*args, **kwargs):
+       if "member" not in kwargs:
+               return
+       if "path" not in kwargs:
+               return;
+       sig_name = kwargs["member"]
+       path = kwargs["path"]
+       print(sig_name)
+       print(path)
+       if sig_name == "PropertyChanged":
+               k, v = args
+               print(k)
+               print(v)
+       else:
+               ob = args[0]
+               print(ob)
+
+
+def enter_mainloop():
+       bus.add_signal_receiver(sig_received, bus_name="org.bluez",
+                               dbus_interface = "org.bluez.HealthDevice",
+                               path_keyword="path",
+                               member_keyword="member",
+                               interface_keyword="interface")
+
+       try:
+               print("Entering main lopp, push Ctrl+C for finish")
+
+               mainloop = gobject.MainLoop()
+               mainloop.run()
+       except KeyboardInterrupt:
+               pass
+       finally:
+               print("Exiting, bye")
+
+hdp_manager = dbus.Interface(bus.get_object("org.bluez", "/org/bluez"),
+                                               "org.bluez.HealthManager")
+
+role = None
+while role == None:
+       print("Select 1. source or 2. sink: ",)
+       try:
+               sel = int(sys.stdin.readline())
+               if sel == 1:
+                       role = "Source"
+               elif sel == 2:
+                       role = "Sink"
+               else:
+                       raise ValueError
+       except (TypeError, ValueError):
+               print("Wrong selection, try again: ",)
+       except KeyboardInterrupt:
+               sys.exit()
+
+dtype = None
+while dtype == None:
+       print("Select a data type: ",)
+       try:
+               sel = int(sys.stdin.readline())
+               if (sel < 0) or (sel > 65535):
+                       raise ValueError
+               dtype = sel;
+       except (TypeError, ValueError):
+               print("Wrong selection, try again: ",)
+       except KeyboardInterrupt:
+               sys.exit()
+
+pref = None
+if role == "Source":
+       while pref == None:
+               try:
+                       print("Select a preferred data channel type 1.",)
+                       print("reliable 2. streaming: ",)
+                       sel = int(sys.stdin.readline())
+                       if sel == 1:
+                               pref = "Reliable"
+                       elif sel == 2:
+                               pref = "Streaming"
+                       else:
+                               raise ValueError
+
+               except (TypeError, ValueError):
+                       print("Wrong selection, try again")
+               except KeyboardInterrupt:
+                       sys.exit()
+
+       app_path = hdp_manager.CreateApplication({
+                                       "DataType": dbus.types.UInt16(dtype),
+                                       "Role": role,
+                                       "Description": "Test Source",
+                                       "ChannelType": pref})
+else:
+       app_path = hdp_manager.CreateApplication({
+                                       "DataType": dbus.types.UInt16(dtype),
+                                       "Description": "Test sink",
+                                       "Role": role})
+
+print("New application created:", app_path)
+
+con = None
+while con == None:
+       try:
+               print("Connect to a remote device (y/n)? ",)
+               sel = sys.stdin.readline()
+               if sel in ("y\n", "yes\n", "Y\n", "YES\n"):
+                       con = True
+               elif sel in ("n\n", "no\n", "N\n", "NO\n"):
+                       con = False
+               else:
+                       print("Wrong selection, try again.")
+       except KeyboardInterrupt:
+               sys.exit()
+
+if not con:
+       enter_mainloop()
+       sys.exit()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                               "org.bluez.Manager")
+
+adapters = manager.ListAdapters()
+
+i = 1
+for ad in adapters:
+       print("%d. %s" % (i, ad))
+       i = i + 1
+
+print("Select an adapter: ",)
+select = None
+while select == None:
+       try:
+               pos = int(sys.stdin.readline()) - 1
+               if pos < 0:
+                       raise TypeError
+               select = adapters[pos]
+       except (TypeError, IndexError, ValueError):
+               print("Wrong selection, try again: ",)
+       except KeyboardInterrupt:
+               sys.exit()
+
+adapter =  dbus.Interface(bus.get_object("org.bluez", select),
+                                               "org.bluez.Adapter")
+
+devices = adapter.ListDevices()
+
+if len(devices) == 0:
+       print("No devices available")
+       sys.exit()
+
+i = 1
+for dev in devices:
+       print("%d. %s" % (i, dev))
+       i = i + 1
+
+print("Select a device: ",)
+select = None
+while select == None:
+       try:
+               pos = int(sys.stdin.readline()) - 1
+               if pos < 0:
+                       raise TypeError
+               select = devices[pos]
+       except (TypeError, IndexError, ValueError):
+               print("Wrong selection, try again: ",)
+       except KeyboardInterrupt:
+               sys.exit()
+
+device = dbus.Interface(bus.get_object("org.bluez", select),
+                                       "org.bluez.HealthDevice")
+
+echo = None
+while echo == None:
+       try:
+               print("Perform an echo (y/n)? ",)
+               sel = sys.stdin.readline()
+               if sel in ("y\n", "yes\n", "Y\n", "YES\n"):
+                       echo = True
+               elif sel in ("n\n", "no\n", "N\n", "NO\n"):
+                       echo = False
+               else:
+                       print("Wrong selection, try again.")
+       except KeyboardInterrupt:
+               sys.exit()
+
+if echo:
+       if device.Echo():
+               print("Echo was ok")
+       else:
+               print("Echo war wrong, exiting")
+               sys.exit()
+
+print("Connecting to device %s" % (select))
+
+if role == "Source":
+       chan = device.CreateChannel(app_path, "Reliable")
+else:
+       chan = device.CreateChannel(app_path, "Any")
+
+print(chan)
+
+enter_mainloop()
+
+hdp_manager.DestroyApplication(app_path)
diff --git a/test/test-health-sink b/test/test-health-sink
new file mode 100755 (executable)
index 0000000..ce7337a
--- /dev/null
@@ -0,0 +1,85 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+# -*- coding: utf-8 -*-
+
+import dbus
+import dbus.service
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+import sys
+
+DBusGMainLoop(set_as_default=True)
+loop = gobject.MainLoop()
+
+bus = dbus.SystemBus()
+
+hdp_manager = dbus.Interface(bus.get_object("org.bluez", "/org/bluez"),
+                                               "org.bluez.HealthManager")
+app_path = hdp_manager.CreateApplication({"DataType": dbus.types.UInt16(4103),
+                                       "Role": "sink"})
+
+print(app_path)
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                               "org.bluez.Manager")
+
+adapters = manager.ListAdapters()
+
+i = 1
+for ad in adapters:
+       print("%d. %s" % (i, ad))
+       i = i + 1
+
+print("Select an adapter: ",)
+select = None
+while select == None:
+       try:
+               pos = int(sys.stdin.readline()) - 1
+               if pos < 0:
+                       raise TypeError
+               select = adapters[pos]
+       except (TypeError, IndexError, ValueError):
+               print("Wrong selection, try again: ",)
+       except KeyboardInterrupt:
+               sys.exit()
+
+adapter =  dbus.Interface(bus.get_object("org.bluez", select),
+                                               "org.bluez.Adapter")
+
+devices = adapter.ListDevices()
+
+if len(devices) == 0:
+       print("No devices available")
+       sys.exit()
+
+i = 1
+for dev in devices:
+       print("%d. %s" % (i, dev))
+       i = i + 1
+
+print("Select a device: ",)
+select = None
+while select == None:
+       try:
+               pos = int(sys.stdin.readline()) - 1
+               if pos < 0:
+                       raise TypeError
+               select = devices[pos]
+       except (TypeError, IndexError, ValueError):
+               print("Wrong selection, try again: ",)
+       except KeyboardInterrupt:
+               sys.exit()
+
+print("Connecting to %s" % (select))
+device = dbus.Interface(bus.get_object("org.bluez", select),
+                                       "org.bluez.HealthDevice")
+
+chan = device.CreateChannel(app_path, "Any")
+
+print(chan)
+
+print("Push Enter for finishing")
+sys.stdin.readline()
+
+hdp_manager.DestroyApplication(app_path)
diff --git a/test/test-input b/test/test-input
new file mode 100755 (executable)
index 0000000..110cbef
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+if len(args) < 2:
+       print("""Usage: %s <command>
+
+       connect <bdaddr>
+       disconnect <bdaddr>
+       """ % sys.argv[0])
+       sys.exit(1)
+
+device = adapter.FindDevice(args[1])
+input = dbus.Interface(bus.get_object("org.bluez", device),
+                               "org.bluez.Input")
+
+if args[0] == "connect":
+       input.Connect()
+elif args[0] == "disconnect":
+       input.Disconnect()
+else:
+       print("Unknown command")
+       sys.exit(1)
diff --git a/test/test-manager b/test/test-manager
new file mode 100755 (executable)
index 0000000..8a7e2f6
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
+
+import dbus
+import dbus.mainloop.glib
+
+def adapter_added(path):
+       print("Adapter with path %s added" % (path))
+
+def adapter_removed(path):
+       print("Adapter with path %s removed" % (path))
+
+def default_changed(path):
+       print("Default adapter is now at path %s" % (path))
+
+if __name__ == "__main__":
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object('org.bluez', '/'),
+                                                       'org.bluez.Manager')
+
+       manager.connect_to_signal("AdapterAdded", adapter_added)
+
+       manager.connect_to_signal("AdapterRemoved", adapter_removed)
+
+       manager.connect_to_signal("DefaultAdapterChanged", default_changed)
+
+       try:
+               path = manager.DefaultAdapter()
+               default_changed(path)
+       except:
+               pass
+
+       mainloop = GObject.MainLoop()
+       mainloop.run()
diff --git a/test/test-nap b/test/test-nap
new file mode 100755 (executable)
index 0000000..dc779ad
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import time
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                               "org.bluez.Manager")
+
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+server = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                               "org.bluez.NetworkServer")
+
+service = "nap"
+
+if (len(args) < 1):
+       bridge = "tether"
+else:
+       bridge = args[0]
+
+server.Register(service, bridge)
+
+print("Server for %s registered for %s" % (service, bridge))
+
+print("Press CTRL-C to disconnect")
+
+try:
+       time.sleep(1000)
+       print("Terminating connection")
+except:
+       pass
+
+server.Unregister(service)
diff --git a/test/test-network b/test/test-network
new file mode 100755 (executable)
index 0000000..2ade584
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import time
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                               "org.bluez.Manager")
+
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+if (len(args) < 1):
+       print("Usage: %s <address> [service]" % (sys.argv[0]))
+       sys.exit(1)
+
+address = args[0]
+
+if (len(args) < 2):
+       service = "panu"
+else:
+       service = args[1]
+
+device = adapter.FindDevice(address)
+
+network = dbus.Interface(bus.get_object("org.bluez", device),
+                                               "org.bluez.Network")
+
+iface = network.Connect(service)
+
+print("Connected %s to %s" % (device, address))
+
+print("Press CTRL-C to disconnect")
+
+try:
+       time.sleep(1000)
+       print("Terminating connection")
+except:
+       pass
+
+network.Disconnect()
diff --git a/test/test-oob b/test/test-oob
new file mode 100755 (executable)
index 0000000..bec9de5
--- /dev/null
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import gobject
+
+import dbus.mainloop.glib
+
+def create_device_reply(device):
+       print("Pairing succeed!")
+       mainloop.quit()
+
+def create_device_error(error):
+       print("Pairing failed.")
+       mainloop.quit()
+
+if __name__ == '__main__':
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       mainloop = gobject.MainLoop()
+
+       bus = dbus.SystemBus()
+       manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                                       "org.bluez.Manager")
+
+       adapter0_path = manager.FindAdapter("hci0")
+       adapter1_path = manager.FindAdapter("hci1")
+
+       adapter0 = dbus.Interface(bus.get_object("org.bluez", adapter0_path),
+                                                       "org.bluez.Adapter")
+       adapter1 = dbus.Interface(bus.get_object("org.bluez", adapter1_path),
+                                                       "org.bluez.Adapter")
+
+       adapter0_address = adapter0.GetProperties()["Address"]
+       adapter1_address = adapter1.GetProperties()["Address"]
+       print("Adapters:")
+       print("    hci0: " + adapter0_address)
+       print("    hci1: " + adapter1_address)
+       print()
+
+       print("Removing any existing bond...")
+
+       try:
+               device = adapter0.FindDevice(adapter1_address)
+               adapter0.RemoveDevice(device)
+       except:
+               pass
+
+       try:
+               device = adapter1.FindDevice(adapter0_address)
+               adapter1.RemoveDevice(device)
+       except:
+               pass
+
+       print("Done.")
+       print()
+       print("Reading local Out of Band data...")
+
+       oob_adapter0 = dbus.Interface(bus.get_object("org.bluez",
+                                       adapter0_path), "org.bluez.OutOfBand")
+       oob_adapter1 = dbus.Interface(bus.get_object("org.bluez",
+                                       adapter1_path), "org.bluez.OutOfBand")
+
+       oob0 = oob_adapter0.ReadLocalData()
+       oob1 = oob_adapter1.ReadLocalData()
+
+       print("Done.")
+       print()
+       print("Exchanging Out of Band data...")
+
+       oob_adapter0.AddRemoteData(adapter1_address, oob1[0], oob1[1])
+       oob_adapter1.AddRemoteData(adapter0_address, oob0[0], oob0[1])
+
+       print("Done.")
+       print()
+       print("Starting to pair.")
+       adapter1.CreatePairedDevice(adapter0_address, "/test/agent_oob",
+                                       "DisplayYesNo",
+                                       reply_handler=create_device_reply,
+                                       error_handler=create_device_error)
+
+       mainloop.run()
diff --git a/test/test-proximity b/test/test-proximity
new file mode 100755 (executable)
index 0000000..b08a62a
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+'''
+Proximity Monitor test script
+'''
+
+import gobject
+
+import sys
+import dbus
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+def property_changed(name, value):
+
+       print("PropertyChanged('%s', '%s')" % (name, value))
+       mainloop.quit()
+
+if __name__ == "__main__":
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                       "org.bluez.Manager")
+
+       option_list = [
+               make_option("-i", "--adapter", action="store",
+                       type="string", dest="dev_id"),
+               make_option("-b", "--device", action="store",
+                       type="string", dest="address"),
+
+               ]
+       parser = OptionParser(option_list=option_list)
+
+       (options, args) = parser.parse_args()
+
+       if options.dev_id:
+               adapter_path = manager.FindAdapter(options.dev_id)
+       else:
+               adapter_path = manager.DefaultAdapter()
+
+       adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+       if (len(args) < 1):
+               print("Usage: %s <command>" % (sys.argv[0]))
+               print("")
+               print("  -b MAC LinkLossAlertLevel <none|mild|high>")
+               print("  -b MAC ImmediateAlertLevel <none|mild|high>")
+               sys.exit(1)
+
+       device_path = adapter.FindDevice(options.address)
+
+       bus.add_signal_receiver(property_changed, bus_name="org.bluez",
+                               dbus_interface="org.bluez.ProximityMonitor",
+                               signal_name="PropertyChanged")
+
+       proximity = dbus.Interface(bus.get_object("org.bluez",
+                                       device_path), "org.bluez.ProximityMonitor")
+
+       print("Proximity SetProperty('%s', '%s')" % (args[0], args[1]))
+       proximity.SetProperty(args[0], args[1])
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/test-sap-server b/test/test-sap-server
new file mode 100755 (executable)
index 0000000..df838f6
--- /dev/null
@@ -0,0 +1,140 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from sap import *
+import time
+
+def connect_disconnect_by_client(sap):
+
+    print("[Test] Connect - Disconnect by client \n")
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if sap.proc_disconnectByClient():
+                print("OK")
+                return 0
+
+        print("NOT OK")
+        return 1
+
+    except BluetoothError as e:
+        print("Error " + str(e))
+
+
+def connect_disconnect_by_server_gracefully(sap, timeout=0):
+
+    print("[Test] Connect - Disconnect by server with timer \n")
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if sap.proc_disconnectByServer(timeout):
+                print("OK")
+                return 0
+
+        print("NOT OK")
+        return 1
+
+    except BluetoothError as e:
+        print("Error " + str(e))
+
+
+def connect_txAPDU_disconnect_by_client(sap):
+
+    print("[Test] Connect - TX APDU - Disconnect by client \n")
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if not sap.proc_transferAPDU():
+                print("NOT OK 1")
+                return 1
+
+            if not sap.proc_transferAPDU():
+                print("NOT OK 2")
+                return 1
+
+            if not sap.proc_transferAPDU():
+                print("NOT OK 3")
+                return 1
+
+            if not sap.proc_transferAPDU():
+                print("NOT OK 4")
+                return 1
+
+            if sap.proc_disconnectByClient():
+                print("OK")
+                return 0
+
+        print("NOT OK")
+        return 1
+
+    except BluetoothError as e:
+        print("Error " + str(e))
+
+def connect_rfcomm_only_and_wait_for_close_by_server(sap):
+
+    print("[Test] Connect rfcomm only  - Disconnect by server timeout \n")
+
+    if not sap.isConnected():
+       sap.connect()
+
+    time.sleep(40)
+    print("OK")
+
+def power_sim_off_on(sap):
+
+    print("[Test] Powe sim off \n")
+
+    try:
+        if not sap.isConnected():
+           sap.connect()
+
+        if sap.proc_connect():
+            if not sap.proc_resetSim():
+                print("NOT OK")
+                return 1
+
+            if not sap.proc_powerSimOff():
+                print("NOT OK")
+                return 1
+
+            if not sap.proc_powerSimOn():
+                print("NOT OK")
+                return 1
+
+            if sap.proc_disconnectByClient():
+                print("OK")
+                return 0
+
+        print("NOT OK")
+        return 1
+
+    except BluetoothError as e:
+        print("Error " + str(e))
+
+
+if __name__ == "__main__":
+
+    host = "00:00:00:00:00:0"  # server bd_addr
+    port = 8  # sap server port
+
+    try:
+        s = SAPClient(host, port)
+    except BluetoothError as e:
+        print("Error " + str(e))
+
+    connect_disconnect_by_client(s)
+    connect_disconnect_by_server_gracefully(s)
+    connect_disconnect_by_server_gracefully(s, 40)  #  wait 40 sec for srv to close rfcomm sock
+    connect_rfcomm_only_and_wait_for_close_by_server(s)
+    connect_txAPDU_disconnect_by_client(s)
+    power_sim_off_on(s)
diff --git a/test/test-serial b/test/test-serial
new file mode 100755 (executable)
index 0000000..8858dbd
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import time
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                               "org.bluez.Manager")
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+if (len(args) < 1):
+       print("Usage: %s <address> [service]" % (sys.argv[0]))
+       sys.exit(1)
+
+address = args[0]
+
+if (len(args) < 2):
+       service = "spp"
+else:
+       service = args[1]
+
+path = adapter.FindDevice(address)
+
+serial = dbus.Interface(bus.get_object("org.bluez", path),
+                                               "org.bluez.Serial")
+
+node = serial.Connect(service)
+
+print("Connected %s to %s" % (node, address))
+
+print("Press CTRL-C to disconnect")
+
+try:
+       time.sleep(1000)
+       print("Terminating connection")
+except:
+       pass
+
+serial.Disconnect(node)
diff --git a/test/test-serial-proxy b/test/test-serial-proxy
new file mode 100755 (executable)
index 0000000..7963f23
--- /dev/null
@@ -0,0 +1,66 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import time
+import dbus
+import socket
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                               "org.bluez.Manager")
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+if (len(args) < 1):
+       print("Usage: %s <socket_name> [service]" % (sys.argv[0]))
+       sys.exit(1)
+
+socket_name = args[0]
+
+if (len(args) < 2):
+       service = "spp"
+else:
+       service = args[1]
+
+sk = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+sk.bind(socket_name)
+sk.listen(1)
+
+proxy_manager = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                               "org.bluez.SerialProxyManager")
+proxy_path = proxy_manager.CreateProxy(service, socket_name)
+
+proxy = dbus.Interface(bus.get_object("org.bluez", proxy_path),
+                                                       "org.bluez.SerialProxy")
+proxy.Enable()
+
+conn, addr = sk.accept()
+
+print("Waiting for message")
+
+while 1:
+       data = conn.recv(1024)
+       if data:
+               print(data)
+               break
+
+proxy.Disable()
+proxy_manager.RemoveProxy(proxy_path)
+conn.close()
diff --git a/test/test-service b/test/test-service
new file mode 100755 (executable)
index 0000000..8eea9e2
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import dbus
+import time
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+service = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                               "org.bluez.Service")
+
+if (len(args) < 1):
+       print("Usage: %s <command>" % (sys.argv[0]))
+       print("")
+       print("  addrecord <file>")
+       sys.exit(1)
+
+if (args[0] == "addrecord"):
+       if (len(args) < 2):
+               print("Need file parameter")
+       else:
+               f = open(args[1])
+               record = f.read()
+               f.close()
+               handle = service.AddRecord(record)
+               print("0x%x" % (handle))
+               time.sleep(120)
+       sys.exit(0)
+
+print("Unknown command")
+sys.exit(1)
diff --git a/test/test-telephony b/test/test-telephony
new file mode 100755 (executable)
index 0000000..bd7d3b2
--- /dev/null
@@ -0,0 +1,178 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
+
+option_list = [
+               make_option("-i", "--device", action="store",
+                               type="string", dest="dev_id"),
+               ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+       adapter_path = manager.FindAdapter(options.dev_id)
+else:
+       adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+test = dbus.Interface(bus.get_object("org.bluez", "/org/bluez/test"),
+                       "org.bluez.TelephonyTest")
+
+if len(args) < 1:
+       print("""Usage: %s <command>
+
+       connect <bdaddr>
+       disconnect <bdaddr>
+       outgoing <number>
+       incoming <number>
+       cancel
+       signal <level>
+       battery <level>
+       roaming <yes|no>
+       registration <status>
+       subscriber <number>
+       speakergain <bdaddr> [level]
+       microphonegain <bdaddr> [level]
+       play <bdaddr>
+       stop <bdaddr>
+       """ % sys.argv[0])
+       sys.exit(1)
+
+if args[0] == "connect":
+       if len(args) < 2:
+               print("Need device address parameter")
+               sys.exit(1)
+       device = adapter.FindDevice(args[1])
+       headset = dbus.Interface(bus.get_object("org.bluez", device),
+                                       "org.bluez.Headset")
+       headset.Connect()
+       sys.exit(0)
+
+if args[0] == "disconnect":
+       if len(args) < 2:
+               print("Need device address parameter")
+               sys.exit(1)
+       device = adapter.FindDevice(args[1])
+       headset = dbus.Interface(bus.get_object("org.bluez", device),
+                                       "org.bluez.Headset")
+       headset.Disconnect()
+       sys.exit(0)
+
+if args[0] == "speakergain":
+       if len(args) < 2:
+               print("Need device address parameter")
+               sys.exit(1)
+       device = adapter.FindDevice(args[1])
+       headset = dbus.Interface(bus.get_object("org.bluez", device),
+                                       "org.bluez.Headset")
+       if len(args) > 2:
+               headset.SetProperty('SpeakerGain', dbus.UInt16(args[2]))
+       else:
+               props = headset.GetProperties()
+               print(props['SpeakerGain'])
+
+       sys.exit(0)
+
+if args[0] == "microphonegain":
+       if len(args) < 2:
+               print("Need device address parameter")
+               sys.exit(1)
+       device = adapter.FindDevice(args[1])
+       headset = dbus.Interface(bus.get_object("org.bluez", device),
+                                       "org.bluez.Headset")
+       if len(args) > 2:
+               headset.SetProperty('MicrophoneGain', dbus.UInt16(args[2]))
+       else:
+               props = headset.GetProperties()
+               print(props['MicrophoneGain'])
+
+       sys.exit(0)
+
+if args[0] == "play":
+       if len(args) < 2:
+               print("Need device address parameter")
+               sys.exit(1)
+       device = adapter.FindDevice(args[1])
+       headset = dbus.Interface(bus.get_object("org.bluez", device),
+                                       "org.bluez.Headset")
+       headset.Play()
+
+       sys.exit(0)
+
+if args[0] == "stop":
+       if len(args) < 2:
+               print("Need device address parameter")
+               sys.exit(1)
+       device = adapter.FindDevice(args[1])
+       headset = dbus.Interface(bus.get_object("org.bluez", device),
+                                       "org.bluez.Headset")
+       headset.Stop()
+
+       sys.exit(0)
+
+if args[0] == "outgoing":
+       if len(args) > 1:
+               test.OutgoingCall(args[1])
+       else:
+               print("Need number parameter")
+       sys.exit(0)
+
+if args[0] == "incoming":
+       if len(args) > 1:
+               test.IncomingCall(args[1])
+       else:
+               print("Need number parameter")
+       sys.exit(0)
+
+if args[0] == "cancel":
+       test.CancelCall()
+       sys.exit(0)
+
+if args[0] == "signal":
+       if len(args) > 1:
+               test.SignalStrength(args[1])
+       else:
+               print("Need signal strength parameter")
+       sys.exit(0)
+
+if args[0] == "battery":
+       if len(args) > 1:
+               test.BatteryLevel(args[1])
+       else:
+               print("Need battery level parameter")
+       sys.exit(0)
+
+if args[0] == "roaming":
+       if len(args) > 1:
+               test.RoamingStatus(args[1] == "yes" or False)
+       else:
+               print("Need yes/no parameter")
+       sys.exit(0)
+
+if args[0] == "registration":
+       if len(args) > 1:
+               test.RegistrationStatus(args[1] == "yes" or False)
+       else:
+               print("Need yes/no parameter")
+       sys.exit(0)
+
+if args[0] == "subscriber":
+       if len(args) > 1:
+               test.SetSubscriberNumber(args[1])
+       else:
+               print("Need number parameter")
+       sys.exit(0)
+
+print("Unknown command")
+sys.exit(1)
diff --git a/test/test-textfile.c b/test/test-textfile.c
new file mode 100644 (file)
index 0000000..e0a0c5b
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "textfile.h"
+
+static void print_entry(char *key, char *value, void *data)
+{
+       printf("%s %s\n", key, value);
+}
+
+int main(int argc, char *argv[])
+{
+       char filename[] = "/tmp/textfile";
+       char key[18], value[512], *str;
+       unsigned int i, j, size, max = 10;
+       int fd;
+
+       size = getpagesize();
+       printf("System uses a page size of %d bytes\n\n", size);
+
+       fd = creat(filename, 0644);
+       if (ftruncate(fd, 0) < 0)
+               return -errno;
+
+       memset(value, 0, sizeof(value));
+       for (i = 0; i < (size / sizeof(value)); i++) {
+               if (write(fd, value, sizeof(value)) < 0)
+                       return -errno;
+       }
+
+       close(fd);
+
+       sprintf(key, "11:11:11:11:11:11");
+       str = textfile_get(filename, key);
+
+       if (truncate(filename, 0) < 0)
+               return -errno;
+
+       sprintf(key, "00:00:00:00:00:00");
+       if (textfile_del(filename, key) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       memset(value, 0, sizeof(value));
+       if (textfile_put(filename, key, value) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       str = textfile_get(filename, key);
+       if (!str)
+               fprintf(stderr, "No value for %s\n", key);
+       else
+               free(str);
+
+       snprintf(value, sizeof(value), "Test");
+       if (textfile_put(filename, key, value) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       if (textfile_put(filename, key, value) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       if (textfile_put(filename, key, value) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       if (textfile_del(filename, key) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       str = textfile_get(filename, key);
+       if (str) {
+               fprintf(stderr, "Found value for %s\n", key);
+               free(str);
+       }
+
+       for (i = 1; i < max + 1; i++) {
+               sprintf(key, "00:00:00:00:00:%02X", i);
+
+               memset(value, 0, sizeof(value));
+               for (j = 0; j < i; j++)
+                       value[j] = 'x';
+
+               printf("%s %s\n", key, value);
+
+               if (textfile_put(filename, key, value) < 0) {
+                       fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+                       break;
+               }
+
+               str = textfile_get(filename, key);
+               if (!str)
+                       fprintf(stderr, "No value for %s\n", key);
+               else
+                       free(str);
+       }
+
+
+       sprintf(key, "00:00:00:00:00:%02X", max);
+
+       memset(value, 0, sizeof(value));
+       for (j = 0; j < max; j++)
+               value[j] = 'y';
+
+       if (textfile_put(filename, key, value) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       sprintf(key, "00:00:00:00:00:%02X", 1);
+
+       memset(value, 0, sizeof(value));
+       for (j = 0; j < max; j++)
+               value[j] = 'z';
+
+       if (textfile_put(filename, key, value) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       printf("\n");
+
+       for (i = 1; i < max + 1; i++) {
+               sprintf(key, "00:00:00:00:00:%02X", i);
+
+               str = textfile_get(filename, key);
+               if (str) {
+                       printf("%s %s\n", key, str);
+                       free(str);
+               }
+       }
+
+
+       sprintf(key, "00:00:00:00:00:%02X", 2);
+
+       if (textfile_del(filename, key) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       sprintf(key, "00:00:00:00:00:%02X", max - 3);
+
+       if (textfile_del(filename, key) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       printf("\n");
+
+       textfile_foreach(filename, print_entry, NULL);
+
+
+       sprintf(key, "00:00:00:00:00:%02X", 1);
+
+       if (textfile_del(filename, key) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       sprintf(key, "00:00:00:00:00:%02X", max);
+
+       if (textfile_del(filename, key) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       sprintf(key, "00:00:00:00:00:%02X", max + 1);
+
+       if (textfile_del(filename, key) < 0)
+               fprintf(stderr, "%s (%d)\n", strerror(errno), errno);
+
+       printf("\n");
+
+       textfile_foreach(filename, print_entry, NULL);
+
+       return 0;
+}
diff --git a/test/test-thermometer b/test/test-thermometer
new file mode 100755 (executable)
index 0000000..9216264
--- /dev/null
@@ -0,0 +1,91 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+'''
+Thermometer test script
+'''
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+class Watcher(dbus.service.Object):
+       @dbus.service.method("org.bluez.ThermometerWatcher",
+                                       in_signature="a{sv}", out_signature="")
+       def MeasurementReceived(self, measure):
+               print(measure["Measurement"], " measurement received")
+               print("Exponent: ", measure["Exponent"])
+               print("Mantissa: ", measure["Mantissa"])
+               print("Unit: ", measure["Unit"])
+
+               if "Time" in measure:
+                       print("Time: ", measure["Time"])
+
+               if "Type" in measure:
+                       print("Type: ", measure["Type"])
+
+def property_changed(name, value):
+
+       print("PropertyChanged('%s', '%s')" % (name, value))
+
+if __name__ == "__main__":
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SystemBus()
+
+       manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+                                       "org.bluez.Manager")
+
+       option_list = [
+               make_option("-i", "--adapter", action="store",
+                       type="string", dest="adapter"),
+               make_option("-b", "--device", action="store",
+                       type="string", dest="address"),
+               ]
+
+       parser = OptionParser(option_list=option_list)
+
+       (options, args) = parser.parse_args()
+
+       if not options.address:
+               print("Usage: %s [-i <adapter>] -b <bdaddr> [command]" % (sys.argv[0]))
+               print("Possible commands:")
+               print("\tEnableIntermediateMeasurement")
+               sys.exit(1)
+
+       if options.adapter:
+               adapter_path = manager.FindAdapter(options.adapter)
+       else:
+               adapter_path = manager.DefaultAdapter()
+
+       adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+                                                       "org.bluez.Adapter")
+
+       device_path = adapter.FindDevice(options.address)
+
+       bus.add_signal_receiver(property_changed, bus_name="org.bluez",
+                               dbus_interface="org.bluez.Thermometer",
+                               signal_name="PropertyChanged")
+
+       thermometer = dbus.Interface(bus.get_object("org.bluez",
+                                       device_path), "org.bluez.Thermometer")
+
+       path = "/test/watcher"
+       watcher = Watcher(bus, path)
+
+       thermometer.RegisterWatcher(path)
+
+       if len(args) > 0:
+               if args[0] == "EnableIntermediateMeasurement":
+                       thermometer.EnableIntermediateMeasurement(path)
+               else:
+                       print("unknown command")
+                       sys.exit(1)
+
+       mainloop = gobject.MainLoop()
+       mainloop.run()
diff --git a/test/uuidtest.c b/test/uuidtest.c
new file mode 100644 (file)
index 0000000..a8b46d7
--- /dev/null
@@ -0,0 +1,319 @@
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+const char *base = "00000000-0000-1000-8000-00805F9B34FB";
+
+uint8_t xbase[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb};
+
+uint16_t sixteen = 0x1234;
+const char *uuidsixteen128 = "00001234-0000-1000-8000-00805F9B34FB";
+const char *uuidsixteen16 = "0x1234";
+const char *uuidsixteen16a = "1234";
+
+uint8_t xuuidsixteen[] = {0x00, 0x00, 0x12, 0x34, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb};
+
+uint32_t thirtytwo = 0x12345678;
+const char *uuidthirtytwo32 = "0x12345678";
+const char *uuidthirtytwo32a = "12345678";
+const char *uuidthirtytwo128 = "12345678-0000-1000-8000-00805F9B34FB";
+
+uint8_t xuuidthirtytwo[] = {0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb};
+
+const char *malformed[] = {
+       "0",
+       "01",
+       "012",
+       "xxxx",
+       "xxxxx",
+       "0xxxxx",
+       "0123456",
+       "012g4567",
+       "012345678",
+       "0x234567u9",
+       "01234567890",
+       "00001234-0000-1000-8000-00805F9B34F",
+       "00001234-0000-1000-8000 00805F9B34FB",
+       "00001234-0000-1000-8000-00805F9B34FBC",
+       "00001234-0000-1000-800G-00805F9B34FB",
+       NULL,
+       };
+
+int main(int argc, char *argv[])
+{
+       bt_uuid_t u, u2, u3, u4, u5, ub, u128;
+       uint128_t n, i;
+       char buf[512];
+       int s;
+
+       memcpy(&n, xbase, 16);
+       ntoh128(&n, &i);
+
+       if (bt_string_to_uuid(&u, base)) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_string_to_uuid(&ub, base)) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u.type != 128) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (ub.type != 128) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (memcmp(&u.value.u128, &i, 16) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (memcmp(&ub.value.u128, &i, 16) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (memcmp(&ub.value.u128, &u.value.u128, 16) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u, &ub) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       bt_uuid_to_string(&u, buf, sizeof(buf));
+       /* printf("%s\n", buf); */
+
+       if (strcasecmp(buf, base) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       memcpy(&n, xuuidsixteen, 16);
+       ntoh128(&n, &i);
+
+       bt_uuid16_create(&u, sixteen);
+       bt_uuid_to_uuid128(&u, &u128);
+
+       if (bt_string_to_uuid(&u2, uuidsixteen16)) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_string_to_uuid(&u3, uuidsixteen16a)) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_string_to_uuid(&u4, uuidsixteen128)) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       bt_uuid128_create(&u5, i);
+
+       if (u.type != 16) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u128.type != 128) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u.value.u16 != sixteen) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u2.type != 16) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u3.type != 16) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u4.type != 128) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u5.type != 128) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u, &u2) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u2, &u3) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u, &u3) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u3, &u4) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u4, &u5) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u5, &u) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u, &ub) == 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u5, &ub) == 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u, &u128) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&ub, &u128) == 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       memcpy(&n, xuuidthirtytwo, 16);
+       ntoh128(&n, &i);
+
+       bt_uuid32_create(&u, thirtytwo);
+       bt_uuid_to_uuid128(&u, &u128);
+       bt_string_to_uuid(&u2, uuidthirtytwo32);
+       bt_string_to_uuid(&u3, uuidthirtytwo32a);
+       bt_string_to_uuid(&u4, uuidthirtytwo128);
+       bt_uuid128_create(&u5, i);
+
+       /*
+       bt_uuid_to_string(&u2, buf, sizeof(buf));
+       printf("%s\n", buf);
+
+       bt_uuid_to_string(&u3, buf, sizeof(buf));
+       printf("%s\n", buf);
+
+       bt_uuid_to_string(&u4, buf, sizeof(buf));
+       printf("%s\n", buf);
+       */
+
+       if (u.type != 32) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u128.type != 128) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u.value.u32 != thirtytwo) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u2.type != 32) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u3.type != 32) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u4.type != 128) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (u5.type != 128) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u, &u2) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u2, &u3) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u3, &u4) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u4, &u5) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u5, &u) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u, &ub) == 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u5, &ub) == 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&u, &u128) != 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       if (bt_uuid_cmp(&ub, &u128) == 0) {
+               printf("Fail %d\n", __LINE__);
+               return 1;
+       }
+
+       for (s = 0; malformed[s]; ++s) {
+               if (bt_string_to_uuid(&u3, malformed[s]) == 0) {
+                       printf("Fail %s %d\n", malformed[s], __LINE__);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
diff --git a/thermometer/main.c b/thermometer/main.c
new file mode 100644 (file)
index 0000000..4447b52
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <glib.h>
+#include <errno.h>
+#include <gdbus.h>
+
+#include "plugin.h"
+#include "manager.h"
+#include "hcid.h"
+#include "log.h"
+
+static DBusConnection *connection = NULL;
+
+static int thermometer_init(void)
+{
+       if (!main_opts.gatt_enabled) {
+               DBG("GATT is disabled");
+               return -ENOTSUP;
+       }
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+       if (connection == NULL)
+               return -EIO;
+
+       if (thermometer_manager_init(connection) < 0) {
+               dbus_connection_unref(connection);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void thermometer_exit(void)
+{
+       if (!main_opts.gatt_enabled)
+               return;
+
+       thermometer_manager_exit();
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
+
+BLUETOOTH_PLUGIN_DEFINE(thermometer, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                                       thermometer_init, thermometer_exit)
diff --git a/thermometer/manager.c b/thermometer/manager.c
new file mode 100644 (file)
index 0000000..3d5452b
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <gdbus.h>
+#include <errno.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "thermometer.h"
+#include "manager.h"
+
+static DBusConnection *connection = NULL;
+
+static gint primary_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct gatt_primary *prim = a;
+       const char *uuid = b;
+
+       return g_strcmp0(prim->uuid, uuid);
+}
+
+static int thermometer_driver_probe(struct btd_device *device, GSList *uuids)
+{
+       struct gatt_primary *tattr;
+       GSList *primaries, *l;
+
+       primaries = btd_device_get_primaries(device);
+
+       l = g_slist_find_custom(primaries, HEALTH_THERMOMETER_UUID,
+                                                       primary_uuid_cmp);
+       if (l == NULL)
+               return -EINVAL;
+
+       tattr = l->data;
+
+       return thermometer_register(connection, device, tattr);
+}
+
+static void thermometer_driver_remove(struct btd_device *device)
+{
+       thermometer_unregister(device);
+}
+
+static struct btd_device_driver thermometer_device_driver = {
+       .name   = "thermometer-device-driver",
+       .uuids  = BTD_UUIDS(HEALTH_THERMOMETER_UUID),
+       .probe  = thermometer_driver_probe,
+       .remove = thermometer_driver_remove
+};
+
+int thermometer_manager_init(DBusConnection *conn)
+{
+       int ret;
+
+       ret = btd_register_device_driver(&thermometer_device_driver);
+       if (ret < 0)
+                return ret;
+
+       connection = dbus_connection_ref(conn);
+       return 0;
+}
+
+void thermometer_manager_exit(void)
+{
+       btd_unregister_device_driver(&thermometer_device_driver);
+
+       dbus_connection_unref(connection);
+       connection = NULL;
+}
diff --git a/thermometer/manager.h b/thermometer/manager.h
new file mode 100644 (file)
index 0000000..ed928ad
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int thermometer_manager_init(DBusConnection *conn);
+void thermometer_manager_exit(void);
diff --git a/thermometer/thermometer.c b/thermometer/thermometer.c
new file mode 100644 (file)
index 0000000..85f0811
--- /dev/null
@@ -0,0 +1,1265 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gdbus.h>
+#include <errno.h>
+#include <bluetooth/uuid.h>
+
+#include "dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+#include "error.h"
+#include "log.h"
+#include "gattrib.h"
+#include "attio.h"
+#include "att.h"
+#include "gatt.h"
+#include "thermometer.h"
+
+#define THERMOMETER_INTERFACE "org.bluez.Thermometer"
+
+/* Temperature measurement flag fields */
+#define TEMP_UNITS             0x01
+#define TEMP_TIME_STAMP                0x02
+#define TEMP_TYPE              0x04
+
+#define FLOAT_MAX_MANTISSA     16777216 /* 2^24 */
+
+struct thermometer {
+       DBusConnection          *conn;          /* The connection to the bus */
+       struct btd_device       *dev;           /* Device reference */
+       GAttrib                 *attrib;        /* GATT connection */
+       struct att_range        *svc_range;     /* Thermometer range */
+       guint                   attioid;        /* Att watcher id */
+       guint                   attindid;       /* Att incications id */
+       guint                   attnotid;       /* Att notifications id */
+       GSList                  *chars;         /* Characteristics */
+       GSList                  *fwatchers;     /* Final measurements */
+       GSList                  *iwatchers;     /* Intermediate measurements */
+       gboolean                intermediate;
+       uint8_t                 type;
+       uint16_t                interval;
+       uint16_t                max;
+       uint16_t                min;
+       gboolean                has_type;
+       gboolean                has_interval;
+};
+
+struct characteristic {
+       struct gatt_char                attr;   /* Characteristic */
+       GSList                  *desc;  /* Descriptors */
+       struct thermometer      *t;     /* Thermometer where the char belongs */
+};
+
+struct descriptor {
+       struct characteristic   *ch;
+       uint16_t                handle;
+       bt_uuid_t               uuid;
+};
+
+struct watcher {
+       struct thermometer      *t;
+       guint                   id;
+       char                    *srv;
+       char                    *path;
+};
+
+struct measurement {
+       int16_t         exp;
+       int32_t         mant;
+       uint64_t        time;
+       gboolean        suptime;
+       char            *unit;
+       char            *type;
+       char            *value;
+};
+
+struct tmp_interval_data {
+       struct thermometer      *thermometer;
+       uint16_t                interval;
+};
+
+static GSList *thermometers = NULL;
+
+const char *temp_type[] = {
+       "<reserved>",
+       "Armpit",
+       "Body",
+       "Ear",
+       "Finger",
+       "Intestines",
+       "Mouth",
+       "Rectum",
+       "Toe",
+       "Tympanum"
+};
+
+static const gchar *temptype2str(uint8_t value)
+{
+        if (value > 0 && value < G_N_ELEMENTS(temp_type))
+               return temp_type[value];
+
+       error("Temperature type %d reserved for future use", value);
+       return NULL;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+       struct watcher *watcher = user_data;
+
+       g_free(watcher->path);
+       g_free(watcher->srv);
+       g_free(watcher);
+}
+
+static void remove_watcher(gpointer user_data)
+{
+       struct watcher *watcher = user_data;
+
+       g_dbus_remove_watch(watcher->t->conn, watcher->id);
+}
+
+static void destroy_char(gpointer user_data)
+{
+       struct characteristic *c = user_data;
+
+       g_slist_free_full(c->desc, g_free);
+       g_free(c);
+}
+
+static void destroy_thermometer(gpointer user_data)
+{
+       struct thermometer *t = user_data;
+
+       if (t->attioid > 0)
+               btd_device_remove_attio_callback(t->dev, t->attioid);
+
+       if (t->attindid > 0)
+               g_attrib_unregister(t->attrib, t->attindid);
+
+       if (t->attnotid > 0)
+               g_attrib_unregister(t->attrib, t->attnotid);
+
+       if (t->attrib != NULL)
+               g_attrib_unref(t->attrib);
+
+       if (t->chars != NULL)
+               g_slist_free_full(t->chars, destroy_char);
+
+       if (t->fwatchers != NULL)
+               g_slist_free_full(t->fwatchers, remove_watcher);
+
+       dbus_connection_unref(t->conn);
+       btd_device_unref(t->dev);
+       g_free(t->svc_range);
+       g_free(t);
+}
+
+static gint cmp_device(gconstpointer a, gconstpointer b)
+{
+       const struct thermometer *t = a;
+       const struct btd_device *dev = b;
+
+       if (dev == t->dev)
+               return 0;
+
+       return -1;
+}
+
+static gint cmp_watcher(gconstpointer a, gconstpointer b)
+{
+       const struct watcher *watcher = a;
+       const struct watcher *match = b;
+       int ret;
+
+       ret = g_strcmp0(watcher->srv, match->srv);
+       if (ret != 0)
+               return ret;
+
+       return g_strcmp0(watcher->path, match->path);
+}
+
+static gint cmp_char_uuid(gconstpointer a, gconstpointer b)
+{
+       const struct characteristic *ch = a;
+       const char *uuid = b;
+
+       return g_strcmp0(ch->attr.uuid, uuid);
+}
+
+static gint cmp_char_val_handle(gconstpointer a, gconstpointer b)
+{
+       const struct characteristic *ch = a;
+       const uint16_t *handle = b;
+
+       return ch->attr.value_handle - *handle;
+}
+
+static gint cmp_descriptor(gconstpointer a, gconstpointer b)
+{
+       const struct descriptor *desc = a;
+       const bt_uuid_t *uuid = b;
+
+       return bt_uuid_cmp(&desc->uuid, uuid);
+}
+
+static struct characteristic *get_characteristic(struct thermometer *t,
+                                                       const char *uuid)
+{
+       GSList *l;
+
+       l = g_slist_find_custom(t->chars, uuid, cmp_char_uuid);
+       if (l == NULL)
+               return NULL;
+
+       return l->data;
+}
+
+static struct descriptor *get_descriptor(struct characteristic *ch,
+                                                       const bt_uuid_t *uuid)
+{
+       GSList *l;
+
+       l = g_slist_find_custom(ch->desc, uuid, cmp_descriptor);
+       if (l == NULL)
+               return NULL;
+
+       return l->data;
+}
+
+static void change_property(struct thermometer *t, const char *name,
+                                                       gpointer value) {
+       if (g_strcmp0(name, "Intermediate") == 0) {
+               gboolean *intermediate = value;
+               if (t->intermediate == *intermediate)
+                       return;
+
+               t->intermediate = *intermediate;
+               emit_property_changed(t->conn, device_get_path(t->dev),
+                                       THERMOMETER_INTERFACE, name,
+                                       DBUS_TYPE_BOOLEAN, &t->intermediate);
+       } else if (g_strcmp0(name, "Interval") == 0) {
+               uint16_t *interval = value;
+               if (t->has_interval && t->interval == *interval)
+                       return;
+
+               t->has_interval = TRUE;
+               t->interval = *interval;
+               emit_property_changed(t->conn, device_get_path(t->dev),
+                                       THERMOMETER_INTERFACE, name,
+                                       DBUS_TYPE_UINT16, &t->interval);
+       } else if (g_strcmp0(name, "Maximum") == 0) {
+               uint16_t *max = value;
+               if (t->max == *max)
+                       return;
+
+               t->max = *max;
+               emit_property_changed(t->conn, device_get_path(t->dev),
+                                       THERMOMETER_INTERFACE, name,
+                                       DBUS_TYPE_UINT16, &t->max);
+       } else if (g_strcmp0(name, "Minimum") == 0) {
+               uint16_t *min = value;
+               if (t->min == *min)
+                       return;
+
+               t->min = *min;
+               emit_property_changed(t->conn, device_get_path(t->dev),
+                                       THERMOMETER_INTERFACE, name,
+                                       DBUS_TYPE_UINT16, &t->min);
+       } else
+               DBG("%s is not a thermometer property", name);
+}
+
+static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct descriptor *desc = user_data;
+       uint8_t value[ATT_MAX_MTU];
+       uint16_t max, min;
+       int vlen;
+
+       if (status != 0) {
+               DBG("Valid Range descriptor read failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       if (!dec_read_resp(pdu, len, value, &vlen)) {
+               DBG("Protocol error\n");
+               return;
+       }
+
+       if (vlen < 4) {
+               DBG("Invalid range received");
+               return;
+       }
+
+       min = att_get_u16(&value[0]);
+       max = att_get_u16(&value[2]);
+
+       if (min == 0 || min > max) {
+               DBG("Invalid range");
+               return;
+       }
+
+       change_property(desc->ch->t, "Maximum", &max);
+       change_property(desc->ch->t, "Minimum", &min);
+}
+
+static void measurement_cb(guint8 status, const guint8 *pdu,
+                                               guint16 len, gpointer user_data)
+{
+       char *msg = user_data;
+
+       if (status != 0)
+               error("%s failed", msg);
+
+       g_free(msg);
+}
+
+static void process_thermometer_desc(struct descriptor *desc)
+{
+       struct characteristic *ch = desc->ch;
+       char uuidstr[MAX_LEN_UUID_STR];
+       bt_uuid_t btuuid;
+
+       bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+       if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0) {
+               uint8_t atval[2];
+               uint16_t val;
+               char *msg;
+
+               if (g_strcmp0(ch->attr.uuid,
+                                       TEMPERATURE_MEASUREMENT_UUID) == 0) {
+                       if (g_slist_length(ch->t->fwatchers) == 0)
+                               return;
+
+                       val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
+                       msg = g_strdup("Enable Temperature Measurement "
+                                                               "indication");
+               } else if (g_strcmp0(ch->attr.uuid,
+                                       INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+                       if (g_slist_length(ch->t->iwatchers) == 0)
+                               return;
+
+                       val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
+                       msg = g_strdup("Enable Intermediate Temperature "
+                                                               "notification");
+               } else if (g_strcmp0(ch->attr.uuid,
+                                       MEASUREMENT_INTERVAL_UUID) == 0) {
+                       val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
+                       msg = g_strdup("Enable Measurement Interval "
+                                                               "indication");
+               } else
+                       goto done;
+
+               att_put_u16(val, atval);
+               gatt_write_char(ch->t->attrib, desc->handle, atval, 2,
+                                                       measurement_cb, msg);
+               return;
+       }
+
+       bt_uuid16_create(&btuuid, GATT_CHARAC_VALID_RANGE_UUID);
+
+       if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0 && g_strcmp0(ch->attr.uuid,
+                                       MEASUREMENT_INTERVAL_UUID) == 0) {
+               gatt_read_char(ch->t->attrib, desc->handle, 0,
+                                               valid_range_desc_cb, desc);
+               return;
+       }
+
+done:
+       bt_uuid_to_string(&desc->uuid, uuidstr, MAX_LEN_UUID_STR);
+       DBG("Ignored descriptor %s in characteristic %s", uuidstr,
+                                                               ch->attr.uuid);
+}
+
+static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct characteristic *ch = user_data;
+       struct att_data_list *list;
+       uint8_t format;
+       int i;
+
+       if (status != 0) {
+               error("Discover all characteristic descriptors failed [%s]: %s",
+                                       ch->attr.uuid, att_ecode2str(status));
+               return;
+       }
+
+       list = dec_find_info_resp(pdu, len, &format);
+       if (list == NULL)
+               return;
+
+       for (i = 0; i < list->num; i++) {
+               struct descriptor *desc;
+               uint8_t *value;
+
+               value = list->data[i];
+               desc = g_new0(struct descriptor, 1);
+               desc->handle = att_get_u16(value);
+               desc->ch = ch;
+
+               if (format == 0x01)
+                       desc->uuid = att_get_uuid16(&value[2]);
+               else
+                       desc->uuid = att_get_uuid128(&value[2]);
+
+               ch->desc = g_slist_append(ch->desc, desc);
+               process_thermometer_desc(desc);
+       }
+
+       att_data_list_free(list);
+}
+
+static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct characteristic *ch = user_data;
+       struct thermometer *t = ch->t;
+       uint8_t value[ATT_MAX_MTU];
+       int vlen;
+
+       if (status != 0) {
+               DBG("Temperature Type value read failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       if (!dec_read_resp(pdu, len, value, &vlen)) {
+               DBG("Protocol error.");
+               return;
+       }
+
+       if (vlen != 1) {
+               DBG("Invalid length for Temperature type");
+               return;
+       }
+
+       t->has_type = TRUE;
+       t->type = value[0];
+}
+
+static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct characteristic *ch = user_data;
+       uint8_t value[ATT_MAX_MTU];
+       uint16_t interval;
+       int vlen;
+
+       if (status != 0) {
+               DBG("Measurement Interval value read failed: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       if (!dec_read_resp(pdu, len, value, &vlen)) {
+               DBG("Protocol error\n");
+               return;
+       }
+
+       if (vlen < 2) {
+               DBG("Invalid Interval received");
+               return;
+       }
+
+       interval = att_get_u16(&value[0]);
+       change_property(ch->t, "Interval", &interval);
+}
+
+static void process_thermometer_char(struct characteristic *ch)
+{
+       if (g_strcmp0(ch->attr.uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+               gboolean intermediate = TRUE;
+               change_property(ch->t, "Intermediate", &intermediate);
+               return;
+       } else if (g_strcmp0(ch->attr.uuid, TEMPERATURE_TYPE_UUID) == 0)
+               gatt_read_char(ch->t->attrib, ch->attr.value_handle, 0,
+                                                       read_temp_type_cb, ch);
+       else if (g_strcmp0(ch->attr.uuid, MEASUREMENT_INTERVAL_UUID) == 0)
+               gatt_read_char(ch->t->attrib, ch->attr.value_handle, 0,
+                                                       read_interval_cb, ch);
+}
+
+static void configure_thermometer_cb(GSList *characteristics, guint8 status,
+                                                       gpointer user_data)
+{
+       struct thermometer *t = user_data;
+       GSList *l;
+
+       if (status != 0) {
+               error("Discover thermometer characteristics: %s",
+                                                       att_ecode2str(status));
+               return;
+       }
+
+       for (l = characteristics; l; l = l->next) {
+               struct gatt_char *c = l->data;
+               struct characteristic *ch;
+               uint16_t start, end;
+
+               ch = g_new0(struct characteristic, 1);
+               ch->attr.handle = c->handle;
+               ch->attr.properties = c->properties;
+               ch->attr.value_handle = c->value_handle;
+               memcpy(ch->attr.uuid, c->uuid, MAX_LEN_UUID_STR + 1);
+               ch->t = t;
+
+               t->chars = g_slist_append(t->chars, ch);
+
+               process_thermometer_char(ch);
+
+               start = c->value_handle + 1;
+
+               if (l->next != NULL) {
+                       struct gatt_char *c = l->next->data;
+                       if (start == c->handle)
+                               continue;
+                       end = c->handle - 1;
+               } else if (c->value_handle != t->svc_range->end)
+                       end = t->svc_range->end;
+               else
+                       continue;
+
+               gatt_find_info(t->attrib, start, end, discover_desc_cb, ch);
+       }
+}
+
+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct thermometer *t = data;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusMessage *reply;
+
+       reply = dbus_message_new_method_return(msg);
+       if (reply == NULL)
+               return NULL;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       dict_append_entry(&dict, "Intermediate", DBUS_TYPE_BOOLEAN,
+                                                       &t->intermediate);
+
+       if (t->has_interval) {
+               dict_append_entry(&dict, "Interval", DBUS_TYPE_UINT16,
+                                                               &t->interval);
+               dict_append_entry(&dict, "Maximum", DBUS_TYPE_UINT16, &t->max);
+               dict_append_entry(&dict, "Minimum", DBUS_TYPE_UINT16, &t->min);
+       }
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       return reply;
+}
+
+static void write_interval_cb (guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct tmp_interval_data *data = user_data;
+
+       if (status != 0) {
+               error("Interval Write Request failed %s",
+                                                       att_ecode2str(status));
+               goto done;
+       }
+
+       if (!dec_write_resp(pdu, len)) {
+               error("Interval Write Request: protocol error");
+               goto done;
+       }
+
+       change_property(data->thermometer, "Interval", &data->interval);
+
+done:
+       g_free(user_data);
+}
+
+static DBusMessage *write_attr_interval(struct thermometer *t, DBusMessage *msg,
+                                                               uint16_t value)
+{
+       struct tmp_interval_data *data;
+       struct characteristic *ch;
+       uint8_t atval[2];
+
+       if (t->attrib == NULL)
+               return btd_error_not_connected(msg);
+
+       ch = get_characteristic(t, MEASUREMENT_INTERVAL_UUID);
+       if (ch == NULL)
+               return btd_error_not_available(msg);
+
+       if (value < t->min || value > t->max)
+               return btd_error_invalid_args(msg);
+
+       att_put_u16(value, &atval[0]);
+
+       data = g_new0(struct tmp_interval_data, 1);
+       data->thermometer = t;
+       data->interval = value;
+       gatt_write_char(t->attrib, ch->attr.value_handle, atval, 2,
+                                               write_interval_cb, data);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       struct thermometer *t = data;
+       const char *property;
+       DBusMessageIter iter;
+       DBusMessageIter sub;
+       uint16_t value;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return btd_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &property);
+       if (g_strcmp0("Interval", property) != 0)
+               return btd_error_invalid_args(msg);
+
+       if (!t->has_interval)
+               return btd_error_not_available(msg);
+
+       dbus_message_iter_next(&iter);
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &sub);
+
+       if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&sub, &value);
+
+       return write_attr_interval(t, msg, value);
+}
+
+static void enable_final_measurement(struct thermometer *t)
+{
+       struct characteristic *ch;
+       struct descriptor *desc;
+       bt_uuid_t btuuid;
+       uint8_t atval[2];
+       char *msg;
+
+       if (t->attrib == NULL)
+               return;
+
+       ch = get_characteristic(t, TEMPERATURE_MEASUREMENT_UUID);
+       if (ch == NULL) {
+               DBG("Temperature measurement characteristic not found");
+               return;
+       }
+
+       bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       desc = get_descriptor(ch, &btuuid);
+       if (desc == NULL) {
+               DBG("Client characteristic configuration descriptor not found");
+               return;
+       }
+
+       atval[0] = 0x02;
+       atval[1] = 0x00;
+       msg = g_strdup("Enable final measurement");
+       gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
+}
+
+static void enable_intermediate_measurement(struct thermometer *t)
+{
+       struct characteristic *ch;
+       struct descriptor *desc;
+       bt_uuid_t btuuid;
+       uint8_t atval[2];
+       char *msg;
+
+       if (t->attrib == NULL)
+               return;
+
+       ch = get_characteristic(t, INTERMEDIATE_TEMPERATURE_UUID);
+       if (ch == NULL) {
+               DBG("Intermediate measurement characteristic not found");
+               return;
+       }
+
+       bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       desc = get_descriptor(ch, &btuuid);
+       if (desc == NULL) {
+               DBG("Client characteristic configuration descriptor not found");
+               return;
+       }
+
+       atval[0] = 0x01;
+       atval[1] = 0x00;
+       msg = g_strdup("Enable intermediate measurement");
+       gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
+}
+
+static void disable_final_measurement(struct thermometer *t)
+{
+       struct characteristic *ch;
+       struct descriptor *desc;
+       bt_uuid_t btuuid;
+       uint8_t atval[2];
+       char *msg;
+
+       if (t->attrib == NULL)
+               return;
+
+       ch = get_characteristic(t, TEMPERATURE_MEASUREMENT_UUID);
+       if (ch == NULL) {
+               DBG("Temperature measurement characteristic not found");
+               return;
+       }
+
+       bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       desc = get_descriptor(ch, &btuuid);
+       if (desc == NULL) {
+               DBG("Client characteristic configuration descriptor not found");
+               return;
+       }
+
+       atval[0] = 0x00;
+       atval[1] = 0x00;
+       msg = g_strdup("Disable final measurement");
+       gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
+}
+
+static void disable_intermediate_measurement(struct thermometer *t)
+{
+       struct characteristic *ch;
+       struct descriptor *desc;
+       bt_uuid_t btuuid;
+       uint8_t atval[2];
+       char *msg;
+
+       if (t->attrib == NULL)
+               return;
+
+       ch = get_characteristic(t, INTERMEDIATE_TEMPERATURE_UUID);
+       if (ch == NULL) {
+               DBG("Intermediate measurement characteristic not found");
+               return;
+       }
+
+       bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       desc = get_descriptor(ch, &btuuid);
+       if (desc == NULL) {
+               DBG("Client characteristic configuration descriptor not found");
+               return;
+       }
+
+       atval[0] = 0x00;
+       atval[1] = 0x00;
+       msg = g_strdup("Disable intermediate measurement");
+       gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
+}
+
+static void remove_int_watcher(struct thermometer *t, struct watcher *w)
+{
+       if (!g_slist_find(t->iwatchers, w))
+               return;
+
+       t->iwatchers = g_slist_remove(t->iwatchers, w);
+
+       if (g_slist_length(t->iwatchers) == 0)
+               disable_intermediate_measurement(t);
+}
+
+static void watcher_exit(DBusConnection *conn, void *user_data)
+{
+       struct watcher *watcher = user_data;
+       struct thermometer *t = watcher->t;
+
+       DBG("Thermometer watcher %s disconnected", watcher->path);
+
+       remove_int_watcher(t, watcher);
+
+       t->fwatchers = g_slist_remove(t->fwatchers, watcher);
+       g_dbus_remove_watch(watcher->t->conn, watcher->id);
+
+       if (g_slist_length(t->fwatchers) == 0)
+               disable_final_measurement(t);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+                                                       const char *path)
+{
+       struct watcher *match;
+       GSList *l;
+
+       match = g_new0(struct watcher, 1);
+       match->srv = g_strdup(sender);
+       match->path = g_strdup(path);
+
+       l = g_slist_find_custom(list, match, cmp_watcher);
+       destroy_watcher(match);
+
+       if (l != NULL)
+               return l->data;
+
+       return NULL;
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct thermometer *t = data;
+       struct watcher *watcher;
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(t->fwatchers, sender, path);
+       if (watcher != NULL)
+               return btd_error_already_exists(msg);
+
+       DBG("Thermometer watcher %s registered", path);
+
+       watcher = g_new0(struct watcher, 1);
+       watcher->srv = g_strdup(sender);
+       watcher->path = g_strdup(path);
+       watcher->t = t;
+       watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
+                                               watcher, destroy_watcher);
+
+       if (g_slist_length(t->fwatchers) == 0)
+               enable_final_measurement(t);
+
+       t->fwatchers = g_slist_prepend(t->fwatchers, watcher);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct thermometer *t = data;
+       struct watcher *watcher;
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(t->fwatchers, sender, path);
+       if (watcher == NULL)
+               return btd_error_does_not_exist(msg);
+
+       DBG("Thermometer watcher %s unregistered", path);
+
+       remove_int_watcher(t, watcher);
+
+       t->fwatchers = g_slist_remove(t->fwatchers, watcher);
+       g_dbus_remove_watch(watcher->t->conn, watcher->id);
+
+       if (g_slist_length(t->fwatchers) == 0)
+               disable_final_measurement(t);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct thermometer *t = data;
+       struct watcher *watcher;
+       char *path;
+
+       if (!t->intermediate)
+               return btd_error_not_supported(msg);
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(t->fwatchers, sender, path);
+       if (watcher == NULL)
+               return btd_error_does_not_exist(msg);
+
+       if (find_watcher(t->iwatchers, sender, path))
+               return btd_error_already_exists(msg);
+
+       DBG("Intermediate measurement watcher %s registered", path);
+
+       if (g_slist_length(t->iwatchers) == 0)
+               enable_intermediate_measurement(t);
+
+       t->iwatchers = g_slist_prepend(t->iwatchers, watcher);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg,
+                                                               void *data)
+{
+       const char *sender = dbus_message_get_sender(msg);
+       struct thermometer *t = data;
+       struct watcher *watcher;
+       char *path;
+
+       if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                                                       DBUS_TYPE_INVALID))
+               return btd_error_invalid_args(msg);
+
+       watcher = find_watcher(t->iwatchers, sender, path);
+       if (watcher == NULL)
+               return btd_error_does_not_exist(msg);
+
+       DBG("Intermediate measurement %s unregistered", path);
+
+       remove_int_watcher(t, watcher);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable thermometer_methods[] = {
+       { GDBUS_METHOD("GetProperties",
+                       NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+                       get_properties) },
+       { GDBUS_ASYNC_METHOD("SetProperty",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+                       set_property) },
+       { GDBUS_METHOD("RegisterWatcher",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       register_watcher) },
+       { GDBUS_METHOD("UnregisterWatcher",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       unregister_watcher) },
+       { GDBUS_METHOD("EnableIntermediateMeasurement",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       enable_intermediate) },
+       { GDBUS_METHOD("DisableIntermediateMeasurement",
+                       GDBUS_ARGS({ "agent", "o" }), NULL,
+                       disable_intermediate) },
+       { }
+};
+
+static const GDBusSignalTable thermometer_signals[] = {
+       { GDBUS_SIGNAL("PropertyChanged",
+                       GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+       { }
+};
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+       struct watcher *w = data;
+       struct measurement *m = user_data;
+       DBusConnection *conn = w->t->conn;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       DBusMessage *msg;
+
+       msg = dbus_message_new_method_call(w->srv, w->path,
+                               "org.bluez.ThermometerWatcher",
+                               "MeasurementReceived");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                       DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+       dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp);
+       dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant);
+       dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit);
+
+       if (m->suptime)
+               dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time);
+
+       dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type);
+       dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value);
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       dbus_message_set_no_reply(msg, TRUE);
+       g_dbus_send_message(conn, msg);
+}
+
+static void recv_measurement(struct thermometer *t, struct measurement *m)
+{
+       GSList *wlist;
+
+       if (g_strcmp0(m->value, "Intermediate") == 0)
+               wlist = t->iwatchers;
+       else
+               wlist = t->fwatchers;
+
+       g_slist_foreach(wlist, update_watcher, m);
+}
+
+static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
+                                               uint16_t len, gboolean final)
+{
+       struct measurement m;
+       const char *type;
+       uint8_t flags;
+       uint32_t raw;
+
+       if (len < 4) {
+               DBG("Mandatory flags are not provided");
+               return;
+       }
+
+       flags = pdu[3];
+       if (flags & TEMP_UNITS)
+               m.unit = "Fahrenheit";
+       else
+               m.unit = "Celsius";
+
+       if (len < 8) {
+               DBG("Temperature measurement value is not provided");
+               return;
+       }
+
+       raw = att_get_u32(&pdu[4]);
+       m.mant = raw & 0x00FFFFFF;
+       m.exp = ((int32_t) raw) >> 24;
+
+       if (m.mant & 0x00800000) {
+               /* convert to C2 negative value */
+               m.mant = m.mant - FLOAT_MAX_MANTISSA;
+       }
+
+       if (flags & TEMP_TIME_STAMP) {
+               struct tm ts;
+               time_t time;
+
+               if (len < 15) {
+                       DBG("Can't get time stamp value");
+                       return;
+               }
+
+               ts.tm_year = att_get_u16(&pdu[8]) - 1900;
+               ts.tm_mon = pdu[10] - 1;
+               ts.tm_mday = pdu[11];
+               ts.tm_hour = pdu[12];
+               ts.tm_min = pdu[13];
+               ts.tm_sec = pdu[14];
+               ts.tm_isdst = -1;
+
+               time = mktime(&ts);
+               m.time = (uint64_t) time;
+               m.suptime = TRUE;
+       } else
+               m.suptime = FALSE;
+
+       if (flags & TEMP_TYPE) {
+               uint8_t index;
+
+               if (m.suptime && len >= 16)
+                       index = 15;
+               else if (!m.suptime && len >= 9)
+                       index = 9;
+               else {
+                       DBG("Can't get temperature type");
+                       return;
+               }
+
+               type = temptype2str(pdu[index]);
+       } else if (t->has_type)
+               type = temptype2str(t->type);
+       else
+               type = NULL;
+
+       m.type = type ? g_strdup(type) : NULL;
+       m.value = final ? "Final" : "Intermediate";
+
+       recv_measurement(t, &m);
+       g_free(m.type);
+}
+
+static void proc_measurement_interval(struct thermometer *t, const uint8_t *pdu,
+                                                               uint16_t len)
+{
+       uint16_t interval;
+
+       if (len < 5) {
+               DBG("Measurement interval value is not provided");
+               return;
+       }
+
+       interval = att_get_u16(&pdu[3]);
+
+       change_property(t, "Interval", &interval);
+}
+
+static void ind_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+       struct thermometer *t = user_data;
+       const struct characteristic *ch;
+       uint8_t opdu[ATT_MAX_MTU];
+       uint16_t handle, olen;
+       GSList *l;
+
+       if (len < 3) {
+               DBG("Bad pdu received");
+               return;
+       }
+
+       handle = att_get_u16(&pdu[1]);
+       l = g_slist_find_custom(t->chars, &handle, cmp_char_val_handle);
+       if (l == NULL) {
+               DBG("Unexpected handle: 0x%04x", handle);
+               return;
+       }
+
+       ch = l->data;
+
+       if (g_strcmp0(ch->attr.uuid, TEMPERATURE_MEASUREMENT_UUID) == 0)
+               proc_measurement(t, pdu, len, TRUE);
+       else if (g_strcmp0(ch->attr.uuid, MEASUREMENT_INTERVAL_UUID) == 0)
+               proc_measurement_interval(t, pdu, len);
+
+       olen = enc_confirmation(opdu, sizeof(opdu));
+
+       if (olen > 0)
+               g_attrib_send(t->attrib, 0, opdu[0], opdu, olen, NULL, NULL,
+                                                                       NULL);
+}
+
+static void notif_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+       struct thermometer *t = user_data;
+       const struct characteristic *ch;
+       uint16_t handle;
+       GSList *l;
+
+       if (len < 3) {
+               DBG("Bad pdu received");
+               return;
+       }
+
+       handle = att_get_u16(&pdu[1]);
+       l = g_slist_find_custom(t->chars, &handle, cmp_char_val_handle);
+       if (l == NULL) {
+               DBG("Unexpected handle: 0x%04x", handle);
+               return;
+       }
+
+       ch = l->data;
+       if (g_strcmp0(ch->attr.uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0)
+               proc_measurement(t, pdu, len, FALSE);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+       struct thermometer *t = user_data;
+
+       t->attrib = g_attrib_ref(attrib);
+
+       t->attindid = g_attrib_register(t->attrib, ATT_OP_HANDLE_IND,
+                                                       ind_handler, t, NULL);
+       t->attnotid = g_attrib_register(t->attrib, ATT_OP_HANDLE_NOTIFY,
+                                                       notif_handler, t, NULL);
+       gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end,
+                                       NULL, configure_thermometer_cb, t);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+       struct thermometer *t = user_data;
+
+       DBG("GATT Disconnected");
+
+       if (t->attindid > 0) {
+               g_attrib_unregister(t->attrib, t->attindid);
+               t->attindid = 0;
+       }
+
+       if (t->attnotid > 0) {
+               g_attrib_unregister(t->attrib, t->attnotid);
+               t->attnotid = 0;
+       }
+
+       g_attrib_unref(t->attrib);
+       t->attrib = NULL;
+}
+
+int thermometer_register(DBusConnection *connection, struct btd_device *device,
+                                               struct gatt_primary *tattr)
+{
+       const gchar *path = device_get_path(device);
+       struct thermometer *t;
+
+       t = g_new0(struct thermometer, 1);
+       t->conn = dbus_connection_ref(connection);
+       t->dev = btd_device_ref(device);
+       t->svc_range = g_new0(struct att_range, 1);
+       t->svc_range->start = tattr->range.start;
+       t->svc_range->end = tattr->range.end;
+
+       if (!g_dbus_register_interface(t->conn, path, THERMOMETER_INTERFACE,
+                               thermometer_methods, thermometer_signals,
+                               NULL, t, destroy_thermometer)) {
+               error("D-Bus failed to register %s interface",
+                                                       THERMOMETER_INTERFACE);
+               destroy_thermometer(t);
+               return -EIO;
+       }
+
+       thermometers = g_slist_prepend(thermometers, t);
+
+       t->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+                                               attio_disconnected_cb, t);
+       return 0;
+}
+
+void thermometer_unregister(struct btd_device *device)
+{
+       struct thermometer *t;
+       GSList *l;
+
+       l = g_slist_find_custom(thermometers, device, cmp_device);
+       if (l == NULL)
+               return;
+
+       t = l->data;
+       thermometers = g_slist_remove(thermometers, t);
+       g_dbus_unregister_interface(t->conn, device_get_path(t->dev),
+                                                       THERMOMETER_INTERFACE);
+}
diff --git a/thermometer/thermometer.h b/thermometer/thermometer.h
new file mode 100644 (file)
index 0000000..330503c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int thermometer_register(DBusConnection *connection, struct btd_device *device,
+                                               struct gatt_primary *tattr);
+void thermometer_unregister(struct btd_device *device);
diff --git a/time/main.c b/time/main.c
new file mode 100644 (file)
index 0000000..d876725
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <glib.h>
+#include <errno.h>
+
+#include "plugin.h"
+#include "hcid.h"
+#include "log.h"
+#include "server.h"
+
+static int time_init(void)
+{
+       if (!main_opts.gatt_enabled) {
+               DBG("GATT is disabled");
+               return -ENOTSUP;
+       }
+
+       return time_server_init();
+}
+
+static void time_exit(void)
+{
+       if (!main_opts.gatt_enabled)
+               return;
+
+       time_server_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(time, VERSION,
+                       BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+                       time_init, time_exit)
diff --git a/time/server.c b/time/server.c
new file mode 100644 (file)
index 0000000..13a7bbe
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <time.h>
+#include <errno.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
+#include "attrib-server.h"
+#include "gatt-service.h"
+#include "log.h"
+#include "server.h"
+
+#define CURRENT_TIME_SVC_UUID          0x1805
+
+#define LOCAL_TIME_INFO_CHR_UUID       0x2A0F
+#define CT_TIME_CHR_UUID               0x2A2B
+
+static int encode_current_time(uint8_t value[10])
+{
+       struct timespec tp;
+       struct tm tm;
+
+       if (clock_gettime(CLOCK_REALTIME, &tp) == -1) {
+               int err = -errno;
+
+               error("clock_gettime: %s", strerror(-err));
+               return err;
+       }
+
+       if (localtime_r(&tp.tv_sec, &tm) == NULL) {
+               error("localtime_r() failed");
+               /* localtime_r() does not set errno */
+               return -EINVAL;
+       }
+
+       att_put_u16(1900 + tm.tm_year, &value[0]); /* Year */
+       value[2] = tm.tm_mon + 1; /* Month */
+       value[3] = tm.tm_mday; /* Day */
+       value[4] = tm.tm_hour; /* Hours */
+       value[5] = tm.tm_min; /* Minutes */
+       value[6] = tm.tm_sec; /* Seconds */
+       value[7] = tm.tm_wday == 0 ? 7 : tm.tm_wday; /* Day of Week */
+       /* From Time Profile spec: "The number of 1/256 fractions of a second."
+        * In 1s there are 256 fractions, in 1ns there are 256/10^9 fractions.
+        * To avoid integer overflow, we use the equivalent 1/3906250 ratio. */
+       value[8] = tp.tv_nsec / 3906250; /* Fractions256 */
+       value[9] = 0x00; /* Adjust Reason */
+
+       return 0;
+}
+
+static uint8_t current_time_read(struct attribute *a,
+                                struct btd_device *device, gpointer user_data)
+{
+       uint8_t value[10];
+
+       if (encode_current_time(value) < 0)
+               return ATT_ECODE_IO;
+
+       /* FIXME: Provide the adapter in next function */
+       attrib_db_update(NULL, a->handle, NULL, value, sizeof(value), NULL);
+
+       return 0;
+}
+
+static uint8_t local_time_info_read(struct attribute *a,
+                               struct btd_device *device, gpointer user_data)
+{
+       uint8_t value[2];
+
+       DBG("a=%p", a);
+
+       tzset();
+
+       /* FIXME: POSIX "daylight" variable only indicates whether there is DST
+        * for the local time or not. The offset is unknown. */
+       value[0] = daylight ? 0xff : 0x00;
+
+       /* Convert POSIX "timezone" (seconds West of GMT) to Time Profile
+        * format (offset from UTC in number of 15 minutes increments). */
+       value[1] = (uint8_t) (-1 * timezone / (60 * 15));
+
+       /* FIXME: Provide the adapter in next function */
+       attrib_db_update(NULL, a->handle, NULL, value, sizeof(value), NULL);
+
+       return 0;
+}
+
+static void register_current_time_service(void)
+{
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, CURRENT_TIME_SVC_UUID);
+
+       /* Current Time service */
+       /* FIXME: Provide the adapter in next function */
+       gatt_service_add(NULL, GATT_PRIM_SVC_UUID, &uuid,
+                               /* CT Time characteristic */
+                               GATT_OPT_CHR_UUID, CT_TIME_CHR_UUID,
+                               GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ |
+                                                       ATT_CHAR_PROPER_NOTIFY,
+                               GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                                               current_time_read, NULL,
+
+                               /* Local Time Information characteristic */
+                               GATT_OPT_CHR_UUID, LOCAL_TIME_INFO_CHR_UUID,
+                               GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ,
+                               GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+                                               local_time_info_read, NULL,
+
+                               GATT_OPT_INVALID);
+}
+
+int time_server_init(void)
+{
+       register_current_time_service();
+
+       return 0;
+}
+
+void time_server_exit(void)
+{
+}
diff --git a/time/server.h b/time/server.h
new file mode 100644 (file)
index 0000000..621bf2b
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Nokia Corporation
+ *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+int time_server_init(void);
+void time_server_exit(void);
diff --git a/tools/avctrl.8 b/tools/avctrl.8
new file mode 100644 (file)
index 0000000..7c3759d
--- /dev/null
@@ -0,0 +1,39 @@
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH AVCTRL 8 "JUNE 6, 2005" "" ""
+
+.SH NAME
+avctrl \- Bluetooth Audio/Video control utility
+.SH SYNOPSIS
+.BR "avctrl
+[
+.I options
+]
+<command>
+.SH DESCRIPTION
+.B avctrl
+is used to control the Audio/Video dongles.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible options.
+.TP
+.BI -q
+Don't display any messages.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/avctrl.c b/tools/avctrl.c
new file mode 100644 (file)
index 0000000..3621a68
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <usb.h>
+
+#ifdef NEED_USB_GET_BUSSES
+static inline struct usb_bus *usb_get_busses(void)
+{
+       return usb_busses;
+}
+#endif
+
+#ifndef USB_DIR_OUT
+#define USB_DIR_OUT    0x00
+#endif
+
+#ifndef USB_DIR_IN
+#define USB_DIR_IN     0x80
+#endif
+
+#define HID_REQ_GET_REPORT     0x01
+#define HID_REQ_GET_IDLE       0x02
+#define HID_REQ_GET_PROTOCOL   0x03
+#define HID_REQ_SET_REPORT     0x09
+#define HID_REQ_SET_IDLE       0x0a
+#define HID_REQ_SET_PROTOCOL   0x0b
+
+struct device_info;
+
+struct device_id {
+       uint16_t vendor;
+       uint16_t product;
+       int (*func)(struct device_info *dev, int argc, char *argv[]);
+};
+
+struct device_info {
+       struct usb_device *dev;
+       struct device_id *id;
+};
+
+#define GET_STATE              0x01
+#define GET_REMOTE_BDADDR      0x02
+#define DISCOVER               0x03
+#define SWITCH_TO_DFU          0x04
+#define READ_CODEC             0x05
+
+static int dongle_csr(struct device_info *devinfo, int argc, char *argv[])
+{
+       char buf[8];
+       struct usb_dev_handle *udev;
+       int err, intf = 2;
+
+       memset(buf, 0, sizeof(buf));
+
+       if (!strncasecmp(argv[0], "discover", 4))
+               buf[0] = DISCOVER;
+       else if (!strncasecmp(argv[0], "switch", 3))
+               buf[0] = SWITCH_TO_DFU;
+       else if (!strncasecmp(argv[0], "dfu", 3))
+               buf[0] = SWITCH_TO_DFU;
+       else
+               return -EINVAL;
+
+       udev = usb_open(devinfo->dev);
+       if (!udev)
+               return -errno;
+
+       if (usb_claim_interface(udev, intf) < 0) {
+               err = -errno;
+               usb_close(udev);
+               return err;
+       }
+
+       err = usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                               HID_REQ_SET_REPORT, 0x03 << 8, intf, buf, sizeof(buf), 10000);
+
+       if (err == 0) {
+               err = -1;
+               errno = EALREADY;
+       } else {
+               if (errno == ETIMEDOUT)
+                       err = 0;
+       }
+
+       usb_release_interface(udev, intf);
+       usb_close(udev);
+
+       return err;
+}
+
+static struct device_id device_list[] = {
+       { 0x0a12, 0x1004, dongle_csr },
+       { -1 }
+};
+
+static struct device_id *match_device(uint16_t vendor, uint16_t product)
+{
+       int i;
+
+       for (i = 0; device_list[i].func; i++) {
+               if (vendor == device_list[i].vendor &&
+                               product == device_list[i].product)
+                       return &device_list[i];
+       }
+
+       return NULL;
+}
+
+static int find_devices(struct device_info *devinfo, size_t size)
+{
+       struct usb_bus *bus;
+       struct usb_device *dev;
+       struct device_id *id;
+       unsigned int count = 0;
+
+       usb_find_busses();
+       usb_find_devices();
+
+       for (bus = usb_get_busses(); bus; bus = bus->next)
+               for (dev = bus->devices; dev; dev = dev->next) {
+                       id = match_device(dev->descriptor.idVendor,
+                                               dev->descriptor.idProduct);
+                       if (!id)
+                               continue;
+
+                       if (count < size) {
+                               devinfo[count].dev = dev;
+                               devinfo[count].id = id;
+                               count++;
+                       }
+               }
+
+       return count;
+}
+
+static void usage(void)
+{
+       printf("avctrl - Bluetooth Audio/Video control utility\n\n");
+
+       printf("Usage:\n"
+               "\tavctrl [options] <command>\n"
+               "\n");
+
+       printf("Options:\n"
+               "\t-h, --help           Display help\n"
+               "\t-q, --quiet          Don't display any messages\n"
+               "\n");
+
+       printf("Commands:\n"
+               "\tdiscover         Simulate pressing the discover button\n"
+               "\tswitch           Switch the dongle to DFU mode\n"
+               "\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "quiet",      0, 0, 'q' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       struct device_info dev[16];
+       int i, opt, num, quiet = 0;
+
+       while ((opt = getopt_long(argc, argv, "+qh", main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'q':
+                       quiet = 1;
+                       break;
+               case 'h':
+                       usage();
+                       exit(0);
+               default:
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       usb_init();
+
+       num = find_devices(dev, sizeof(dev) / sizeof(dev[0]));
+       if (num <= 0) {
+               if (!quiet)
+                       fprintf(stderr, "No Audio/Video devices found\n");
+               exit(1);
+       }
+
+       for (i = 0; i < num; i++) {
+               struct device_id *id = dev[i].id;
+               int err;
+
+               if (!quiet)
+                       printf("Selecting device %04x:%04x ",
+                                               id->vendor, id->product);
+               fflush(stdout);
+
+               err = id->func(&dev[i], argc, argv);
+               if (err < 0) {
+                       if (!quiet)
+                               printf("failed (%s)\n", strerror(-err));
+               } else {
+                       if (!quiet)
+                               printf("was successful\n");
+               }
+       }
+
+       return 0;
+}
diff --git a/tools/avinfo.c b/tools/avinfo.c
new file mode 100644 (file)
index 0000000..63b0da6
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#define AVDTP_PSM                      25
+
+/* Commands */
+#define AVDTP_DISCOVER                 0x01
+#define AVDTP_GET_CAPABILITIES         0x02
+
+#define AVDTP_PKT_TYPE_SINGLE          0x00
+
+#define AVDTP_MSG_TYPE_COMMAND         0x00
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT          0x01
+#define AVDTP_REPORTING                        0x02
+#define AVDTP_RECOVERY                 0x03
+#define AVDTP_CONTENT_PROTECTION       0x04
+#define AVDTP_HEADER_COMPRESSION       0x05
+#define AVDTP_MULTIPLEXING             0x06
+#define AVDTP_MEDIA_CODEC              0x07
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE          0x00
+#define AVDTP_SEP_TYPE_SINK            0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO         0x00
+#define AVDTP_MEDIA_TYPE_VIDEO         0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA    0x02
+
+#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 << 0)
+
+#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 << 0)
+
+#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 << 0)
+
+#define SBC_SUBBANDS_4                 (1 << 1)
+#define SBC_SUBBANDS_8                 (1 << 0)
+
+#define SBC_ALLOCATION_SNR             (1 << 1)
+#define SBC_ALLOCATION_LOUDNESS                (1 << 0)
+
+#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 << 0)
+
+#define MPEG_LAYER_MP1                 (1 << 2)
+#define MPEG_LAYER_MP2                 (1 << 1)
+#define MPEG_LAYER_MP3                 (1 << 0)
+
+#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 << 0)
+
+#define MPEG_BIT_RATE_VBR              0x8000
+#define MPEG_BIT_RATE_320000           0x4000
+#define MPEG_BIT_RATE_256000           0x2000
+#define MPEG_BIT_RATE_224000           0x1000
+#define MPEG_BIT_RATE_192000           0x0800
+#define MPEG_BIT_RATE_160000           0x0400
+#define MPEG_BIT_RATE_128000           0x0200
+#define MPEG_BIT_RATE_112000           0x0100
+#define MPEG_BIT_RATE_96000            0x0080
+#define MPEG_BIT_RATE_80000            0x0040
+#define MPEG_BIT_RATE_64000            0x0020
+#define MPEG_BIT_RATE_56000            0x0010
+#define MPEG_BIT_RATE_48000            0x0008
+#define MPEG_BIT_RATE_40000            0x0004
+#define MPEG_BIT_RATE_32000            0x0002
+#define MPEG_BIT_RATE_FREE             0x0001
+
+struct avdtp_service_capability {
+       uint8_t category;
+       uint8_t length;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+       uint8_t signal_id:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+       uint8_t rfa0:1;
+       uint8_t inuse:1;
+       uint8_t seid:6;
+       uint8_t rfa2:3;
+       uint8_t type:1;
+       uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid_req {
+       struct avdtp_header header;
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct avdtp_media_codec_capability {
+       uint8_t rfa0:4;
+       uint8_t media_type:4;
+       uint8_t media_codec_type;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+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 avdtp_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+       uint8_t rfa0:2;
+       uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct seid_info {
+       uint8_t seid:6;
+       uint8_t inuse:1;
+       uint8_t rfa0:1;
+       uint8_t media_type:4;
+       uint8_t type:1;
+       uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid_req {
+       struct avdtp_header header;
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_media_codec_capability {
+       uint8_t media_type:4;
+       uint8_t rfa0:4;
+       uint8_t media_codec_type;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+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 discover_resp {
+       struct avdtp_header header;
+       struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+       struct avdtp_header header;
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+
+static void print_mpeg12(struct mpeg_codec_cap *mpeg)
+{
+       printf("\tMedia Codec: MPEG12\n\t\tChannel Modes: ");
+
+       if (mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO)
+               printf("Mono ");
+       if (mpeg->channel_mode & MPEG_CHANNEL_MODE_DUAL_CHANNEL)
+               printf("DualChannel ");
+       if (mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO)
+               printf("Stereo ");
+       if (mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO)
+               printf("JointStereo");
+
+       printf("\n\t\tFrequencies: ");
+       if (mpeg->frequency & MPEG_SAMPLING_FREQ_16000)
+               printf("16Khz ");
+       if (mpeg->frequency & MPEG_SAMPLING_FREQ_22050)
+               printf("22.05Khz ");
+       if (mpeg->frequency & MPEG_SAMPLING_FREQ_24000)
+               printf("24Khz ");
+       if (mpeg->frequency & MPEG_SAMPLING_FREQ_32000)
+               printf("32Khz ");
+       if (mpeg->frequency & MPEG_SAMPLING_FREQ_44100)
+               printf("44.1Khz ");
+       if (mpeg->frequency & MPEG_SAMPLING_FREQ_48000)
+               printf("48Khz ");
+
+       printf("\n\t\tCRC: %s", mpeg->crc ? "Yes" : "No");
+
+       printf("\n\t\tLayer: ");
+       if (mpeg->layer & MPEG_LAYER_MP1)
+               printf("1 ");
+       if (mpeg->layer & MPEG_LAYER_MP2)
+               printf("2 ");
+       if (mpeg->layer & MPEG_LAYER_MP3)
+               printf("3 ");
+
+       printf("\n\t\tBit Rate: ");
+       if (mpeg->bitrate & MPEG_BIT_RATE_FREE)
+               printf("Free format");
+       else {
+               if (mpeg->bitrate & MPEG_BIT_RATE_32000)
+                       printf("32kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_40000)
+                       printf("40kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_48000)
+                       printf("48kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_56000)
+                       printf("56kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_64000)
+                       printf("64kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_80000)
+                       printf("80kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_96000)
+                       printf("96kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_112000)
+                       printf("112kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_128000)
+                       printf("128kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_160000)
+                       printf("160kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_192000)
+                       printf("192kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_224000)
+                       printf("224kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_256000)
+                       printf("256kbps ");
+               if (mpeg->bitrate & MPEG_BIT_RATE_320000)
+                       printf("320kbps ");
+       }
+
+       printf("\n\t\tVBR: %s", mpeg->bitrate & MPEG_BIT_RATE_VBR ? "Yes" :
+               "No");
+
+       printf("\n\t\tPayload Format: ");
+       if (mpeg->mpf)
+               printf("RFC-2250 RFC-3119\n");
+       else
+               printf("RFC-2250\n");
+}
+
+static void print_sbc(struct sbc_codec_cap *sbc)
+{
+       printf("\tMedia Codec: SBC\n\t\tChannel Modes: ");
+
+       if (sbc->channel_mode & SBC_CHANNEL_MODE_MONO)
+               printf("Mono ");
+       if (sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
+               printf("DualChannel ");
+       if (sbc->channel_mode & SBC_CHANNEL_MODE_STEREO)
+               printf("Stereo ");
+       if (sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
+               printf("JointStereo");
+
+       printf("\n\t\tFrequencies: ");
+       if (sbc->frequency & SBC_SAMPLING_FREQ_16000)
+               printf("16Khz ");
+       if (sbc->frequency & SBC_SAMPLING_FREQ_32000)
+               printf("32Khz ");
+       if (sbc->frequency & SBC_SAMPLING_FREQ_44100)
+               printf("44.1Khz ");
+       if (sbc->frequency & SBC_SAMPLING_FREQ_48000)
+               printf("48Khz ");
+
+       printf("\n\t\tSubbands: ");
+       if (sbc->allocation_method & SBC_SUBBANDS_4)
+               printf("4 ");
+       if (sbc->allocation_method & SBC_SUBBANDS_8)
+               printf("8");
+
+       printf("\n\t\tBlocks: ");
+       if (sbc->block_length & SBC_BLOCK_LENGTH_4)
+               printf("4 ");
+       if (sbc->block_length & SBC_BLOCK_LENGTH_8)
+               printf("8 ");
+       if (sbc->block_length & SBC_BLOCK_LENGTH_12)
+               printf("12 ");
+       if (sbc->block_length & SBC_BLOCK_LENGTH_16)
+               printf("16 ");
+
+       printf("\n\t\tBitpool Range: %d-%d\n",
+                               sbc->min_bitpool, sbc->max_bitpool);
+}
+
+static void print_media_codec(struct avdtp_media_codec_capability *cap)
+{
+       switch (cap->media_codec_type) {
+       case A2DP_CODEC_SBC:
+               print_sbc((void *) cap);
+               break;
+       case A2DP_CODEC_MPEG12:
+               print_mpeg12((void *) cap);
+               break;
+       default:
+               printf("\tMedia Codec: Unknown\n");
+       }
+}
+
+static void print_caps(void *data, int size)
+{
+       int processed;
+
+       for (processed = 0; processed + 2 < size;) {
+               struct avdtp_service_capability *cap;
+
+               cap = data;
+
+               if (processed + 2 + cap->length > size) {
+                       printf("Invalid capability data in getcap resp\n");
+                       break;
+               }
+
+               switch (cap->category) {
+               case AVDTP_MEDIA_TRANSPORT:
+               case AVDTP_REPORTING:
+               case AVDTP_RECOVERY:
+               case AVDTP_CONTENT_PROTECTION:
+               case AVDTP_MULTIPLEXING:
+                       /* FIXME: Add proper functions */
+                       break;
+               case AVDTP_MEDIA_CODEC:
+                       print_media_codec((void *) cap->data);
+                       break;
+               }
+
+               processed += 2 + cap->length;
+               data += 2 + cap->length;
+       }
+}
+
+static void init_request(struct avdtp_header *header, int request_id)
+{
+       static int transaction = 0;
+
+       header->packet_type = AVDTP_PKT_TYPE_SINGLE;
+       header->message_type = AVDTP_MSG_TYPE_COMMAND;
+       header->transaction = transaction;
+       header->signal_id = request_id;
+
+       /* clear rfa bits */
+       header->rfa0 = 0;
+
+       transaction = (transaction + 1) % 16;
+}
+
+static ssize_t avdtp_send(int sk, void *data, int len)
+{
+       ssize_t ret;
+
+       ret = send(sk, data, len, 0);
+
+       if (ret < 0)
+               ret = -errno;
+       else if (ret != len)
+               ret = -EIO;
+
+       if (ret < 0) {
+               printf("Unable to send message: %s (%zd)\n",
+                                               strerror(-ret), -ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+static ssize_t avdtp_receive(int sk, void *data, int len)
+{
+       ssize_t ret;
+
+       ret = recv(sk, data, len, 0);
+
+       if (ret < 0) {
+               printf("Unable to receive message: %s (%d)\n",
+                                               strerror(errno), errno);
+               return -errno;
+       }
+
+       return ret;
+}
+
+static ssize_t avdtp_get_caps(int sk, int seid)
+{
+       struct seid_req req;
+       char buffer[1024];
+       struct getcap_resp *caps = (void *) buffer;
+       ssize_t ret;
+
+       memset(&req, 0, sizeof(req));
+       init_request(&req.header, AVDTP_GET_CAPABILITIES);
+       req.acp_seid = seid;
+
+       ret = avdtp_send(sk, &req, sizeof(req));
+       if (ret < 0)
+               return ret;
+
+       memset(&buffer, 0, sizeof(buffer));
+       ret = avdtp_receive(sk, caps, sizeof(buffer));
+       if (ret < 0)
+               return ret;
+
+       if ((size_t) ret < (sizeof(struct getcap_resp) + 4 +
+                       sizeof(struct avdtp_media_codec_capability))) {
+               printf("Invalid capabilities\n");
+               return -1;
+       }
+
+       print_caps(caps, ret);
+
+       return 0;
+}
+
+static ssize_t avdtp_discover(int sk)
+{
+       struct avdtp_header req;
+       char buffer[256];
+       struct discover_resp *discover = (void *) buffer;
+       int seps, i;
+       ssize_t ret;
+
+       memset(&req, 0, sizeof(req));
+       init_request(&req, AVDTP_DISCOVER);
+
+       ret = avdtp_send(sk, &req, sizeof(req));
+       if (ret < 0)
+               return ret;
+
+       memset(&buffer, 0, sizeof(buffer));
+       ret = avdtp_receive(sk, discover, sizeof(buffer));
+       if (ret < 0)
+               return ret;
+
+       seps = (ret - sizeof(struct avdtp_header)) / sizeof(struct seid_info);
+       for (i = 0; i < seps; i++) {
+               const char *type, *media;
+
+               switch (discover->seps[i].type) {
+               case AVDTP_SEP_TYPE_SOURCE:
+                       type = "Source";
+                       break;
+               case AVDTP_SEP_TYPE_SINK:
+                       type = "Sink";
+                       break;
+               default:
+                       type = "Invalid";
+               }
+
+               switch (discover->seps[i].media_type) {
+               case AVDTP_MEDIA_TYPE_AUDIO:
+                       media = "Audio";
+                       break;
+               case AVDTP_MEDIA_TYPE_VIDEO:
+                       media = "Video";
+                       break;
+               case AVDTP_MEDIA_TYPE_MULTIMEDIA:
+                       media = "Multimedia";
+                       break;
+               default:
+                       media = "Invalid";
+               }
+
+               printf("Stream End-Point #%d: %s %s %s\n",
+                                       discover->seps[i].seid, media, type,
+                                       discover->seps[i].inuse ? "*" : "");
+
+               avdtp_get_caps(sk, discover->seps[i].seid);
+       }
+
+       return 0;
+}
+
+static int l2cap_connect(bdaddr_t *src, bdaddr_t *dst)
+{
+       struct sockaddr_l2 l2a;
+       int sk;
+
+       memset(&l2a, 0, sizeof(l2a));
+       l2a.l2_family = AF_BLUETOOTH;
+       bacpy(&l2a.l2_bdaddr, src);
+
+       sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+       if (sk < 0) {
+               printf("Cannot create L2CAP socket. %s(%d)\n", strerror(errno),
+                               errno);
+               return -errno;
+       }
+
+       if (bind(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) {
+               printf("Bind failed. %s (%d)\n", strerror(errno), errno);
+               return -errno;
+       }
+
+       memset(&l2a, 0, sizeof(l2a));
+       l2a.l2_family = AF_BLUETOOTH;
+       bacpy(&l2a.l2_bdaddr, dst);
+       l2a.l2_psm = htobs(AVDTP_PSM);
+
+       if (connect(sk, (struct sockaddr *) &l2a, sizeof(l2a)) < 0) {
+               printf("Connect failed. %s(%d)\n", strerror(errno), errno);
+               return -errno;
+       }
+
+       return sk;
+}
+
+static void usage(void)
+{
+       printf("avinfo - Audio/Video Info Tool ver %s\n", VERSION);
+       printf("Usage:\n"
+               "\tavinfo [options] <remote address>\n");
+       printf("Options:\n"
+               "\t-h\t\tDisplay help\n"
+               "\t-i\t\tSpecify source interface\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "device",     1, 0, 'i' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       bdaddr_t src, dst;
+       int opt, sk, dev_id;
+
+       if (argc < 2) {
+               usage();
+               exit(0);
+       }
+
+       bacpy(&src, BDADDR_ANY);
+       dev_id = hci_get_route(&src);
+       if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) {
+               printf("Cannot find any local adapter\n");
+               exit(-1);
+       }
+
+       while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'i':
+                       if (!strncmp(optarg, "hci", 3))
+                               hci_devba(atoi(optarg + 3), &src);
+                       else
+                               str2ba(optarg, &src);
+                       break;
+
+               case 'h':
+               default:
+                       usage();
+                       exit(0);
+               }
+       }
+
+       printf("Connecting ... \n");
+
+       if (bachk(argv[optind]) < 0) {
+               printf("Invalid argument\n");
+               exit(1);
+       }
+
+       str2ba(argv[optind], &dst);
+       sk = l2cap_connect(&src, &dst);
+       if (sk < 0)
+               exit(1);
+
+       if (avdtp_discover(sk) < 0)
+               exit(1);
+
+       return 0;
+}
diff --git a/tools/bccmd.8 b/tools/bccmd.8
new file mode 100644 (file)
index 0000000..28cbe88
--- /dev/null
@@ -0,0 +1,130 @@
+.TH BCCMD 8 "Jun 20 2006" BlueZ "Linux System Administration"
+.SH NAME
+bccmd \- Utility for the CSR BCCMD interface
+.SH SYNOPSIS
+.B bccmd
+.br
+.B bccmd [-t <transport>] [-d <device>] <command> [<args>]
+.br
+.B bccmd [-h --help]
+.br
+.SH DESCRIPTION
+.B
+bccmd
+issues BlueCore commands to
+.B
+Cambridge Silicon Radio
+devices. If run without the <command> argument, a short help page will be displayed.
+.SH OPTIONS
+.TP
+.BI -t\ <transport>
+Specify the communication transport. Valid options are:
+.RS
+.TP
+.BI HCI
+Local device with Host Controller Interface (default).
+.TP
+.BI USB
+Direct USB connection.
+.TP
+.BI BCSP
+Blue Core Serial Protocol.
+.TP
+.BI H4
+H4 serial protocol.
+.TP
+.BI 3WIRE
+3WIRE protocol (not implemented).
+.SH
+.TP
+.BI -d\ <dev>
+Specify a particular device to operate on. If not specified, default is the first available HCI device
+or /dev/ttyS0 for serial transports.
+.SH COMMANDS
+.TP
+.BI builddef
+Get build definitions
+.TP
+.BI keylen\ <handle>
+Get current crypt key length
+.TP
+.BI clock
+Get local Bluetooth clock
+.TP
+.BI rand
+Get random number
+.TP
+.BI chiprev
+Get chip revision
+.TP
+.BI buildname
+Get the full build name
+.TP
+.BI panicarg
+Get panic code argument
+.TP
+.BI faultarg
+Get fault code argument
+.TP
+.BI coldreset
+Perform cold reset
+.TP
+.BI warmreset
+Perform warm reset
+.TP
+.BI disabletx
+Disable TX on the device
+.TP
+.BI enabletx
+Enable TX on the device
+.TP
+.BI singlechan\ <channel>
+Lock radio on specific channel
+.TP
+.BI hoppingon
+Revert to channel hopping
+.TP
+.BI rttxdata1\ <decimal\ freq\ MHz>\ <level>
+TXData1 radio test
+.TP
+.BI radiotest\ <decimal\ freq\ MHz>\ <level>\ <id>
+Run radio tests, tests 4, 6 and 7 are transmit tests
+.TP
+.BI memtypes
+Get memory types
+.TP
+.BI psget\ [-r]\ [-s\ <stores>]\ <key>
+Get value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI psset\ [-r]\ [-s\ <stores>]\ <key>\ <value>
+Set value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI psclr\ [-r]\ [-s\ <stores>]\ <key>
+Clear value for PS key.
+-r sends a warm reset afterwards
+.TP
+.BI pslist\ [-r]\ [-s\ <stores>]
+List all PS keys.
+-r sends a warm reset afterwards
+.TP
+.BI psread\ [-r]\ [-s\ <stores>]
+Read all PS keys.
+-r sends a warm reset afterwards
+.TP
+.BI psload\ [-r]\ [-s\ <stores>]\ <file>
+Load all PS keys from PSR file.
+-r sends a warm reset afterwards
+.TP
+.BI pscheck\ [-r]\ [-s\ <stores>]\ <file>
+Check syntax of PSR file.
+-r sends a warm reset afterwards
+.SH KEYS
+bdaddr country devclass keymin keymax features commands version
+remver hciextn mapsco baudrate hostintf anafreq anaftrim usbvid
+usbpid dfupid bootmode
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org>,
+man page by Adam Laurie <adam@algroup.co.uk>
+.PP
diff --git a/tools/bccmd.c b/tools/bccmd.c
new file mode 100644 (file)
index 0000000..952bf13
--- /dev/null
@@ -0,0 +1,1237 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "csr.h"
+
+#define CSR_TRANSPORT_UNKNOWN  0
+#define CSR_TRANSPORT_HCI      1
+#define CSR_TRANSPORT_USB      2
+#define CSR_TRANSPORT_BCSP     3
+#define CSR_TRANSPORT_H4       4
+#define CSR_TRANSPORT_3WIRE    5
+
+#define CSR_STORES_PSI         (0x0001)
+#define CSR_STORES_PSF         (0x0002)
+#define CSR_STORES_PSROM       (0x0004)
+#define CSR_STORES_PSRAM       (0x0008)
+#define CSR_STORES_DEFAULT     (CSR_STORES_PSI | CSR_STORES_PSF)
+
+#define CSR_TYPE_NULL          0
+#define CSR_TYPE_COMPLEX       1
+#define CSR_TYPE_UINT8         2
+#define CSR_TYPE_UINT16                3
+#define CSR_TYPE_UINT32                4
+
+#define CSR_TYPE_ARRAY         CSR_TYPE_COMPLEX
+#define CSR_TYPE_BDADDR                CSR_TYPE_COMPLEX
+
+static inline int transport_open(int transport, char *device, speed_t bcsp_rate)
+{
+       switch (transport) {
+       case CSR_TRANSPORT_HCI:
+               return csr_open_hci(device);
+#ifdef HAVE_LIBUSB
+       case CSR_TRANSPORT_USB:
+               return csr_open_usb(device);
+#endif
+       case CSR_TRANSPORT_BCSP:
+               return csr_open_bcsp(device, bcsp_rate);
+       case CSR_TRANSPORT_H4:
+               return csr_open_h4(device);
+       case CSR_TRANSPORT_3WIRE:
+               return csr_open_3wire(device);
+       default:
+               fprintf(stderr, "Unsupported transport\n");
+               return -1;
+       }
+}
+
+static inline int transport_read(int transport, uint16_t varid, uint8_t *value, uint16_t length)
+{
+       switch (transport) {
+       case CSR_TRANSPORT_HCI:
+               return csr_read_hci(varid, value, length);
+#ifdef HAVE_LIBUSB
+       case CSR_TRANSPORT_USB:
+               return csr_read_usb(varid, value, length);
+#endif
+       case CSR_TRANSPORT_BCSP:
+               return csr_read_bcsp(varid, value, length);
+       case CSR_TRANSPORT_H4:
+               return csr_read_h4(varid, value, length);
+       case CSR_TRANSPORT_3WIRE:
+               return csr_read_3wire(varid, value, length);
+       default:
+               errno = EOPNOTSUPP;
+               return -1;
+       }
+}
+
+static inline int transport_write(int transport, uint16_t varid, uint8_t *value, uint16_t length)
+{
+       switch (transport) {
+       case CSR_TRANSPORT_HCI:
+               return csr_write_hci(varid, value, length);
+#ifdef HAVE_LIBUSB
+       case CSR_TRANSPORT_USB:
+               return csr_write_usb(varid, value, length);
+#endif
+       case CSR_TRANSPORT_BCSP:
+               return csr_write_bcsp(varid, value, length);
+       case CSR_TRANSPORT_H4:
+               return csr_write_h4(varid, value, length);
+       case CSR_TRANSPORT_3WIRE:
+               return csr_write_3wire(varid, value, length);
+       default:
+               errno = EOPNOTSUPP;
+               return -1;
+       }
+}
+
+static inline void transport_close(int transport)
+{
+       switch (transport) {
+       case CSR_TRANSPORT_HCI:
+               csr_close_hci();
+               break;
+#ifdef HAVE_LIBUSB
+       case CSR_TRANSPORT_USB:
+               csr_close_usb();
+               break;
+#endif
+       case CSR_TRANSPORT_BCSP:
+               csr_close_bcsp();
+               break;
+       case CSR_TRANSPORT_H4:
+               csr_close_h4();
+               break;
+       case CSR_TRANSPORT_3WIRE:
+               csr_close_3wire();
+               break;
+       }
+}
+
+static struct {
+       uint16_t pskey;
+       int type;
+       int size;
+       char *str;
+} storage[] = {
+       { CSR_PSKEY_BDADDR,                   CSR_TYPE_BDADDR,  8,  "bdaddr"   },
+       { CSR_PSKEY_COUNTRYCODE,              CSR_TYPE_UINT16,  0,  "country"  },
+       { CSR_PSKEY_CLASSOFDEVICE,            CSR_TYPE_UINT32,  0,  "devclass" },
+       { CSR_PSKEY_ENC_KEY_LMIN,             CSR_TYPE_UINT16,  0,  "keymin"   },
+       { CSR_PSKEY_ENC_KEY_LMAX,             CSR_TYPE_UINT16,  0,  "keymax"   },
+       { CSR_PSKEY_LOCAL_SUPPORTED_FEATURES, CSR_TYPE_ARRAY,   8,  "features" },
+       { CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS, CSR_TYPE_ARRAY,   18, "commands" },
+       { CSR_PSKEY_HCI_LMP_LOCAL_VERSION,    CSR_TYPE_UINT16,  0,  "version"  },
+       { CSR_PSKEY_LMP_REMOTE_VERSION,       CSR_TYPE_UINT8,   0,  "remver"   },
+       { CSR_PSKEY_HOSTIO_USE_HCI_EXTN,      CSR_TYPE_UINT16,  0,  "hciextn"  },
+       { CSR_PSKEY_HOSTIO_MAP_SCO_PCM,       CSR_TYPE_UINT16,  0,  "mapsco"   },
+       { CSR_PSKEY_UART_BAUDRATE,            CSR_TYPE_UINT16,  0,  "baudrate" },
+       { CSR_PSKEY_HOST_INTERFACE,           CSR_TYPE_UINT16,  0,  "hostintf" },
+       { CSR_PSKEY_ANA_FREQ,                 CSR_TYPE_UINT16,  0,  "anafreq"  },
+       { CSR_PSKEY_ANA_FTRIM,                CSR_TYPE_UINT16,  0,  "anaftrim" },
+       { CSR_PSKEY_USB_VENDOR_ID,            CSR_TYPE_UINT16,  0,  "usbvid"   },
+       { CSR_PSKEY_USB_PRODUCT_ID,           CSR_TYPE_UINT16,  0,  "usbpid"   },
+       { CSR_PSKEY_USB_DFU_PRODUCT_ID,       CSR_TYPE_UINT16,  0,  "dfupid"   },
+       { CSR_PSKEY_INITIAL_BOOTMODE,         CSR_TYPE_UINT16,  0,  "bootmode" },
+       { 0x0000 },
+};
+
+static char *storestostr(uint16_t stores)
+{
+       switch (stores) {
+       case 0x0000:
+               return "Default";
+       case 0x0001:
+               return "psi";
+       case 0x0002:
+               return "psf";
+       case 0x0004:
+               return "psrom";
+       case 0x0008:
+               return "psram";
+       default:
+               return "Unknown";
+       }
+}
+
+static char *memorytostr(uint16_t type)
+{
+       switch (type) {
+       case 0x0000:
+               return "Flash memory";
+       case 0x0001:
+               return "EEPROM";
+       case 0x0002:
+               return "RAM (transient)";
+       case 0x0003:
+               return "ROM (or \"read-only\" flash memory)";
+       default:
+               return "Unknown";
+       }
+}
+
+#define OPT_RANGE(min, max) \
+               if (argc < (min)) { errno = EINVAL; return -1; } \
+               if (argc > (max)) { errno = E2BIG; return -1; }
+
+static struct option help_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static int opt_help(int argc, char *argv[], int *help)
+{
+       int opt;
+
+       while ((opt=getopt_long(argc, argv, "+h", help_options, NULL)) != EOF) {
+               switch (opt) {
+               case 'h':
+                       if (help)
+                               *help = 1;
+                       break;
+               }
+       }
+
+       return optind;
+}
+
+#define OPT_HELP(range, help) \
+               opt_help(argc, argv, (help)); \
+               argc -= optind; argv += optind; optind = 0; \
+               OPT_RANGE((range), (range))
+
+static int cmd_builddef(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t def = 0x0000, nextdef = 0x0000;
+       int err = 0;
+
+       OPT_HELP(0, NULL);
+
+       printf("Build definitions:\n");
+
+       while (1) {
+               memset(array, 0, sizeof(array));
+               array[0] = def & 0xff;
+               array[1] = def >> 8;
+
+               err = transport_read(transport, CSR_VARID_GET_NEXT_BUILDDEF, array, 8);
+               if (err < 0)
+                       break;
+
+               nextdef = array[2] | (array[3] << 8);
+
+               if (nextdef == 0x0000)
+                       break;
+
+               def = nextdef;
+
+               printf("0x%04x - %s\n", def, csr_builddeftostr(def));
+       }
+
+       return err;
+}
+
+static int cmd_keylen(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t handle, keylen;
+       int err;
+
+       OPT_HELP(1, NULL);
+
+       handle = atoi(argv[0]);
+
+       memset(array, 0, sizeof(array));
+       array[0] = handle & 0xff;
+       array[1] = handle >> 8;
+
+       err = transport_read(transport, CSR_VARID_CRYPT_KEY_LENGTH, array, 8);
+       if (err < 0)
+               return -1;
+
+       handle = array[0] | (array[1] << 8);
+       keylen = array[2] | (array[3] << 8);
+
+       printf("Crypt key length: %d bit\n", keylen * 8);
+
+       return 0;
+}
+
+static int cmd_clock(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint32_t clock;
+       int err;
+
+       OPT_HELP(0, NULL);
+
+       memset(array, 0, sizeof(array));
+
+       err = transport_read(transport, CSR_VARID_BT_CLOCK, array, 8);
+       if (err < 0)
+               return -1;
+
+       clock = array[2] | (array[3] << 8) | (array[0] << 16) | (array[1] << 24);
+
+       printf("Bluetooth clock: 0x%04x (%d)\n", clock, clock);
+
+       return 0;
+}
+
+static int cmd_rand(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t rand;
+       int err;
+
+       OPT_HELP(0, NULL);
+
+       memset(array, 0, sizeof(array));
+
+       err = transport_read(transport, CSR_VARID_RAND, array, 8);
+       if (err < 0)
+               return -1;
+
+       rand = array[0] | (array[1] << 8);
+
+       printf("Random number: 0x%02x (%d)\n", rand, rand);
+
+       return 0;
+}
+
+static int cmd_chiprev(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t rev;
+       char *str;
+       int err;
+
+       OPT_HELP(0, NULL);
+
+       memset(array, 0, sizeof(array));
+
+       err = transport_read(transport, CSR_VARID_CHIPREV, array, 8);
+       if (err < 0)
+               return -1;
+
+       rev = array[0] | (array[1] << 8);
+
+       switch (rev) {
+       case 0x64:
+               str = "BC1 ES";
+               break;
+       case 0x65:
+               str = "BC1";
+               break;
+       case 0x89:
+               str = "BC2-External A";
+               break;
+       case 0x8a:
+               str = "BC2-External B";
+               break;
+       case 0x28:
+               str = "BC2-ROM";
+               break;
+       case 0x43:
+               str = "BC3-Multimedia";
+               break;
+       case 0x15:
+               str = "BC3-ROM";
+               break;
+       case 0xe2:
+               str = "BC3-Flash";
+               break;
+       case 0x26:
+               str = "BC4-External";
+               break;
+       case 0x30:
+               str = "BC4-ROM";
+               break;
+       default:
+               str = "NA";
+               break;
+       }
+
+       printf("Chip revision: 0x%04x (%s)\n", rev, str);
+
+       return 0;
+}
+
+static int cmd_buildname(int transport, int argc, char *argv[])
+{
+       uint8_t array[130];
+       char name[64];
+       unsigned int i;
+       int err;
+
+       OPT_HELP(0, NULL);
+
+       memset(array, 0, sizeof(array));
+
+       err = transport_read(transport, CSR_VARID_READ_BUILD_NAME, array, 128);
+       if (err < 0)
+               return -1;
+
+       for (i = 0; i < sizeof(name); i++)
+               name[i] = array[(i * 2) + 4];
+
+       printf("Build name: %s\n", name);
+
+       return 0;
+}
+
+static int cmd_panicarg(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t error;
+       int err;
+
+       OPT_HELP(0, NULL);
+
+       memset(array, 0, sizeof(array));
+
+       err = transport_read(transport, CSR_VARID_PANIC_ARG, array, 8);
+       if (err < 0)
+               return -1;
+
+       error = array[0] | (array[1] << 8);
+
+       printf("Panic code: 0x%02x (%s)\n", error,
+                                       error < 0x100 ? "valid" : "invalid");
+
+       return 0;
+}
+
+static int cmd_faultarg(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t error;
+       int err;
+
+       OPT_HELP(0, NULL);
+
+       memset(array, 0, sizeof(array));
+
+       err = transport_read(transport, CSR_VARID_FAULT_ARG, array, 8);
+       if (err < 0)
+               return -1;
+
+       error = array[0] | (array[1] << 8);
+
+       printf("Fault code: 0x%02x (%s)\n", error,
+                                       error < 0x100 ? "valid" : "invalid");
+
+       return 0;
+}
+
+static int cmd_coldreset(int transport, int argc, char *argv[])
+{
+       return transport_write(transport, CSR_VARID_COLD_RESET, NULL, 0);
+}
+
+static int cmd_warmreset(int transport, int argc, char *argv[])
+{
+       return transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+}
+
+static int cmd_disabletx(int transport, int argc, char *argv[])
+{
+       return transport_write(transport, CSR_VARID_DISABLE_TX, NULL, 0);
+}
+
+static int cmd_enabletx(int transport, int argc, char *argv[])
+{
+       return transport_write(transport, CSR_VARID_ENABLE_TX, NULL, 0);
+}
+
+static int cmd_singlechan(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t channel;
+
+       OPT_HELP(1, NULL);
+
+       channel = atoi(argv[0]);
+
+       if (channel > 2401 && channel < 2481)
+               channel -= 2402;
+
+       if (channel > 78) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       memset(array, 0, sizeof(array));
+       array[0] = channel & 0xff;
+       array[1] = channel >> 8;
+
+       return transport_write(transport, CSR_VARID_SINGLE_CHAN, array, 8);
+}
+
+static int cmd_hoppingon(int transport, int argc, char *argv[])
+{
+       return transport_write(transport, CSR_VARID_HOPPING_ON, NULL, 0);
+}
+
+static int cmd_rttxdata1(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t freq, level;
+
+       OPT_HELP(2, NULL);
+
+       freq = atoi(argv[0]);
+
+       if (!strncasecmp(argv[1], "0x", 2))
+               level = strtol(argv[1], NULL, 16);
+       else
+               level = atoi(argv[1]);
+
+       memset(array, 0, sizeof(array));
+       array[0] = 0x04;
+       array[1] = 0x00;
+       array[2] = freq & 0xff;
+       array[3] = freq >> 8;
+       array[4] = level & 0xff;
+       array[5] = level >> 8;
+
+       return transport_write(transport, CSR_VARID_RADIOTEST, array, 8);
+}
+
+static int cmd_radiotest(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t freq, level, test;
+
+       OPT_HELP(3, NULL);
+
+       freq = atoi(argv[0]);
+
+       if (!strncasecmp(argv[1], "0x", 2))
+               level = strtol(argv[1], NULL, 16);
+       else
+               level = atoi(argv[1]);
+
+       test = atoi(argv[2]);
+
+       memset(array, 0, sizeof(array));
+       array[0] = test & 0xff;
+       array[1] = test >> 8;
+       array[2] = freq & 0xff;
+       array[3] = freq >> 8;
+       array[4] = level & 0xff;
+       array[5] = level >> 8;
+
+       return transport_write(transport, CSR_VARID_RADIOTEST, array, 8);
+}
+
+static int cmd_memtypes(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t type, stores[4] = { 0x0001, 0x0002, 0x0004, 0x0008 };
+       int i, err;
+
+       OPT_HELP(0, NULL);
+
+       for (i = 0; i < 4; i++) {
+               memset(array, 0, sizeof(array));
+               array[0] = stores[i] & 0xff;
+               array[1] = stores[i] >> 8;
+
+               err = transport_read(transport, CSR_VARID_PS_MEMORY_TYPE, array, 8);
+               if (err < 0)
+                       continue;
+
+               type = array[2] + (array[3] << 8);
+
+               printf("%s (0x%04x) = %s (%d)\n", storestostr(stores[i]),
+                                       stores[i], memorytostr(type), type);
+       }
+
+       return 0;
+}
+
+static struct option pskey_options[] = {
+       { "stores",     1, 0, 's' },
+       { "reset",      0, 0, 'r' },
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static int opt_pskey(int argc, char *argv[], uint16_t *stores, int *reset, int *help)
+{
+       int opt;
+
+       while ((opt=getopt_long(argc, argv, "+s:rh", pskey_options, NULL)) != EOF) {
+               switch (opt) {
+               case 's':
+                       if (!stores)
+                               break;
+                       if (!strcasecmp(optarg, "default"))
+                               *stores = 0x0000;
+                       else if (!strcasecmp(optarg, "implementation"))
+                               *stores = 0x0001;
+                       else if (!strcasecmp(optarg, "factory"))
+                               *stores = 0x0002;
+                       else if (!strcasecmp(optarg, "rom"))
+                               *stores = 0x0004;
+                       else if (!strcasecmp(optarg, "ram"))
+                               *stores = 0x0008;
+                       else if (!strcasecmp(optarg, "psi"))
+                               *stores = 0x0001;
+                       else if (!strcasecmp(optarg, "psf"))
+                               *stores = 0x0002;
+                       else if (!strcasecmp(optarg, "psrom"))
+                               *stores = 0x0004;
+                       else if (!strcasecmp(optarg, "psram"))
+                               *stores = 0x0008;
+                       else if (!strncasecmp(optarg, "0x", 2))
+                               *stores = strtol(optarg, NULL, 16);
+                       else
+                               *stores = atoi(optarg);
+                       break;
+
+               case 'r':
+                       if (reset)
+                               *reset = 1;
+                       break;
+
+               case 'h':
+                       if (help)
+                               *help = 1;
+                       break;
+               }
+       }
+
+       return optind;
+}
+
+#define OPT_PSKEY(min, max, stores, reset, help) \
+               opt_pskey(argc, argv, (stores), (reset), (help)); \
+               argc -= optind; argv += optind; optind = 0; \
+               OPT_RANGE((min), (max))
+
+static int cmd_psget(int transport, int argc, char *argv[])
+{
+       uint8_t array[128];
+       uint16_t pskey, length, value, stores = CSR_STORES_DEFAULT;
+       uint32_t val32;
+       int i, err, reset = 0;
+
+       memset(array, 0, sizeof(array));
+
+       OPT_PSKEY(1, 1, &stores, &reset, NULL);
+
+       if (strncasecmp(argv[0], "0x", 2)) {
+               pskey = atoi(argv[0]);
+
+               for (i = 0; storage[i].pskey; i++) {
+                       if (strcasecmp(storage[i].str, argv[0]))
+                               continue;
+
+                       pskey = storage[i].pskey;
+                       break;
+               }
+       } else
+               pskey = strtol(argv[0] + 2, NULL, 16);
+
+       memset(array, 0, sizeof(array));
+       array[0] = pskey & 0xff;
+       array[1] = pskey >> 8;
+       array[2] = stores & 0xff;
+       array[3] = stores >> 8;
+
+       err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+       if (err < 0)
+               return err;
+
+       length = array[2] + (array[3] << 8);
+       if (length + 6 > (int) sizeof(array) / 2)
+               return -EIO;
+
+       memset(array, 0, sizeof(array));
+       array[0] = pskey & 0xff;
+       array[1] = pskey >> 8;
+       array[2] = length & 0xff;
+       array[3] = length >> 8;
+       array[4] = stores & 0xff;
+       array[5] = stores >> 8;
+
+       err = transport_read(transport, CSR_VARID_PS, array, (length + 3) * 2);
+       if (err < 0)
+               return err;
+
+       switch (length) {
+       case 1:
+               value = array[6] | (array[7] << 8);
+               printf("%s: 0x%04x (%d)\n", csr_pskeytostr(pskey), value, value);
+               break;
+
+       case 2:
+               val32 = array[8] | (array[9] << 8) | (array[6] << 16) | (array[7] << 24);
+               printf("%s: 0x%08x (%d)\n", csr_pskeytostr(pskey), val32, val32);
+               break;
+
+       default:
+               printf("%s:", csr_pskeytostr(pskey));
+               for (i = 0; i < length; i++)
+                       printf(" 0x%02x%02x", array[(i * 2) + 6], array[(i * 2) + 7]);
+               printf("\n");
+               break;
+       }
+
+       if (reset)
+               transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+       return err;
+}
+
+static int cmd_psset(int transport, int argc, char *argv[])
+{
+       uint8_t array[128];
+       uint16_t pskey, length, value, stores = CSR_STORES_PSRAM;
+       uint32_t val32;
+       int i, err, reset = 0;
+
+       memset(array, 0, sizeof(array));
+
+       OPT_PSKEY(2, 81, &stores, &reset, NULL);
+
+       if (strncasecmp(argv[0], "0x", 2)) {
+               pskey = atoi(argv[0]);
+
+               for (i = 0; storage[i].pskey; i++) {
+                       if (strcasecmp(storage[i].str, argv[0]))
+                               continue;
+
+                       pskey = storage[i].pskey;
+                       break;
+               }
+       } else
+               pskey = strtol(argv[0] + 2, NULL, 16);
+
+       memset(array, 0, sizeof(array));
+       array[0] = pskey & 0xff;
+       array[1] = pskey >> 8;
+       array[2] = stores & 0xff;
+       array[3] = stores >> 8;
+
+       err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+       if (err < 0)
+               return err;
+
+       length = array[2] + (array[3] << 8);
+       if (length + 6 > (int) sizeof(array) / 2)
+               return -EIO;
+
+       memset(array, 0, sizeof(array));
+       array[0] = pskey & 0xff;
+       array[1] = pskey >> 8;
+       array[2] = length & 0xff;
+       array[3] = length >> 8;
+       array[4] = stores & 0xff;
+       array[5] = stores >> 8;
+
+       argc--;
+       argv++;
+
+       switch (length) {
+       case 1:
+               if (argc != 1) {
+                       errno = E2BIG;
+                       return -1;
+               }
+
+               if (!strncasecmp(argv[0], "0x", 2))
+                       value = strtol(argv[0] + 2, NULL, 16);
+               else
+                       value = atoi(argv[0]);
+
+               array[6] = value & 0xff;
+               array[7] = value >> 8;
+               break;
+
+       case 2:
+               if (argc != 1) {
+                       errno = E2BIG;
+                       return -1;
+               }
+
+               if (!strncasecmp(argv[0], "0x", 2))
+                       val32 = strtol(argv[0] + 2, NULL, 16);
+               else
+                       val32 = atoi(argv[0]);
+
+               array[6] = (val32 & 0xff0000) >> 16;
+               array[7] = val32 >> 24;
+               array[8] = val32 & 0xff;
+               array[9] = (val32 & 0xff00) >> 8;
+               break;
+
+       default:
+               if (argc != length * 2) {
+                       errno = EINVAL;
+                       return -1;
+               }
+
+               for (i = 0; i < length * 2; i++)
+                       if (!strncasecmp(argv[0], "0x", 2))
+                               array[i + 6] = strtol(argv[i] + 2, NULL, 16);
+                       else
+                               array[i + 6] = atoi(argv[i]);
+               break;
+       }
+
+       err = transport_write(transport, CSR_VARID_PS, array, (length + 3) * 2);
+       if (err < 0)
+               return err;
+
+       if (reset)
+               transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+       return err;
+}
+
+static int cmd_psclr(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t pskey, stores = CSR_STORES_PSRAM;
+       int i, err, reset = 0;
+
+       OPT_PSKEY(1, 1, &stores, &reset, NULL);
+
+       if (strncasecmp(argv[0], "0x", 2)) {
+               pskey = atoi(argv[0]);
+
+               for (i = 0; storage[i].pskey; i++) {
+                       if (strcasecmp(storage[i].str, argv[0]))
+                               continue;
+
+                       pskey = storage[i].pskey;
+                       break;
+               }
+       } else
+               pskey = strtol(argv[0] + 2, NULL, 16);
+
+       memset(array, 0, sizeof(array));
+       array[0] = pskey & 0xff;
+       array[1] = pskey >> 8;
+       array[2] = stores & 0xff;
+       array[3] = stores >> 8;
+
+       err = transport_write(transport, CSR_VARID_PS_CLR_STORES, array, 8);
+       if (err < 0)
+               return err;
+
+       if (reset)
+               transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+       return err;
+}
+
+static int cmd_pslist(int transport, int argc, char *argv[])
+{
+       uint8_t array[8];
+       uint16_t pskey = 0x0000, length, stores = CSR_STORES_DEFAULT;
+       int err, reset = 0;
+
+       OPT_PSKEY(0, 0, &stores, &reset, NULL);
+
+       while (1) {
+               memset(array, 0, sizeof(array));
+               array[0] = pskey & 0xff;
+               array[1] = pskey >> 8;
+               array[2] = stores & 0xff;
+               array[3] = stores >> 8;
+
+               err = transport_read(transport, CSR_VARID_PS_NEXT, array, 8);
+               if (err < 0)
+                       break;
+
+               pskey = array[4] + (array[5] << 8);
+               if (pskey == 0x0000)
+                       break;
+
+               memset(array, 0, sizeof(array));
+               array[0] = pskey & 0xff;
+               array[1] = pskey >> 8;
+               array[2] = stores & 0xff;
+               array[3] = stores >> 8;
+
+               err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+               if (err < 0)
+                       continue;
+
+               length = array[2] + (array[3] << 8);
+
+               printf("0x%04x - %s (%d bytes)\n", pskey,
+                                       csr_pskeytostr(pskey), length * 2);
+       }
+
+       if (reset)
+               transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+       return 0;
+}
+
+static int cmd_psread(int transport, int argc, char *argv[])
+{
+       uint8_t array[256];
+       uint16_t pskey = 0x0000, length, stores = CSR_STORES_DEFAULT;
+       char *str, val[7];
+       int i, err, reset = 0;
+
+       OPT_PSKEY(0, 0, &stores, &reset, NULL);
+
+       while (1) {
+               memset(array, 0, sizeof(array));
+               array[0] = pskey & 0xff;
+               array[1] = pskey >> 8;
+               array[2] = stores & 0xff;
+               array[3] = stores >> 8;
+
+               err = transport_read(transport, CSR_VARID_PS_NEXT, array, 8);
+               if (err < 0)
+                       break;
+
+               pskey = array[4] + (array[5] << 8);
+               if (pskey == 0x0000)
+                       break;
+
+               memset(array, 0, sizeof(array));
+               array[0] = pskey & 0xff;
+               array[1] = pskey >> 8;
+               array[2] = stores & 0xff;
+               array[3] = stores >> 8;
+
+               err = transport_read(transport, CSR_VARID_PS_SIZE, array, 8);
+               if (err < 0)
+                       continue;
+
+               length = array[2] + (array[3] << 8);
+               if (length + 6 > (int) sizeof(array) / 2)
+                       continue;
+
+               memset(array, 0, sizeof(array));
+               array[0] = pskey & 0xff;
+               array[1] = pskey >> 8;
+               array[2] = length & 0xff;
+               array[3] = length >> 8;
+               array[4] = stores & 0xff;
+               array[5] = stores >> 8;
+
+               err = transport_read(transport, CSR_VARID_PS, array, (length + 3) * 2);
+               if (err < 0)
+                       continue;
+
+               str = csr_pskeytoval(pskey);
+               if (!strcasecmp(str, "UNKNOWN")) {
+                       sprintf(val, "0x%04x", pskey);
+                       str = NULL;
+               }
+
+               printf("// %s%s\n&%04x =", str ? "PSKEY_" : "",
+                                               str ? str : val, pskey);
+               for (i = 0; i < length; i++)
+                       printf(" %02x%02x", array[(i * 2) + 7], array[(i * 2) + 6]);
+               printf("\n");
+       }
+
+       if (reset)
+               transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+       return 0;
+}
+
+static int cmd_psload(int transport, int argc, char *argv[])
+{
+       uint8_t array[256];
+       uint16_t pskey, length, size, stores = CSR_STORES_PSRAM;
+       char *str, val[7];
+       int err, reset = 0;
+
+       OPT_PSKEY(1, 1, &stores, &reset, NULL);
+
+       psr_read(argv[0]);
+
+       memset(array, 0, sizeof(array));
+       size = sizeof(array) - 6;
+
+       while (psr_get(&pskey, array + 6, &size) == 0) {
+               str = csr_pskeytoval(pskey);
+               if (!strcasecmp(str, "UNKNOWN")) {
+                       sprintf(val, "0x%04x", pskey);
+                       str = NULL;
+               }
+
+               printf("Loading %s%s ... ", str ? "PSKEY_" : "",
+                                                       str ? str : val);
+               fflush(stdout);
+
+               length = size / 2;
+
+               array[0] = pskey & 0xff;
+               array[1] = pskey >> 8;
+               array[2] = length & 0xff;
+               array[3] = length >> 8;
+               array[4] = stores & 0xff;
+               array[5] = stores >> 8;
+
+               err = transport_write(transport, CSR_VARID_PS, array, size + 6);
+
+               printf("%s\n", err < 0 ? "failed" : "done");
+
+               memset(array, 0, sizeof(array));
+               size = sizeof(array) - 6;
+       }
+
+       if (reset)
+               transport_write(transport, CSR_VARID_WARM_RESET, NULL, 0);
+
+       return 0;
+}
+
+static int cmd_pscheck(int transport, int argc, char *argv[])
+{
+       uint8_t array[256];
+       uint16_t pskey, size;
+       int i;
+
+       OPT_HELP(1, NULL);
+
+       psr_read(argv[0]);
+
+       while (psr_get(&pskey, array, &size) == 0) {
+               printf("0x%04x =", pskey);
+               for (i = 0; i < size; i++)
+                       printf(" 0x%02x", array[i]);
+               printf("\n");
+       }
+
+       return 0;
+}
+
+static struct {
+       char *str;
+       int (*func)(int transport, int argc, char *argv[]);
+       char *arg;
+       char *doc;
+} commands[] = {
+       { "builddef",  cmd_builddef,  "",                    "Get build definitions"          },
+       { "keylen",    cmd_keylen,    "<handle>",            "Get current crypt key length"   },
+       { "clock",     cmd_clock,     "",                    "Get local Bluetooth clock"      },
+       { "rand",      cmd_rand,      "",                    "Get random number"              },
+       { "chiprev",   cmd_chiprev,   "",                    "Get chip revision"              },
+       { "buildname", cmd_buildname, "",                    "Get the full build name"        },
+       { "panicarg",  cmd_panicarg,  "",                    "Get panic code argument"        },
+       { "faultarg",  cmd_faultarg,  "",                    "Get fault code argument"        },
+       { "coldreset", cmd_coldreset, "",                    "Perform cold reset"             },
+       { "warmreset", cmd_warmreset, "",                    "Perform warm reset"             },
+       { "disabletx", cmd_disabletx, "",                    "Disable TX on the device"       },
+       { "enabletx",  cmd_enabletx,  "",                    "Enable TX on the device"        },
+       { "singlechan",cmd_singlechan,"<channel>",           "Lock radio on specific channel" },
+       { "hoppingon", cmd_hoppingon, "",                    "Revert to channel hopping"      },
+       { "rttxdata1", cmd_rttxdata1, "<freq> <level>",      "TXData1 radio test"             },
+       { "radiotest", cmd_radiotest, "<freq> <level> <id>", "Run radio tests"                },
+       { "memtypes",  cmd_memtypes,  NULL,                  "Get memory types"               },
+       { "psget",     cmd_psget,     "<key>",               "Get value for PS key"           },
+       { "psset",     cmd_psset,     "<key> <value>",       "Set value for PS key"           },
+       { "psclr",     cmd_psclr,     "<key>",               "Clear value for PS key"         },
+       { "pslist",    cmd_pslist,    NULL,                  "List all PS keys"               },
+       { "psread",    cmd_psread,    NULL,                  "Read all PS keys"               },
+       { "psload",    cmd_psload,    "<file>",              "Load all PS keys from PSR file" },
+       { "pscheck",   cmd_pscheck,   "<file>",              "Check PSR file"                 },
+       { NULL }
+};
+
+static void usage(void)
+{
+       int i, pos = 0;
+
+       printf("bccmd - Utility for the CSR BCCMD interface\n\n");
+       printf("Usage:\n"
+               "\tbccmd [options] <command>\n\n");
+
+       printf("Options:\n"
+               "\t-t <transport>     Select the transport\n"
+               "\t-d <device>        Select the device\n"
+               "\t-b <bcsp rate>     Select the bcsp transfer rate\n"
+               "\t-h, --help         Display help\n"
+               "\n");
+
+       printf("Transports:\n"
+               "\tHCI USB BCSP H4 3WIRE\n\n");
+
+       printf("Commands:\n");
+       for (i = 0; commands[i].str; i++)
+               printf("\t%-10s %-20s\t%s\n", commands[i].str,
+               commands[i].arg ? commands[i].arg : " ",
+               commands[i].doc);
+       printf("\n");
+
+       printf("Keys:\n\t");
+       for (i = 0; storage[i].pskey; i++) {
+               printf("%s ", storage[i].str);
+               pos += strlen(storage[i].str) + 1;
+               if (pos > 60) {
+                       printf("\n\t");
+                       pos = 0;
+               }
+       }
+       printf("\n");
+}
+
+static struct option main_options[] = {
+       { "transport",  1, 0, 't' },
+       { "device",     1, 0, 'd' },
+       { "bcsprate", 1, 0, 'b'},
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       char *device = NULL;
+       int i, err, opt, transport = CSR_TRANSPORT_HCI;
+       speed_t bcsp_rate = B38400;
+
+       while ((opt=getopt_long(argc, argv, "+t:d:i:b:h", main_options, NULL)) != EOF) {
+               switch (opt) {
+               case 't':
+                       if (!strcasecmp(optarg, "hci"))
+                               transport = CSR_TRANSPORT_HCI;
+                       else if (!strcasecmp(optarg, "usb"))
+                               transport = CSR_TRANSPORT_USB;
+                       else if (!strcasecmp(optarg, "bcsp"))
+                               transport = CSR_TRANSPORT_BCSP;
+                       else if (!strcasecmp(optarg, "h4"))
+                               transport = CSR_TRANSPORT_H4;
+                       else if (!strcasecmp(optarg, "h5"))
+                               transport = CSR_TRANSPORT_3WIRE;
+                       else if (!strcasecmp(optarg, "3wire"))
+                               transport = CSR_TRANSPORT_3WIRE;
+                       else if (!strcasecmp(optarg, "twutl"))
+                               transport = CSR_TRANSPORT_3WIRE;
+                       else
+                               transport = CSR_TRANSPORT_UNKNOWN;
+                       break;
+
+               case 'd':
+               case 'i':
+                       device = strdup(optarg);
+                       break;
+               case 'b':
+                       switch (atoi(optarg)) {
+                       case 9600: bcsp_rate = B9600; break;
+                       case 19200: bcsp_rate = B19200; break;
+                       case 38400: bcsp_rate = B38400; break;
+                       case 57600: bcsp_rate = B57600; break;
+                       case 115200: bcsp_rate = B115200; break;
+                       case 230400: bcsp_rate = B230400; break;
+                       case 460800: bcsp_rate = B460800; break;
+                       case 500000: bcsp_rate = B500000; break;
+                       case 576000: bcsp_rate = B576000; break;
+                       case 921600: bcsp_rate = B921600; break;
+                       case 1000000: bcsp_rate = B1000000; break;
+                       case 1152000: bcsp_rate = B1152000; break;
+                       case 1500000: bcsp_rate = B1500000; break;
+                       case 2000000: bcsp_rate = B2000000; break;
+#ifdef B2500000
+                       case 2500000: bcsp_rate = B2500000; break;
+#endif
+#ifdef B3000000
+                       case 3000000: bcsp_rate = B3000000; break;
+#endif
+#ifdef B3500000
+                       case 3500000: bcsp_rate = B3500000; break;
+#endif
+#ifdef B4000000
+                       case 4000000: bcsp_rate = B4000000; break;
+#endif
+                       default:
+                               printf("Unknown BCSP baud rate specified, defaulting to 38400bps\n");
+                               bcsp_rate = B38400;
+                       }
+                       break;
+               case 'h':
+               default:
+                       usage();
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       if (transport_open(transport, device, bcsp_rate) < 0)
+               exit(1);
+
+       free(device);
+
+       for (i = 0; commands[i].str; i++) {
+               if (strcasecmp(commands[i].str, argv[0]))
+                       continue;
+
+               err = commands[i].func(transport, argc, argv);
+
+               transport_close(transport);
+
+               if (err < 0) {
+                       fprintf(stderr, "Can't execute command: %s (%d)\n",
+                                                       strerror(errno), errno);
+                       exit(1);
+               }
+
+               exit(0);
+       }
+
+       fprintf(stderr, "Unsupported command\n");
+
+       transport_close(transport);
+
+       exit(1);
+}
diff --git a/tools/ciptool.1 b/tools/ciptool.1
new file mode 100644 (file)
index 0000000..65d903d
--- /dev/null
@@ -0,0 +1,68 @@
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH CIPTOOL 1 "JUNE 6, 2003" "" ""
+
+.SH NAME
+ciptool \- Bluetooth Common ISDN Access Profile (CIP)
+.SH SYNOPSIS
+.BR "ciptool
+[
+.I options
+] <
+.I command
+>
+.SH DESCRIPTION
+.B ciptool
+is used to set up, maintain, and inspect the CIP configuration
+of the Bluetooth subsystem in the Linux kernel.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -i " <hciX> | <bdaddr>"
+The command is applied to device
+.I
+hciX
+, which must be the name or the address of an installed Bluetooth
+device. If not specified, the command will be use the first
+available Bluetooth device.
+.SH COMMANDS
+.TP
+.BI show
+Display information about the connected devices.
+.TP
+.BI search
+Search for Bluetooth devices and connect to first one that
+offers CIP support.
+.TP
+.BI connect " <bdaddr> [psm]"
+Connect the local device to the remote Bluetooth device on the
+specified PSM number. If no PSM is specified, it will use the
+SDP to retrieve it from the remote device.
+.TP
+.BI release " [bdaddr]"
+Release a connection to the specific device. If no address is
+given and only one device is connected this will be released.
+.TP
+.BI loopback " <bdaddr> [psm]"
+Create a connection to the remote device for Bluetooth testing.
+This command will not provide a CAPI controller, because it is
+only for testing the CAPI Message Transport Protocol.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/ciptool.c b/tools/ciptool.c
new file mode 100644 (file)
index 0000000..edce9da
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#include <bluetooth/cmtp.h>
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+       return;
+}
+
+static void sig_term(int sig)
+{
+       __io_canceled = 1;
+}
+
+static char *cmtp_state[] = {
+       "unknown",
+       "connected",
+       "open",
+       "bound",
+       "listening",
+       "connecting",
+       "connecting",
+       "config",
+       "disconnecting",
+       "closed"
+};
+
+static char *cmtp_flagstostr(uint32_t flags)
+{
+       static char str[100] = "";
+
+       strcat(str, "[");
+
+       if (flags & (1 << CMTP_LOOPBACK))
+               strcat(str, "loopback");
+
+       strcat(str, "]");
+
+       return str;
+}
+
+static int get_psm(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm)
+{
+       sdp_session_t *s;
+       sdp_list_t *srch, *attrs, *rsp;
+       uuid_t svclass;
+       uint16_t attr;
+       int err;
+
+       if (!(s = sdp_connect(src, dst, 0)))
+               return -1;
+
+       sdp_uuid16_create(&svclass, CIP_SVCLASS_ID);
+       srch = sdp_list_append(NULL, &svclass);
+
+       attr = SDP_ATTR_PROTO_DESC_LIST;
+       attrs = sdp_list_append(NULL, &attr);
+
+       err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp);
+
+       sdp_close(s);
+
+       if (err)
+               return 0;
+
+       for (; rsp; rsp = rsp->next) {
+               sdp_record_t *rec = (sdp_record_t *) rsp->data;
+               sdp_list_t *protos;
+
+               if (!sdp_get_access_protos(rec, &protos)) {
+                       unsigned short p = sdp_get_proto_port(protos, L2CAP_UUID);
+                       if (p > 0) {
+                               *psm = p;
+                               return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int do_connect(int ctl, int dev_id, bdaddr_t *src, bdaddr_t *dst, unsigned short psm, uint32_t flags)
+{
+       struct cmtp_connadd_req req;
+       struct hci_dev_info di;
+       struct sockaddr_l2 addr;
+       struct l2cap_options opts;
+       socklen_t size;
+       int sk;
+
+       hci_devinfo(dev_id, &di);
+       if (!(di.link_policy & HCI_LP_RSWITCH)) {
+               printf("Local device is not accepting role switch\n");
+       }
+
+       if ((sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) {
+               perror("Can't create L2CAP socket");
+               exit(1);
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, src);
+
+       if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+               perror("Can't bind L2CAP socket");
+               close(sk);
+               exit(1);
+       }
+
+       memset(&opts, 0, sizeof(opts));
+       size = sizeof(opts);
+
+       if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &size) < 0) {
+               perror("Can't get L2CAP options");
+               close(sk);
+               exit(1);
+       }
+
+       opts.imtu = CMTP_DEFAULT_MTU;
+       opts.omtu = CMTP_DEFAULT_MTU;
+       opts.flush_to = 0xffff;
+
+       if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+               perror("Can't set L2CAP options");
+               close(sk);
+               exit(1);
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, dst);
+       addr.l2_psm = htobs(psm);
+
+       if (connect(sk, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+               perror("Can't connect L2CAP socket");
+               close(sk);
+               exit(1);
+       }
+
+       req.sock = sk;
+       req.flags = flags;
+
+       if (ioctl(ctl, CMTPCONNADD, &req) < 0) {
+               perror("Can't create connection");
+               exit(1);
+       }
+
+       return sk;
+}
+
+static void cmd_show(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       struct cmtp_connlist_req req;
+       struct cmtp_conninfo ci[16];
+       char addr[18];
+       unsigned int i;
+
+       req.cnum = 16;
+       req.ci   = ci;
+
+       if (ioctl(ctl, CMTPGETCONNLIST, &req) < 0) {
+               perror("Can't get connection list");
+               exit(1);
+       }
+
+       for (i = 0; i < req.cnum; i++) {
+               ba2str(&ci[i].bdaddr, addr);
+               printf("%d %s %s %s\n", ci[i].num, addr,
+                       cmtp_state[ci[i].state],
+                       ci[i].flags ? cmtp_flagstostr(ci[i].flags) : "");
+       }
+}
+
+static void cmd_search(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       inquiry_info *info = NULL;
+       bdaddr_t src, dst;
+       unsigned short psm;
+       int i, dev_id, num_rsp, length, flags;
+       char addr[18];
+       uint8_t class[3];
+
+       ba2str(bdaddr, addr);
+       dev_id = hci_devid(addr);
+       if (dev_id < 0) {
+               dev_id = hci_get_route(NULL);
+               hci_devba(dev_id, &src);
+       } else
+               bacpy(&src, bdaddr);
+
+       length  = 8;    /* ~10 seconds */
+       num_rsp = 0;
+       flags   = 0;
+
+       printf("Searching ...\n");
+
+       num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags);
+
+       for (i = 0; i < num_rsp; i++) {
+               memcpy(class, (info+i)->dev_class, 3);
+               if ((class[1] == 2) && ((class[0] / 4) == 5)) {
+                       bacpy(&dst, &(info+i)->bdaddr);
+                       ba2str(&dst, addr);
+
+                       printf("\tChecking service for %s\n", addr);
+                       if (!get_psm(&src, &dst, &psm))
+                               continue;
+
+                       bt_free(info);
+
+                       printf("\tConnecting to device %s\n", addr);
+                       do_connect(ctl, dev_id, &src, &dst, psm, 0);
+                       return;
+               }
+       }
+
+       bt_free(info);
+       fprintf(stderr, "\tNo devices in range or visible\n");
+       exit(1);
+}
+
+static void cmd_create(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       bdaddr_t src, dst;
+       unsigned short psm;
+       int dev_id;
+       char addr[18];
+
+       if (argc < 2)
+               return;
+
+       str2ba(argv[1], &dst);
+
+       ba2str(bdaddr, addr);
+       dev_id = hci_devid(addr);
+       if (dev_id < 0) {
+               dev_id = hci_get_route(&dst);
+               hci_devba(dev_id, &src);
+       } else
+               bacpy(&src, bdaddr);
+
+       if (argc < 3) {
+               if (!get_psm(&src, &dst, &psm))
+                       psm = 4099;
+       } else
+               psm = atoi(argv[2]);
+
+       do_connect(ctl, dev_id, &src, &dst, psm, 0);
+}
+
+static void cmd_release(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       struct cmtp_conndel_req req;
+       struct cmtp_connlist_req cl;
+       struct cmtp_conninfo ci[16];
+
+       if (argc < 2) {
+               cl.cnum = 16;
+               cl.ci   = ci;
+
+               if (ioctl(ctl, CMTPGETCONNLIST, &cl) < 0) {
+                       perror("Can't get connection list");
+                       exit(1);
+               }
+
+               if (cl.cnum == 0)
+                       return;
+
+               if (cl.cnum != 1) {
+                       fprintf(stderr, "You have to specifiy the device address.\n");
+                       exit(1);
+               }
+
+               bacpy(&req.bdaddr, &ci[0].bdaddr);
+       } else
+               str2ba(argv[1], &req.bdaddr);
+
+       if (ioctl(ctl, CMTPCONNDEL, &req) < 0) {
+               perror("Can't release connection");
+               exit(1);
+       }
+}
+
+static void cmd_loopback(int ctl, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       struct cmtp_conndel_req req;
+       struct sigaction sa;
+       struct pollfd p;
+       sigset_t sigs;
+       bdaddr_t src, dst;
+       unsigned short psm;
+       int dev_id, sk;
+       char addr[18];
+
+       if (argc < 2)
+               return;
+
+       str2ba(argv[1], &dst);
+
+       ba2str(bdaddr, addr);
+       dev_id = hci_devid(addr);
+       if (dev_id < 0) {
+               dev_id = hci_get_route(&dst);
+               hci_devba(dev_id, &src);
+       } else
+               bacpy(&src, bdaddr);
+
+       ba2str(&dst, addr);
+       printf("Connecting to %s in loopback mode\n", addr);
+
+       if (argc < 3) {
+               if (!get_psm(&src, &dst, &psm))
+                       psm = 4099;
+       } else
+               psm = atoi(argv[2]);
+
+       sk = do_connect(ctl, dev_id, &src, &dst, psm, (1 << CMTP_LOOPBACK));
+
+       printf("Press CTRL-C for hangup\n");
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       sigfillset(&sigs);
+       sigdelset(&sigs, SIGCHLD);
+       sigdelset(&sigs, SIGPIPE);
+       sigdelset(&sigs, SIGTERM);
+       sigdelset(&sigs, SIGINT);
+       sigdelset(&sigs, SIGHUP);
+
+       p.fd = sk;
+       p.events = POLLERR | POLLHUP;
+
+       while (!__io_canceled) {
+               p.revents = 0;
+               if (ppoll(&p, 1, NULL, &sigs) > 0)
+                       break;
+       }
+
+       bacpy(&req.bdaddr, &dst);
+       ioctl(ctl, CMTPCONNDEL, &req);
+}
+
+static struct {
+       char *cmd;
+       char *alt;
+       void (*func)(int ctl, bdaddr_t *bdaddr, int argc, char **argv);
+       char *opt;
+       char *doc;
+} command[] = {
+       { "show",     "list",       cmd_show,     0,          "Show remote connections"      },
+       { "search",   "scan",       cmd_search,   0,          "Search for a remote device"   },
+       { "connect",  "create",     cmd_create,   "<bdaddr>", "Connect a remote device"      },
+       { "release",  "disconnect", cmd_release,  "[bdaddr]", "Disconnect the remote device" },
+       { "loopback", "test",       cmd_loopback, "<bdaddr>", "Loopback test of a device"    },
+       { NULL, NULL, NULL, 0, 0 }
+};
+
+static void usage(void)
+{
+       int i;
+
+       printf("ciptool - Bluetooth Common ISDN Access Profile (CIP)\n\n");
+
+       printf("Usage:\n"
+               "\tciptool [options] [command]\n"
+               "\n");
+
+       printf("Options:\n"
+               "\t-i [hciX|bdaddr]   Local HCI device or BD Address\n"
+               "\t-h, --help         Display help\n"
+               "\n");
+
+       printf("Commands:\n");
+       for (i = 0; command[i].cmd; i++)
+               printf("\t%-8s %-10s\t%s\n", command[i].cmd,
+               command[i].opt ? command[i].opt : " ",
+               command[i].doc);
+       printf("\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "device",     1, 0, 'i' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       bdaddr_t bdaddr;
+       int i, opt, ctl;
+
+       bacpy(&bdaddr, BDADDR_ANY);
+
+       while ((opt = getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+               switch(opt) {
+               case 'i':
+                       if (!strncmp(optarg, "hci", 3))
+                               hci_devba(atoi(optarg + 3), &bdaddr);
+                       else
+                               str2ba(optarg, &bdaddr);
+                       break;
+               case 'h':
+                       usage();
+                       exit(0);
+               default:
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               return 0;
+       }
+
+       if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_CMTP)) < 0 ) {
+               perror("Can't open CMTP control socket");
+               exit(1);
+       }
+
+       for (i = 0; command[i].cmd; i++) {
+               if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4))
+                       continue;
+               command[i].func(ctl, &bdaddr, argc, argv);
+               close(ctl);
+               exit(0);
+       }
+
+       usage();
+
+       close(ctl);
+
+       return 0;
+}
diff --git a/tools/csr.c b/tools/csr.c
new file mode 100644 (file)
index 0000000..b4ea1fb
--- /dev/null
@@ -0,0 +1,2853 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "csr.h"
+
+struct psr_data {
+       uint16_t pskey;
+       uint8_t *value;
+       uint8_t size;
+       struct psr_data *next;
+};
+
+static struct psr_data *head = NULL, *tail = NULL;
+
+static struct {
+       uint16_t id;
+       char *str;
+} csr_map[] = {
+       {   66, "HCI 9.8"       },
+       {   97, "HCI 10.3"      },
+       {  101, "HCI 10.5"      },
+       {  111, "HCI 11.0"      },
+       {  112, "HCI 11.1"      },
+       {  114, "HCI 11.2"      },
+       {  115, "HCI 11.3"      },
+       {  117, "HCI 12.0"      },
+       {  119, "HCI 12.1"      },
+       {  133, "HCI 12.2"      },
+       {  134, "HCI 12.3"      },
+       {  162, "HCI 12.4"      },
+       {  165, "HCI 12.5"      },
+       {  169, "HCI 12.6"      },
+       {  188, "HCI 12.7"      },
+       {  218, "HCI 12.8"      },
+       {  283, "HCI 12.9"      },
+       {  203, "HCI 13.2"      },
+       {  204, "HCI 13.2"      },
+       {  210, "HCI 13.3"      },
+       {  211, "HCI 13.3"      },
+       {  213, "HCI 13.4"      },
+       {  214, "HCI 13.4"      },
+       {  225, "HCI 13.5"      },
+       {  226, "HCI 13.5"      },
+       {  237, "HCI 13.6"      },
+       {  238, "HCI 13.6"      },
+       {  242, "HCI 14.0"      },
+       {  243, "HCI 14.0"      },
+       {  244, "HCI 14.0"      },
+       {  245, "HCI 14.0"      },
+       {  254, "HCI 13.7"      },
+       {  255, "HCI 13.7"      },
+       {  264, "HCI 14.1"      },
+       {  265, "HCI 14.1"      },
+       {  267, "HCI 14.2"      },
+       {  268, "HCI 14.2"      },
+       {  272, "HCI 14.3"      },
+       {  273, "HCI 14.3"      },
+       {  274, "HCI 13.8"      },
+       {  275, "HCI 13.8"      },
+       {  286, "HCI 13.9"      },
+       {  287, "HCI 13.9"      },
+       {  309, "HCI 13.10"     },
+       {  310, "HCI 13.10"     },
+       {  313, "HCI 14.4"      },
+       {  314, "HCI 14.4"      },
+       {  323, "HCI 14.5"      },
+       {  324, "HCI 14.5"      },
+       {  336, "HCI 14.6"      },
+       {  337, "HCI 14.6"      },
+       {  351, "HCI 13.11"     },
+       {  352, "HCI 13.11"     },
+       {  362, "HCI 15.0"      },
+       {  363, "HCI 15.0"      },
+       {  364, "HCI 15.0"      },
+       {  365, "HCI 15.0"      },
+       {  373, "HCI 14.7"      },
+       {  374, "HCI 14.7"      },
+       {  379, "HCI 15.1"      },
+       {  380, "HCI 15.1"      },
+       {  381, "HCI 15.1"      },
+       {  382, "HCI 15.1"      },
+       {  392, "HCI 15.2"      },
+       {  393, "HCI 15.2"      },
+       {  394, "HCI 15.2"      },
+       {  395, "HCI 15.2"      },
+       {  436, "HCI 16.0"      },
+       {  437, "HCI 16.0"      },
+       {  438, "HCI 16.0"      },
+       {  439, "HCI 16.0"      },
+       {  443, "HCI 15.3"      },
+       {  444, "HCI 15.3"      },
+       {  465, "HCI 16.1"      },
+       {  466, "HCI 16.1"      },
+       {  467, "HCI 16.1"      },
+       {  468, "HCI 16.1"      },
+       {  487, "HCI 14.8"      },
+       {  488, "HCI 14.8"      },
+       {  492, "HCI 16.2"      },
+       {  493, "HCI 16.2"      },
+       {  495, "HCI 16.2"      },
+       {  496, "HCI 16.2"      },
+       {  502, "HCI 16.1.1"    },
+       {  503, "HCI 16.1.1"    },
+       {  504, "HCI 16.1.1"    },
+       {  505, "HCI 16.1.1"    },
+       {  506, "HCI 16.1.2"    },
+       {  507, "HCI 16.1.2"    },
+       {  508, "HCI 16.1.2"    },
+       {  509, "HCI 16.1.2"    },
+       {  516, "HCI 16.3"      },
+       {  517, "HCI 16.3"      },
+       {  518, "HCI 16.3"      },
+       {  519, "HCI 16.3"      },
+       {  523, "HCI 16.4"      },
+       {  524, "HCI 16.4"      },
+       {  525, "HCI 16.4"      },
+       {  526, "HCI 16.4"      },
+       {  553, "HCI 15.3"      },
+       {  554, "HCI 15.3"      },
+       {  562, "HCI 16.5"      },
+       {  563, "HCI 16.5"      },
+       {  564, "HCI 16.5"      },
+       {  565, "HCI 16.5"      },
+       {  593, "HCI 17.0"      },
+       {  594, "HCI 17.0"      },
+       {  595, "HCI 17.0"      },
+       {  599, "HCI 17.0"      },
+       {  600, "HCI 17.0"      },
+       {  608, "HCI 13.10.1"   },
+       {  609, "HCI 13.10.1"   },
+       {  613, "HCI 17.1"      },
+       {  614, "HCI 17.1"      },
+       {  615, "HCI 17.1"      },
+       {  616, "HCI 17.1"      },
+       {  618, "HCI 17.1"      },
+       {  624, "HCI 17.2"      },
+       {  625, "HCI 17.2"      },
+       {  626, "HCI 17.2"      },
+       {  627, "HCI 17.2"      },
+       {  637, "HCI 16.6"      },
+       {  638, "HCI 16.6"      },
+       {  639, "HCI 16.6"      },
+       {  640, "HCI 16.6"      },
+       {  642, "HCI 13.10.2"   },
+       {  643, "HCI 13.10.2"   },
+       {  644, "HCI 13.10.3"   },
+       {  645, "HCI 13.10.3"   },
+       {  668, "HCI 13.10.4"   },
+       {  669, "HCI 13.10.4"   },
+       {  681, "HCI 16.7"      },
+       {  682, "HCI 16.7"      },
+       {  683, "HCI 16.7"      },
+       {  684, "HCI 16.7"      },
+       {  704, "HCI 16.8"      },
+       {  718, "HCI 16.4.1"    },
+       {  719, "HCI 16.4.1"    },
+       {  720, "HCI 16.4.1"    },
+       {  721, "HCI 16.4.1"    },
+       {  722, "HCI 16.7.1"    },
+       {  723, "HCI 16.7.1"    },
+       {  724, "HCI 16.7.1"    },
+       {  725, "HCI 16.7.1"    },
+       {  731, "HCI 16.7.2"    },
+       {  732, "HCI 16.7.2"    },
+       {  733, "HCI 16.7.2"    },
+       {  734, "HCI 16.7.2"    },
+       {  735, "HCI 16.4.2"    },
+       {  736, "HCI 16.4.2"    },
+       {  737, "HCI 16.4.2"    },
+       {  738, "HCI 16.4.2"    },
+       {  750, "HCI 16.7.3"    },
+       {  751, "HCI 16.7.3"    },
+       {  752, "HCI 16.7.3"    },
+       {  753, "HCI 16.7.3"    },
+       {  760, "HCI 16.7.4"    },
+       {  761, "HCI 16.7.4"    },
+       {  762, "HCI 16.7.4"    },
+       {  763, "HCI 16.7.4"    },
+       {  770, "HCI 16.9"      },
+       {  771, "HCI 16.9"      },
+       {  772, "HCI 16.9"      },
+       {  773, "HCI 16.9"      },
+       {  774, "HCI 17.3"      },
+       {  775, "HCI 17.3"      },
+       {  776, "HCI 17.3"      },
+       {  777, "HCI 17.3"      },
+       {  781, "HCI 16.7.5"    },
+       {  786, "HCI 16.10"     },
+       {  787, "HCI 16.10"     },
+       {  788, "HCI 16.10"     },
+       {  789, "HCI 16.10"     },
+       {  791, "HCI 16.4.3"    },
+       {  792, "HCI 16.4.3"    },
+       {  793, "HCI 16.4.3"    },
+       {  794, "HCI 16.4.3"    },
+       {  798, "HCI 16.11"     },
+       {  799, "HCI 16.11"     },
+       {  800, "HCI 16.11"     },
+       {  801, "HCI 16.11"     },
+       {  806, "HCI 16.7.5"    },
+       {  807, "HCI 16.12"     },
+       {  808, "HCI 16.12"     },
+       {  809, "HCI 16.12"     },
+       {  810, "HCI 16.12"     },
+       {  817, "HCI 16.13"     },
+       {  818, "HCI 16.13"     },
+       {  819, "HCI 16.13"     },
+       {  820, "HCI 16.13"     },
+       {  823, "HCI 13.10.5"   },
+       {  824, "HCI 13.10.5"   },
+       {  826, "HCI 16.14"     },
+       {  827, "HCI 16.14"     },
+       {  828, "HCI 16.14"     },
+       {  829, "HCI 16.14"     },
+       {  843, "HCI 17.3.1"    },
+       {  856, "HCI 17.3.2"    },
+       {  857, "HCI 17.3.2"    },
+       {  858, "HCI 17.3.2"    },
+       { 1120, "HCI 17.11"     },
+       { 1168, "HCI 18.1"      },
+       { 1169, "HCI 18.1"      },
+       { 1241, "HCI 18.x"      },
+       { 1298, "HCI 18.2"      },
+       { 1354, "HCI 18.2"      },
+       { 1392, "HCI 18.2"      },
+       { 1393, "HCI 18.2"      },
+       { 1501, "HCI 18.2"      },
+       { 1503, "HCI 18.2"      },
+       { 1504, "HCI 18.2"      },
+       { 1505, "HCI 18.2"      },
+       { 1506, "HCI 18.2"      },
+       { 1520, "HCI 18.2"      },
+       { 1586, "HCI 18.2"      },
+       { 1591, "HCI 18.2"      },
+       { 1592, "HCI 18.2"      },
+       { 1593, "HCI 18.2.1"    },
+       { 1733, "HCI 18.3"      },
+       { 1734, "HCI 18.3"      },
+       { 1735, "HCI 18.3"      },
+       { 1737, "HCI 18.3"      },
+       { 1915, "HCI 19.2"      },
+       { 1916, "HCI 19.2"      },
+       { 1958, "HCI 19.2"      },
+       { 1981, "Unified 20a"   },
+       { 1982, "Unified 20a"   },
+       { 1989, "HCI 18.4"      },
+       { 2062, "Unified 20a1"  },
+       { 2063, "Unified 20a1"  },
+       { 2067, "Unified 18f"   },
+       { 2068, "Unified 18f"   },
+       { 2243, "Unified 18e"   },
+       { 2244, "Unified 18e"   },
+       { 2258, "Unified 20d"   },
+       { 2259, "Unified 20d"   },
+       { 2361, "Unified 20e"   },
+       { 2362, "Unified 20e"   },
+       { 2386, "Unified 21a"   },
+       { 2387, "Unified 21a"   },
+       { 2423, "Unified 21a"   },
+       { 2424, "Unified 21a"   },
+       { 2623, "Unified 21c"   },
+       { 2624, "Unified 21c"   },
+       { 2625, "Unified 21c"   },
+       { 2626, "Unified 21c"   },
+       { 2627, "Unified 21c"   },
+       { 2628, "Unified 21c"   },
+       { 2629, "Unified 21c"   },
+       { 2630, "Unified 21c"   },
+       { 2631, "Unified 21c"   },
+       { 2632, "Unified 21c"   },
+       { 2633, "Unified 21c"   },
+       { 2634, "Unified 21c"   },
+       { 2635, "Unified 21c"   },
+       { 2636, "Unified 21c"   },
+       { 2649, "Unified 21c"   },
+       { 2650, "Unified 21c"   },
+       { 2651, "Unified 21c"   },
+       { 2652, "Unified 21c"   },
+       { 2653, "Unified 21c"   },
+       { 2654, "Unified 21c"   },
+       { 2655, "Unified 21c"   },
+       { 2656, "Unified 21c"   },
+       { 2658, "Unified 21c"   },
+       { 3057, "Unified 21d"   },
+       { 3058, "Unified 21d"   },
+       { 3059, "Unified 21d"   },
+       { 3060, "Unified 21d"   },
+       { 3062, "Unified 21d"   },
+       { 3063, "Unified 21d"   },
+       { 3064, "Unified 21d"   },
+       { 3164, "Unified 21e"   },
+       { 3413, "Unified 21f"   },
+       { 3414, "Unified 21f"   },
+       { 3415, "Unified 21f"   },
+       { 3424, "Unified 21f"   },
+       { 3454, "Unified 21f"   },
+       { 3684, "Unified 21f"   },
+       { 3764, "Unified 21f"   },
+       { 4276, "Unified 22b"   },
+       { 4277, "Unified 22b"   },
+       { 4279, "Unified 22b"   },
+       { 4281, "Unified 22b"   },
+       { 4282, "Unified 22b"   },
+       { 4283, "Unified 22b"   },
+       { 4284, "Unified 22b"   },
+       { 4285, "Unified 22b"   },
+       { 4289, "Unified 22b"   },
+       { 4290, "Unified 22b"   },
+       { 4291, "Unified 22b"   },
+       { 4292, "Unified 22b"   },
+       { 4293, "Unified 22b"   },
+       { 4294, "Unified 22b"   },
+       { 4295, "Unified 22b"   },
+       { 4363, "Unified 22c"   },
+       { 4373, "Unified 22c"   },
+       { 4374, "Unified 22c"   },
+       { 4532, "Unified 22d"   },
+       { 4533, "Unified 22d"   },
+       { 4698, "Unified 23c"   },
+       { 4839, "Unified 23c"   },
+       { 4841, "Unified 23c"   },
+       { 4866, "Unified 23c"   },
+       { 4867, "Unified 23c"   },
+       { 4868, "Unified 23c"   },
+       { 4869, "Unified 23c"   },
+       { 4870, "Unified 23c"   },
+       { 4871, "Unified 23c"   },
+       { 4872, "Unified 23c"   },
+       { 4874, "Unified 23c"   },
+       { 4875, "Unified 23c"   },
+       { 4876, "Unified 23c"   },
+       { 4877, "Unified 23c"   },
+       { 2526, "Marcel 1 (2005-09-26)" },
+       { 2543, "Marcel 2 (2005-09-28)" },
+       { 2622, "Marcel 3 (2005-10-27)" },
+       { 3326, "Marcel 4 (2006-06-16)" },
+       { 3612, "Marcel 5 (2006-10-24)" },
+       { 4509, "Marcel 6 (2007-06-11)" },
+       { 5417, "Marcel 7 (2008-08-26)" },
+       {  195, "Sniff 1 (2001-11-27)"  },
+       {  220, "Sniff 2 (2002-01-03)"  },
+       {  269, "Sniff 3 (2002-02-22)"  },
+       {  270, "Sniff 4 (2002-02-26)"  },
+       {  284, "Sniff 5 (2002-03-12)"  },
+       {  292, "Sniff 6 (2002-03-20)"  },
+       {  305, "Sniff 7 (2002-04-12)"  },
+       {  306, "Sniff 8 (2002-04-12)"  },
+       {  343, "Sniff 9 (2002-05-02)"  },
+       {  346, "Sniff 10 (2002-05-03)" },
+       {  355, "Sniff 11 (2002-05-16)" },
+       {  256, "Sniff 11 (2002-05-16)" },
+       {  390, "Sniff 12 (2002-06-26)" },
+       {  450, "Sniff 13 (2002-08-16)" },
+       {  451, "Sniff 13 (2002-08-16)" },
+       {  533, "Sniff 14 (2002-10-11)" },
+       {  580, "Sniff 15 (2002-11-14)" },
+       {  623, "Sniff 16 (2002-12-12)" },
+       {  678, "Sniff 17 (2003-01-29)" },
+       {  847, "Sniff 18 (2003-04-17)" },
+       {  876, "Sniff 19 (2003-06-10)" },
+       {  997, "Sniff 22 (2003-09-05)" },
+       { 1027, "Sniff 23 (2003-10-03)" },
+       { 1029, "Sniff 24 (2003-10-03)" },
+       { 1112, "Sniff 25 (2003-12-03)" },
+       { 1113, "Sniff 25 (2003-12-03)" },
+       { 1133, "Sniff 26 (2003-12-18)" },
+       { 1134, "Sniff 26 (2003-12-18)" },
+       { 1223, "Sniff 27 (2004-03-08)" },
+       { 1224, "Sniff 27 (2004-03-08)" },
+       { 1319, "Sniff 31 (2004-04-22)" },
+       { 1320, "Sniff 31 (2004-04-22)" },
+       { 1427, "Sniff 34 (2004-06-16)" },
+       { 1508, "Sniff 35 (2004-07-19)" },
+       { 1509, "Sniff 35 (2004-07-19)" },
+       { 1587, "Sniff 36 (2004-08-18)" },
+       { 1588, "Sniff 36 (2004-08-18)" },
+       { 1641, "Sniff 37 (2004-09-16)" },
+       { 1642, "Sniff 37 (2004-09-16)" },
+       { 1699, "Sniff 38 (2004-10-07)" },
+       { 1700, "Sniff 38 (2004-10-07)" },
+       { 1752, "Sniff 39 (2004-11-02)" },
+       { 1753, "Sniff 39 (2004-11-02)" },
+       { 1759, "Sniff 40 (2004-11-03)" },
+       { 1760, "Sniff 40 (2004-11-03)" },
+       { 1761, "Sniff 40 (2004-11-03)" },
+       { 2009, "Sniff 41 (2005-04-06)" },
+       { 2010, "Sniff 41 (2005-04-06)" },
+       { 2011, "Sniff 41 (2005-04-06)" },
+       { 2016, "Sniff 42 (2005-04-11)" },
+       { 2017, "Sniff 42 (2005-04-11)" },
+       { 2018, "Sniff 42 (2005-04-11)" },
+       { 2023, "Sniff 43 (2005-04-14)" },
+       { 2024, "Sniff 43 (2005-04-14)" },
+       { 2025, "Sniff 43 (2005-04-14)" },
+       { 2032, "Sniff 44 (2005-04-18)" },
+       { 2033, "Sniff 44 (2005-04-18)" },
+       { 2034, "Sniff 44 (2005-04-18)" },
+       { 2288, "Sniff 45 (2005-07-08)" },
+       { 2289, "Sniff 45 (2005-07-08)" },
+       { 2290, "Sniff 45 (2005-07-08)" },
+       { 2388, "Sniff 46 (2005-08-17)" },
+       { 2389, "Sniff 46 (2005-08-17)" },
+       { 2390, "Sniff 46 (2005-08-17)" },
+       { 2869, "Sniff 47 (2006-02-15)" },
+       { 2870, "Sniff 47 (2006-02-15)" },
+       { 2871, "Sniff 47 (2006-02-15)" },
+       { 3214, "Sniff 48 (2006-05-16)" },
+       { 3215, "Sniff 48 (2006-05-16)" },
+       { 3216, "Sniff 48 (2006-05-16)" },
+       { 3356, "Sniff 49 (2006-07-17)" },
+       { 3529, "Sniff 50 (2006-09-21)" },
+       { 3546, "Sniff 51 (2006-09-29)" },
+       { 3683, "Sniff 52 (2006-11-03)" },
+       {    0, }
+};
+
+char *csr_builddeftostr(uint16_t def)
+{
+       switch (def) {
+       case 0x0000:
+               return "NONE";
+       case 0x0001:
+               return "CHIP_BASE_BC01";
+       case 0x0002:
+               return "CHIP_BASE_BC02";
+       case 0x0003:
+               return "CHIP_BC01B";
+       case 0x0004:
+               return "CHIP_BC02_EXTERNAL";
+       case 0x0005:
+               return "BUILD_HCI";
+       case 0x0006:
+               return "BUILD_RFCOMM";
+       case 0x0007:
+               return "BT_VER_1_1";
+       case 0x0008:
+               return "TRANSPORT_ALL";
+       case 0x0009:
+               return "TRANSPORT_BCSP";
+       case 0x000a:
+               return "TRANSPORT_H4";
+       case 0x000b:
+               return "TRANSPORT_USB";
+       case 0x000c:
+               return "MAX_CRYPT_KEY_LEN_56";
+       case 0x000d:
+               return "MAX_CRYPT_KEY_LEN_128";
+       case 0x000e:
+               return "TRANSPORT_USER";
+       case 0x000f:
+               return "CHIP_BC02_KATO";
+       case 0x0010:
+               return "TRANSPORT_NONE";
+       case 0x0012:
+               return "REQUIRE_8MBIT";
+       case 0x0013:
+               return "RADIOTEST";
+       case 0x0014:
+               return "RADIOTEST_LITE";
+       case 0x0015:
+               return "INSTALL_FLASH";
+       case 0x0016:
+               return "INSTALL_EEPROM";
+       case 0x0017:
+               return "INSTALL_COMBO_DOT11";
+       case 0x0018:
+               return "LOWPOWER_TX";
+       case 0x0019:
+               return "TRANSPORT_TWUTL";
+       case 0x001a:
+               return "COMPILER_GCC";
+       case 0x001b:
+               return "CHIP_BC02_CLOUSEAU";
+       case 0x001c:
+               return "CHIP_BC02_TOULOUSE";
+       case 0x001d:
+               return "CHIP_BASE_BC3";
+       case 0x001e:
+               return "CHIP_BC3_NICKNACK";
+       case 0x001f:
+               return "CHIP_BC3_KALIMBA";
+       case 0x0020:
+               return "INSTALL_HCI_MODULE";
+       case 0x0021:
+               return "INSTALL_L2CAP_MODULE";
+       case 0x0022:
+               return "INSTALL_DM_MODULE";
+       case 0x0023:
+               return "INSTALL_SDP_MODULE";
+       case 0x0024:
+               return "INSTALL_RFCOMM_MODULE";
+       case 0x0025:
+               return "INSTALL_HIDIO_MODULE";
+       case 0x0026:
+               return "INSTALL_PAN_MODULE";
+       case 0x0027:
+               return "INSTALL_IPV4_MODULE";
+       case 0x0028:
+               return "INSTALL_IPV6_MODULE";
+       case 0x0029:
+               return "INSTALL_TCP_MODULE";
+       case 0x002a:
+               return "BT_VER_1_2";
+       case 0x002b:
+               return "INSTALL_UDP_MODULE";
+       case 0x002c:
+               return "REQUIRE_0_WAIT_STATES";
+       case 0x002d:
+               return "CHIP_BC3_PADDYWACK";
+       case 0x002e:
+               return "CHIP_BC4_COYOTE";
+       case 0x002f:
+               return "CHIP_BC4_ODDJOB";
+       case 0x0030:
+               return "TRANSPORT_H4DS";
+       case 0x0031:
+               return "CHIP_BASE_BC4";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+char *csr_buildidtostr(uint16_t id)
+{
+       static char str[12];
+       int i;
+
+       for (i = 0; csr_map[i].id; i++)
+               if (csr_map[i].id == id)
+                       return csr_map[i].str;
+
+       snprintf(str, 11, "Build %d", id);
+       return str;
+}
+
+char *csr_chipvertostr(uint16_t ver, uint16_t rev)
+{
+       switch (ver) {
+       case 0x00:
+               return "BlueCore01a";
+       case 0x01:
+               switch (rev) {
+               case 0x64:
+                       return "BlueCore01b (ES)";
+               case 0x65:
+               default:
+                       return "BlueCore01b";
+               }
+       case 0x02:
+               switch (rev) {
+               case 0x89:
+                       return "BlueCore02-External (ES2)";
+               case 0x8a:
+                       return "BlueCore02-External";
+               case 0x28:
+                       return "BlueCore02-ROM/Audio/Flash";
+               default:
+                       return "BlueCore02";
+               }
+       case 0x03:
+               switch (rev) {
+               case 0x43:
+                       return "BlueCore3-MM";
+               case 0x15:
+                       return "BlueCore3-ROM";
+               case 0xe2:
+                       return "BlueCore3-Flash";
+               case 0x26:
+                       return "BlueCore4-External";
+               case 0x30:
+                       return "BlueCore4-ROM";
+               default:
+                       return "BlueCore3 or BlueCore4";
+               }
+       default:
+               return "Unknown";
+       }
+}
+
+char *csr_pskeytostr(uint16_t pskey)
+{
+       switch (pskey) {
+       case CSR_PSKEY_BDADDR:
+               return "Bluetooth address";
+       case CSR_PSKEY_COUNTRYCODE:
+               return "Country code";
+       case CSR_PSKEY_CLASSOFDEVICE:
+               return "Class of device";
+       case CSR_PSKEY_DEVICE_DRIFT:
+               return "Device drift";
+       case CSR_PSKEY_DEVICE_JITTER:
+               return "Device jitter";
+       case CSR_PSKEY_MAX_ACLS:
+               return "Maximum ACL links";
+       case CSR_PSKEY_MAX_SCOS:
+               return "Maximum SCO links";
+       case CSR_PSKEY_MAX_REMOTE_MASTERS:
+               return "Maximum remote masters";
+       case CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY:
+               return "Support master and slave roles simultaneously";
+       case CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN:
+               return "Maximum HCI ACL packet length";
+       case CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN:
+               return "Maximum HCI SCO packet length";
+       case CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS:
+               return "Maximum number of HCI ACL packets";
+       case CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS:
+               return "Maximum number of HCI SCO packets";
+       case CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK:
+               return "Flow control low water mark";
+       case CSR_PSKEY_LC_MAX_TX_POWER:
+               return "Maximum transmit power";
+       case CSR_PSKEY_TX_GAIN_RAMP:
+               return "Transmit gain ramp rate";
+       case CSR_PSKEY_LC_POWER_TABLE:
+               return "Radio power table";
+       case CSR_PSKEY_LC_PEER_POWER_PERIOD:
+               return "Peer transmit power control interval";
+       case CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK:
+               return "Flow control pool low water mark";
+       case CSR_PSKEY_LC_DEFAULT_TX_POWER:
+               return "Default transmit power";
+       case CSR_PSKEY_LC_RSSI_GOLDEN_RANGE:
+               return "RSSI at bottom of golden receive range";
+       case CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK:
+               return "Combo: PIO lines and logic to disable transmit";
+       case CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK:
+               return "Combo: priority activity PIO lines and logic";
+       case CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE:
+               return "Combo: 802.11b channel number base PIO line";
+       case CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS:
+               return "Combo: channels to block either side of 802.11b";
+       case CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI:
+               return "Maximum transmit power when peer has no RSSI";
+       case CSR_PSKEY_LC_CONNECTION_RX_WINDOW:
+               return "Receive window size during connections";
+       case CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE:
+               return "Combo: which TX packets shall we protect";
+       case CSR_PSKEY_LC_ENHANCED_POWER_TABLE:
+               return "Radio power table";
+       case CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG:
+               return "RSSI configuration for use with wideband RSSI";
+       case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD:
+               return "Combo: How much notice will we give the Combo Card";
+       case CSR_PSKEY_BT_CLOCK_INIT:
+               return "Initial value of Bluetooth clock";
+       case CSR_PSKEY_TX_MR_MOD_DELAY:
+               return "TX Mod delay";
+       case CSR_PSKEY_RX_MR_SYNC_TIMING:
+               return "RX MR Sync Timing";
+       case CSR_PSKEY_RX_MR_SYNC_CONFIG:
+               return "RX MR Sync Configuration";
+       case CSR_PSKEY_LC_LOST_SYNC_SLOTS:
+               return "Time in ms for lost sync in low power modes";
+       case CSR_PSKEY_RX_MR_SAMP_CONFIG:
+               return "RX MR Sync Configuration";
+       case CSR_PSKEY_AGC_HYST_LEVELS:
+               return "AGC hysteresis levels";
+       case CSR_PSKEY_RX_LEVEL_LOW_SIGNAL:
+               return "ANA_RX_LVL at low signal strengths";
+       case CSR_PSKEY_AGC_IQ_LVL_VALUES:
+               return "ANA_IQ_LVL values for AGC algorithmn";
+       case CSR_PSKEY_MR_FTRIM_OFFSET_12DB:
+               return "ANA_RX_FTRIM offset when using 12 dB IF atten ";
+       case CSR_PSKEY_MR_FTRIM_OFFSET_6DB:
+               return "ANA_RX_FTRIM offset when using 6 dB IF atten ";
+       case CSR_PSKEY_NO_CAL_ON_BOOT:
+               return "Do not calibrate radio on boot";
+       case CSR_PSKEY_RSSI_HI_TARGET:
+               return "RSSI high target";
+       case CSR_PSKEY_PREFERRED_MIN_ATTENUATION:
+               return "Preferred minimum attenuator setting";
+       case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE:
+               return "Combo: Treat all packets as high priority";
+       case CSR_PSKEY_LC_MULTISLOT_HOLDOFF:
+               return "Time till single slot packets are used for resync";
+       case CSR_PSKEY_FREE_KEY_PIGEON_HOLE:
+               return "Link key store bitfield";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR0:
+               return "Bluetooth address + link key 0";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR1:
+               return "Bluetooth address + link key 1";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR2:
+               return "Bluetooth address + link key 2";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR3:
+               return "Bluetooth address + link key 3";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR4:
+               return "Bluetooth address + link key 4";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR5:
+               return "Bluetooth address + link key 5";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR6:
+               return "Bluetooth address + link key 6";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR7:
+               return "Bluetooth address + link key 7";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR8:
+               return "Bluetooth address + link key 8";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR9:
+               return "Bluetooth address + link key 9";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR10:
+               return "Bluetooth address + link key 10";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR11:
+               return "Bluetooth address + link key 11";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR12:
+               return "Bluetooth address + link key 12";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR13:
+               return "Bluetooth address + link key 13";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR14:
+               return "Bluetooth address + link key 14";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR15:
+               return "Bluetooth address + link key 15";
+       case CSR_PSKEY_ENC_KEY_LMIN:
+               return "Minimum encryption key length";
+       case CSR_PSKEY_ENC_KEY_LMAX:
+               return "Maximum encryption key length";
+       case CSR_PSKEY_LOCAL_SUPPORTED_FEATURES:
+               return "Local supported features block";
+       case CSR_PSKEY_LM_USE_UNIT_KEY:
+               return "Allow use of unit key for authentication?";
+       case CSR_PSKEY_HCI_NOP_DISABLE:
+               return "Disable the HCI Command_Status event on boot";
+       case CSR_PSKEY_LM_MAX_EVENT_FILTERS:
+               return "Maximum number of event filters";
+       case CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST:
+               return "Allow LM to use enc_mode=2";
+       case CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE:
+               return "LM sends two LMP_accepted messages in test mode";
+       case CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME:
+               return "Maximum time we hold a device around page";
+       case CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME:
+               return "LM period for AFH adaption";
+       case CSR_PSKEY_AFH_OPTIONS:
+               return "Options to configure AFH";
+       case CSR_PSKEY_AFH_RSSI_RUN_PERIOD:
+               return "AFH RSSI reading period";
+       case CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME:
+               return "AFH good channel adding time";
+       case CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL:
+               return "Complete link if acr barge-in role switch refused";
+       case CSR_PSKEY_MAX_PRIVATE_KEYS:
+               return "Max private link keys stored";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0:
+               return "Bluetooth address + link key 0";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1:
+               return "Bluetooth address + link key 1";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2:
+               return "Bluetooth address + link key 2";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3:
+               return "Bluetooth address + link key 3";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4:
+               return "Bluetooth address + link key 4";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5:
+               return "Bluetooth address + link key 5";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6:
+               return "Bluetooth address + link key 6";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7:
+               return "Bluetooth address + link key 7";
+       case CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS:
+               return "Local supported commands";
+       case CSR_PSKEY_LM_MAX_ABSENCE_INDEX:
+               return "Maximum absence index allowed";
+       case CSR_PSKEY_DEVICE_NAME:
+               return "Local device's \"user friendly\" name";
+       case CSR_PSKEY_AFH_RSSI_THRESHOLD:
+               return "AFH RSSI threshold";
+       case CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL:
+               return "Scan interval in slots for casual scanning";
+       case CSR_PSKEY_AFH_MIN_MAP_CHANGE:
+               return "The minimum amount to change an AFH map by";
+       case CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD:
+               return "AFH RSSI reading period when in low power mode";
+       case CSR_PSKEY_HCI_LMP_LOCAL_VERSION:
+               return "The HCI and LMP version reported locally";
+       case CSR_PSKEY_LMP_REMOTE_VERSION:
+               return "The LMP version reported remotely";
+       case CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER:
+               return "Maximum number of queued HCI Hardware Error Events";
+       case CSR_PSKEY_DFU_ATTRIBUTES:
+               return "DFU attributes";
+       case CSR_PSKEY_DFU_DETACH_TO:
+               return "DFU detach timeout";
+       case CSR_PSKEY_DFU_TRANSFER_SIZE:
+               return "DFU transfer size";
+       case CSR_PSKEY_DFU_ENABLE:
+               return "DFU enable";
+       case CSR_PSKEY_DFU_LIN_REG_ENABLE:
+               return "Linear Regulator enabled at boot in DFU mode";
+       case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB:
+               return "DFU encryption VM application public key MSB";
+       case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB:
+               return "DFU encryption VM application public key LSB";
+       case CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH:
+               return "DFU encryption VM application M dash";
+       case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB:
+               return "DFU encryption VM application public key R2N MSB";
+       case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB:
+               return "DFU encryption VM application public key R2N LSB";
+       case CSR_PSKEY_BCSP_LM_PS_BLOCK:
+               return "BCSP link establishment block";
+       case CSR_PSKEY_HOSTIO_FC_PS_BLOCK:
+               return "HCI flow control block";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO0:
+               return "Host transport channel 0 settings (BCSP ACK)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO1:
+               return "Host transport channel 1 settings (BCSP-LE)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO2:
+               return "Host transport channel 2 settings (BCCMD)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO3:
+               return "Host transport channel 3 settings (HQ)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO4:
+               return "Host transport channel 4 settings (DM)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO5:
+               return "Host transport channel 5 settings (HCI CMD/EVT)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO6:
+               return "Host transport channel 6 settings (HCI ACL)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO7:
+               return "Host transport channel 7 settings (HCI SCO)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO8:
+               return "Host transport channel 8 settings (L2CAP)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO9:
+               return "Host transport channel 9 settings (RFCOMM)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO10:
+               return "Host transport channel 10 settings (SDP)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO11:
+               return "Host transport channel 11 settings (TEST)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO12:
+               return "Host transport channel 12 settings (DFU)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO13:
+               return "Host transport channel 13 settings (VM)";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO14:
+               return "Host transport channel 14 settings";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO15:
+               return "Host transport channel 15 settings";
+       case CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT:
+               return "UART reset counter timeout";
+       case CSR_PSKEY_HOSTIO_USE_HCI_EXTN:
+               return "Use hci_extn to route non-hci channels";
+       case CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC:
+               return "Use command-complete flow control for hci_extn";
+       case CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE:
+               return "Maximum hci_extn payload size";
+       case CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT:
+               return "BCSP link establishment conf message count";
+       case CSR_PSKEY_HOSTIO_MAP_SCO_PCM:
+               return "Map SCO over PCM";
+       case CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC:
+               return "PCM interface synchronisation is difficult";
+       case CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD:
+               return "Break poll period (microseconds)";
+       case CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE:
+               return "Minimum SCO packet size sent to host over UART HCI";
+       case CSR_PSKEY_HOSTIO_MAP_SCO_CODEC:
+               return "Map SCO over the built-in codec";
+       case CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST:
+               return "High frequency boost for PCM when transmitting CVSD";
+       case CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST:
+               return "High frequency boost for PCM when receiving CVSD";
+       case CSR_PSKEY_PCM_CONFIG32:
+               return "PCM interface settings bitfields";
+       case CSR_PSKEY_USE_OLD_BCSP_LE:
+               return "Use the old version of BCSP link establishment";
+       case CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER:
+               return "CVSD uses the new filter if available";
+       case CSR_PSKEY_PCM_FORMAT:
+               return "PCM data format";
+       case CSR_PSKEY_CODEC_OUT_GAIN:
+               return "Audio output gain when using built-in codec";
+       case CSR_PSKEY_CODEC_IN_GAIN:
+               return "Audio input gain when using built-in codec";
+       case CSR_PSKEY_CODEC_PIO:
+               return "PIO to enable when built-in codec is enabled";
+       case CSR_PSKEY_PCM_LOW_JITTER_CONFIG:
+               return "PCM interface settings for low jitter master mode";
+       case CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS:
+               return "Thresholds for SCO PCM buffers";
+       case CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS:
+               return "Thresholds for SCO HCI buffers";
+       case CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT:
+               return "Route SCO data to specified slot in pcm frame";
+       case CSR_PSKEY_UART_BAUDRATE:
+               return "UART Baud rate";
+       case CSR_PSKEY_UART_CONFIG_BCSP:
+               return "UART configuration when using BCSP";
+       case CSR_PSKEY_UART_CONFIG_H4:
+               return "UART configuration when using H4";
+       case CSR_PSKEY_UART_CONFIG_H5:
+               return "UART configuration when using H5";
+       case CSR_PSKEY_UART_CONFIG_USR:
+               return "UART configuration when under VM control";
+       case CSR_PSKEY_UART_TX_CRCS:
+               return "Use CRCs for BCSP or H5";
+       case CSR_PSKEY_UART_ACK_TIMEOUT:
+               return "Acknowledgement timeout for BCSP and H5";
+       case CSR_PSKEY_UART_TX_MAX_ATTEMPTS:
+               return "Max times to send reliable BCSP or H5 message";
+       case CSR_PSKEY_UART_TX_WINDOW_SIZE:
+               return "Transmit window size for BCSP and H5";
+       case CSR_PSKEY_UART_HOST_WAKE:
+               return "UART host wakeup";
+       case CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT:
+               return "Host interface performance control.";
+       case CSR_PSKEY_PCM_ALWAYS_ENABLE:
+               return "PCM port is always enable when chip is running";
+       case CSR_PSKEY_UART_HOST_WAKE_SIGNAL:
+               return "Signal to use for uart host wakeup protocol";
+       case CSR_PSKEY_UART_CONFIG_H4DS:
+               return "UART configuration when using H4DS";
+       case CSR_PSKEY_H4DS_WAKE_DURATION:
+               return "How long to spend waking the host when using H4DS";
+       case CSR_PSKEY_H4DS_MAXWU:
+               return "Maximum number of H4DS Wake-Up messages to send";
+       case CSR_PSKEY_H4DS_LE_TIMER_PERIOD:
+               return "H4DS Link Establishment Tsync and Tconf period";
+       case CSR_PSKEY_H4DS_TWU_TIMER_PERIOD:
+               return "H4DS Twu timer period";
+       case CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD:
+               return "H4DS Tuart_idle timer period";
+       case CSR_PSKEY_ANA_FTRIM:
+               return "Crystal frequency trim";
+       case CSR_PSKEY_WD_TIMEOUT:
+               return "Watchdog timeout (microseconds)";
+       case CSR_PSKEY_WD_PERIOD:
+               return "Watchdog period (microseconds)";
+       case CSR_PSKEY_HOST_INTERFACE:
+               return "Host interface";
+       case CSR_PSKEY_HQ_HOST_TIMEOUT:
+               return "HQ host command timeout";
+       case CSR_PSKEY_HQ_ACTIVE:
+               return "Enable host query task?";
+       case CSR_PSKEY_BCCMD_SECURITY_ACTIVE:
+               return "Enable configuration security";
+       case CSR_PSKEY_ANA_FREQ:
+               return "Crystal frequency";
+       case CSR_PSKEY_PIO_PROTECT_MASK:
+               return "Access to PIO pins";
+       case CSR_PSKEY_PMALLOC_SIZES:
+               return "pmalloc sizes array";
+       case CSR_PSKEY_UART_BAUD_RATE:
+               return "UART Baud rate (pre 18)";
+       case CSR_PSKEY_UART_CONFIG:
+               return "UART configuration bitfield";
+       case CSR_PSKEY_STUB:
+               return "Stub";
+       case CSR_PSKEY_TXRX_PIO_CONTROL:
+               return "TX and RX PIO control";
+       case CSR_PSKEY_ANA_RX_LEVEL:
+               return "ANA_RX_LVL register initial value";
+       case CSR_PSKEY_ANA_RX_FTRIM:
+               return "ANA_RX_FTRIM register initial value";
+       case CSR_PSKEY_PSBC_DATA_VERSION:
+               return "Persistent store version";
+       case CSR_PSKEY_PCM0_ATTENUATION:
+               return "Volume control on PCM channel 0";
+       case CSR_PSKEY_LO_LVL_MAX:
+               return "Maximum value of LO level control register";
+       case CSR_PSKEY_LO_ADC_AMPL_MIN:
+               return "Minimum value of the LO amplitude measured on the ADC";
+       case CSR_PSKEY_LO_ADC_AMPL_MAX:
+               return "Maximum value of the LO amplitude measured on the ADC";
+       case CSR_PSKEY_IQ_TRIM_CHANNEL:
+               return "IQ calibration channel";
+       case CSR_PSKEY_IQ_TRIM_GAIN:
+               return "IQ calibration gain";
+       case CSR_PSKEY_IQ_TRIM_ENABLE:
+               return "IQ calibration enable";
+       case CSR_PSKEY_TX_OFFSET_HALF_MHZ:
+               return "Transmit offset";
+       case CSR_PSKEY_GBL_MISC_ENABLES:
+               return "Global miscellaneous hardware enables";
+       case CSR_PSKEY_UART_SLEEP_TIMEOUT:
+               return "Time in ms to deep sleep if nothing received";
+       case CSR_PSKEY_DEEP_SLEEP_STATE:
+               return "Deep sleep state usage";
+       case CSR_PSKEY_IQ_ENABLE_PHASE_TRIM:
+               return "IQ phase enable";
+       case CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD:
+               return "Time for which HCI handle is frozen after link removal";
+       case CSR_PSKEY_MAX_FROZEN_HCI_HANDLES:
+               return "Maximum number of frozen HCI handles";
+       case CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY:
+               return "Delay from freezing buf handle to deleting page table";
+       case CSR_PSKEY_IQ_TRIM_PIO_SETTINGS:
+               return "IQ PIO settings";
+       case CSR_PSKEY_USE_EXTERNAL_CLOCK:
+               return "Device uses an external clock";
+       case CSR_PSKEY_DEEP_SLEEP_WAKE_CTS:
+               return "Exit deep sleep on CTS line activity";
+       case CSR_PSKEY_FC_HC2H_FLUSH_DELAY:
+               return "Delay from disconnect to flushing HC->H FC tokens";
+       case CSR_PSKEY_RX_HIGHSIDE:
+               return "Disable the HIGHSIDE bit in ANA_CONFIG";
+       case CSR_PSKEY_TX_PRE_LVL:
+               return "TX pre-amplifier level";
+       case CSR_PSKEY_RX_SINGLE_ENDED:
+               return "RX single ended";
+       case CSR_PSKEY_TX_FILTER_CONFIG:
+               return "TX filter configuration";
+       case CSR_PSKEY_CLOCK_REQUEST_ENABLE:
+               return "External clock request enable";
+       case CSR_PSKEY_RX_MIN_ATTEN:
+               return "Minimum attenuation allowed for receiver";
+       case CSR_PSKEY_XTAL_TARGET_AMPLITUDE:
+               return "Crystal target amplitude";
+       case CSR_PSKEY_PCM_MIN_CPU_CLOCK:
+               return "Minimum CPU clock speed with PCM port running";
+       case CSR_PSKEY_HOST_INTERFACE_PIO_USB:
+               return "USB host interface selection PIO line";
+       case CSR_PSKEY_CPU_IDLE_MODE:
+               return "CPU idle mode when radio is active";
+       case CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS:
+               return "Deep sleep clears the UART RTS line";
+       case CSR_PSKEY_RF_RESONANCE_TRIM:
+               return "Frequency trim for IQ and LNA resonant circuits";
+       case CSR_PSKEY_DEEP_SLEEP_PIO_WAKE:
+               return "PIO line to wake the chip from deep sleep";
+       case CSR_PSKEY_DRAIN_BORE_TIMERS:
+               return "Energy consumption measurement settings";
+       case CSR_PSKEY_DRAIN_TX_POWER_BASE:
+               return "Energy consumption measurement settings";
+       case CSR_PSKEY_MODULE_ID:
+               return "Module serial number";
+       case CSR_PSKEY_MODULE_DESIGN:
+               return "Module design ID";
+       case CSR_PSKEY_MODULE_SECURITY_CODE:
+               return "Module security code";
+       case CSR_PSKEY_VM_DISABLE:
+               return "VM disable";
+       case CSR_PSKEY_MOD_MANUF0:
+               return "Module manufactuer data 0";
+       case CSR_PSKEY_MOD_MANUF1:
+               return "Module manufactuer data 1";
+       case CSR_PSKEY_MOD_MANUF2:
+               return "Module manufactuer data 2";
+       case CSR_PSKEY_MOD_MANUF3:
+               return "Module manufactuer data 3";
+       case CSR_PSKEY_MOD_MANUF4:
+               return "Module manufactuer data 4";
+       case CSR_PSKEY_MOD_MANUF5:
+               return "Module manufactuer data 5";
+       case CSR_PSKEY_MOD_MANUF6:
+               return "Module manufactuer data 6";
+       case CSR_PSKEY_MOD_MANUF7:
+               return "Module manufactuer data 7";
+       case CSR_PSKEY_MOD_MANUF8:
+               return "Module manufactuer data 8";
+       case CSR_PSKEY_MOD_MANUF9:
+               return "Module manufactuer data 9";
+       case CSR_PSKEY_DUT_VM_DISABLE:
+               return "VM disable when entering radiotest modes";
+       case CSR_PSKEY_USR0:
+               return "User configuration data 0";
+       case CSR_PSKEY_USR1:
+               return "User configuration data 1";
+       case CSR_PSKEY_USR2:
+               return "User configuration data 2";
+       case CSR_PSKEY_USR3:
+               return "User configuration data 3";
+       case CSR_PSKEY_USR4:
+               return "User configuration data 4";
+       case CSR_PSKEY_USR5:
+               return "User configuration data 5";
+       case CSR_PSKEY_USR6:
+               return "User configuration data 6";
+       case CSR_PSKEY_USR7:
+               return "User configuration data 7";
+       case CSR_PSKEY_USR8:
+               return "User configuration data 8";
+       case CSR_PSKEY_USR9:
+               return "User configuration data 9";
+       case CSR_PSKEY_USR10:
+               return "User configuration data 10";
+       case CSR_PSKEY_USR11:
+               return "User configuration data 11";
+       case CSR_PSKEY_USR12:
+               return "User configuration data 12";
+       case CSR_PSKEY_USR13:
+               return "User configuration data 13";
+       case CSR_PSKEY_USR14:
+               return "User configuration data 14";
+       case CSR_PSKEY_USR15:
+               return "User configuration data 15";
+       case CSR_PSKEY_USR16:
+               return "User configuration data 16";
+       case CSR_PSKEY_USR17:
+               return "User configuration data 17";
+       case CSR_PSKEY_USR18:
+               return "User configuration data 18";
+       case CSR_PSKEY_USR19:
+               return "User configuration data 19";
+       case CSR_PSKEY_USR20:
+               return "User configuration data 20";
+       case CSR_PSKEY_USR21:
+               return "User configuration data 21";
+       case CSR_PSKEY_USR22:
+               return "User configuration data 22";
+       case CSR_PSKEY_USR23:
+               return "User configuration data 23";
+       case CSR_PSKEY_USR24:
+               return "User configuration data 24";
+       case CSR_PSKEY_USR25:
+               return "User configuration data 25";
+       case CSR_PSKEY_USR26:
+               return "User configuration data 26";
+       case CSR_PSKEY_USR27:
+               return "User configuration data 27";
+       case CSR_PSKEY_USR28:
+               return "User configuration data 28";
+       case CSR_PSKEY_USR29:
+               return "User configuration data 29";
+       case CSR_PSKEY_USR30:
+               return "User configuration data 30";
+       case CSR_PSKEY_USR31:
+               return "User configuration data 31";
+       case CSR_PSKEY_USR32:
+               return "User configuration data 32";
+       case CSR_PSKEY_USR33:
+               return "User configuration data 33";
+       case CSR_PSKEY_USR34:
+               return "User configuration data 34";
+       case CSR_PSKEY_USR35:
+               return "User configuration data 35";
+       case CSR_PSKEY_USR36:
+               return "User configuration data 36";
+       case CSR_PSKEY_USR37:
+               return "User configuration data 37";
+       case CSR_PSKEY_USR38:
+               return "User configuration data 38";
+       case CSR_PSKEY_USR39:
+               return "User configuration data 39";
+       case CSR_PSKEY_USR40:
+               return "User configuration data 40";
+       case CSR_PSKEY_USR41:
+               return "User configuration data 41";
+       case CSR_PSKEY_USR42:
+               return "User configuration data 42";
+       case CSR_PSKEY_USR43:
+               return "User configuration data 43";
+       case CSR_PSKEY_USR44:
+               return "User configuration data 44";
+       case CSR_PSKEY_USR45:
+               return "User configuration data 45";
+       case CSR_PSKEY_USR46:
+               return "User configuration data 46";
+       case CSR_PSKEY_USR47:
+               return "User configuration data 47";
+       case CSR_PSKEY_USR48:
+               return "User configuration data 48";
+       case CSR_PSKEY_USR49:
+               return "User configuration data 49";
+       case CSR_PSKEY_USB_VERSION:
+               return "USB specification version number";
+       case CSR_PSKEY_USB_DEVICE_CLASS_CODES:
+               return "USB device class codes";
+       case CSR_PSKEY_USB_VENDOR_ID:
+               return "USB vendor identifier";
+       case CSR_PSKEY_USB_PRODUCT_ID:
+               return "USB product identifier";
+       case CSR_PSKEY_USB_MANUF_STRING:
+               return "USB manufacturer string";
+       case CSR_PSKEY_USB_PRODUCT_STRING:
+               return "USB product string";
+       case CSR_PSKEY_USB_SERIAL_NUMBER_STRING:
+               return "USB serial number string";
+       case CSR_PSKEY_USB_CONFIG_STRING:
+               return "USB configuration string";
+       case CSR_PSKEY_USB_ATTRIBUTES:
+               return "USB attributes bitmap";
+       case CSR_PSKEY_USB_MAX_POWER:
+               return "USB device maximum power consumption";
+       case CSR_PSKEY_USB_BT_IF_CLASS_CODES:
+               return "USB Bluetooth interface class codes";
+       case CSR_PSKEY_USB_LANGID:
+               return "USB language strings supported";
+       case CSR_PSKEY_USB_DFU_CLASS_CODES:
+               return "USB DFU class codes block";
+       case CSR_PSKEY_USB_DFU_PRODUCT_ID:
+               return "USB DFU product ID";
+       case CSR_PSKEY_USB_PIO_DETACH:
+               return "USB detach/attach PIO line";
+       case CSR_PSKEY_USB_PIO_WAKEUP:
+               return "USB wakeup PIO line";
+       case CSR_PSKEY_USB_PIO_PULLUP:
+               return "USB D+ pullup PIO line";
+       case CSR_PSKEY_USB_PIO_VBUS:
+               return "USB VBus detection PIO Line";
+       case CSR_PSKEY_USB_PIO_WAKE_TIMEOUT:
+               return "Timeout for assertion of USB PIO wake signal";
+       case CSR_PSKEY_USB_PIO_RESUME:
+               return "PIO signal used in place of bus resume";
+       case CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES:
+               return "USB Bluetooth SCO interface class codes";
+       case CSR_PSKEY_USB_SUSPEND_PIO_LEVEL:
+               return "USB PIO levels to set when suspended";
+       case CSR_PSKEY_USB_SUSPEND_PIO_DIR:
+               return "USB PIO I/O directions to set when suspended";
+       case CSR_PSKEY_USB_SUSPEND_PIO_MASK:
+               return "USB PIO lines to be set forcibly in suspend";
+       case CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE:
+               return "The maxmimum packet size for USB endpoint 0";
+       case CSR_PSKEY_USB_CONFIG:
+               return "USB config params for new chips (>bc2)";
+       case CSR_PSKEY_RADIOTEST_ATTEN_INIT:
+               return "Radio test initial attenuator";
+       case CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME:
+               return "IQ first calibration period in test";
+       case CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME:
+               return "IQ subsequent calibration period in test";
+       case CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE:
+               return "LO_LVL calibration enable";
+       case CSR_PSKEY_RADIOTEST_DISABLE_MODULATION:
+               return "Disable modulation during radiotest transmissions";
+       case CSR_PSKEY_RFCOMM_FCON_THRESHOLD:
+               return "RFCOMM aggregate flow control on threshold";
+       case CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD:
+               return "RFCOMM aggregate flow control off threshold";
+       case CSR_PSKEY_IPV6_STATIC_ADDR:
+               return "Static IPv6 address";
+       case CSR_PSKEY_IPV4_STATIC_ADDR:
+               return "Static IPv4 address";
+       case CSR_PSKEY_IPV6_STATIC_PREFIX_LEN:
+               return "Static IPv6 prefix length";
+       case CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR:
+               return "Static IPv6 router address";
+       case CSR_PSKEY_IPV4_STATIC_SUBNET_MASK:
+               return "Static IPv4 subnet mask";
+       case CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR:
+               return "Static IPv4 router address";
+       case CSR_PSKEY_MDNS_NAME:
+               return "Multicast DNS name";
+       case CSR_PSKEY_FIXED_PIN:
+               return "Fixed PIN";
+       case CSR_PSKEY_MDNS_PORT:
+               return "Multicast DNS port";
+       case CSR_PSKEY_MDNS_TTL:
+               return "Multicast DNS TTL";
+       case CSR_PSKEY_MDNS_IPV4_ADDR:
+               return "Multicast DNS IPv4 address";
+       case CSR_PSKEY_ARP_CACHE_TIMEOUT:
+               return "ARP cache timeout";
+       case CSR_PSKEY_HFP_POWER_TABLE:
+               return "HFP power table";
+       case CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS:
+               return "Energy consumption estimation timer counters";
+       case CSR_PSKEY_DRAIN_BORE_COUNTERS:
+               return "Energy consumption estimation counters";
+       case CSR_PSKEY_LOOP_FILTER_TRIM:
+               return "Trim value to optimise loop filter";
+       case CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK:
+               return "Energy consumption estimation current peak";
+       case CSR_PSKEY_VM_E2_CACHE_LIMIT:
+               return "Maximum RAM for caching EEPROM VM application";
+       case CSR_PSKEY_FORCE_16MHZ_REF_PIO:
+               return "PIO line to force 16 MHz reference to be assumed";
+       case CSR_PSKEY_CDMA_LO_REF_LIMITS:
+               return "Local oscillator frequency reference limits for CDMA";
+       case CSR_PSKEY_CDMA_LO_ERROR_LIMITS:
+               return "Local oscillator frequency error limits for CDMA";
+       case CSR_PSKEY_CLOCK_STARTUP_DELAY:
+               return "Clock startup delay in milliseconds";
+       case CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR:
+               return "Deep sleep clock correction factor";
+       case CSR_PSKEY_TEMPERATURE_CALIBRATION:
+               return "Temperature in deg C for a given internal setting";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA:
+               return "Temperature for given internal PA adjustment";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL:
+               return "Temperature for a given TX_PRE_LVL adjustment";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB:
+               return "Temperature for a given TX_BB adjustment";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM:
+               return "Temperature for given crystal trim adjustment";
+       case CSR_PSKEY_TEST_DELTA_OFFSET:
+               return "Frequency offset applied to synthesiser in test mode";
+       case CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET:
+               return "Receiver dynamic level offset depending on channel";
+       case CSR_PSKEY_TEST_FORCE_OFFSET:
+               return "Force use of exact value in PSKEY_TEST_DELTA_OFFSET";
+       case CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS:
+               return "Trap bad division ratios in radio frequency tables";
+       case CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS:
+               return "LO frequency reference limits for CDMA in radiotest";
+       case CSR_PSKEY_INITIAL_BOOTMODE:
+               return "Initial device bootmode";
+       case CSR_PSKEY_ONCHIP_HCI_CLIENT:
+               return "HCI traffic routed internally";
+       case CSR_PSKEY_RX_ATTEN_BACKOFF:
+               return "Receiver attenuation back-off";
+       case CSR_PSKEY_RX_ATTEN_UPDATE_RATE:
+               return "Receiver attenuation update rate";
+       case CSR_PSKEY_SYNTH_TXRX_THRESHOLDS:
+               return "Local oscillator tuning voltage limits for tx and rx";
+       case CSR_PSKEY_MIN_WAIT_STATES:
+               return "Flash wait state indicator";
+       case CSR_PSKEY_RSSI_CORRECTION:
+               return "RSSI correction factor.";
+       case CSR_PSKEY_SCHED_THROTTLE_TIMEOUT:
+               return "Scheduler performance control.";
+       case CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK:
+               return "Deep sleep uses external 32 kHz clock source";
+       case CSR_PSKEY_TRIM_RADIO_FILTERS:
+               return "Trim rx and tx radio filters if true.";
+       case CSR_PSKEY_TRANSMIT_OFFSET:
+               return "Transmit offset in units of 62.5 kHz";
+       case CSR_PSKEY_USB_VM_CONTROL:
+               return "VM application will supply USB descriptors";
+       case CSR_PSKEY_MR_ANA_RX_FTRIM:
+               return "Medium rate value for the ANA_RX_FTRIM register";
+       case CSR_PSKEY_I2C_CONFIG:
+               return "I2C configuration";
+       case CSR_PSKEY_IQ_LVL_RX:
+               return "IQ demand level for reception";
+       case CSR_PSKEY_MR_TX_FILTER_CONFIG:
+               return "TX filter configuration used for enhanced data rate";
+       case CSR_PSKEY_MR_TX_CONFIG2:
+               return "TX filter configuration used for enhanced data rate";
+       case CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET:
+               return "Don't reset bootmode if USB host resets";
+       case CSR_PSKEY_LC_USE_THROTTLING:
+               return "Adjust packet selection on packet error rate";
+       case CSR_PSKEY_CHARGER_TRIM:
+               return "Trim value for the current charger";
+       case CSR_PSKEY_CLOCK_REQUEST_FEATURES:
+               return "Clock request is tristated if enabled";
+       case CSR_PSKEY_TRANSMIT_OFFSET_CLASS1:
+               return "Transmit offset / 62.5 kHz for class 1 radios";
+       case CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO:
+               return "PIO line asserted in class1 operation to avoid PA";
+       case CSR_PSKEY_MR_PIO_CONFIG:
+               return "PIO line asserted in class1 operation to avoid PA";
+       case CSR_PSKEY_UART_CONFIG2:
+               return "The UART Sampling point";
+       case CSR_PSKEY_CLASS1_IQ_LVL:
+               return "IQ demand level for class 1 power level";
+       case CSR_PSKEY_CLASS1_TX_CONFIG2:
+               return "TX filter configuration used for class 1 tx power";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1:
+               return "Temperature for given internal PA adjustment";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1:
+               return "Temperature for given internal PA adjustment";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR:
+               return "Temperature adjustment for TX_PRE_LVL in EDR";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER:
+               return "Temperature for a given TX_BB in EDR header";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD:
+               return "Temperature for a given TX_BB in EDR payload";
+       case CSR_PSKEY_RX_MR_EQ_TAPS:
+               return "Adjust receiver configuration for EDR";
+       case CSR_PSKEY_TX_PRE_LVL_CLASS1:
+               return "TX pre-amplifier level in class 1 operation";
+       case CSR_PSKEY_ANALOGUE_ATTENUATOR:
+               return "TX analogue attenuator setting";
+       case CSR_PSKEY_MR_RX_FILTER_TRIM:
+               return "Trim for receiver used in EDR.";
+       case CSR_PSKEY_MR_RX_FILTER_RESPONSE:
+               return "Filter response for receiver used in EDR.";
+       case CSR_PSKEY_PIO_WAKEUP_STATE:
+               return "PIO deep sleep wake up state ";
+       case CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP:
+               return "TX IF atten off temperature when using EDR.";
+       case CSR_PSKEY_LO_DIV_LATCH_BYPASS:
+               return "Bypass latch for LO dividers";
+       case CSR_PSKEY_LO_VCO_STANDBY:
+               return "Use standby mode for the LO VCO";
+       case CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT:
+               return "Slow clock sampling filter constant";
+       case CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER:
+               return "Slow clock filter fractional threshold";
+       case CSR_PSKEY_USB_ATTRIBUTES_POWER:
+               return "USB self powered";
+       case CSR_PSKEY_USB_ATTRIBUTES_WAKEUP:
+               return "USB responds to wake-up";
+       case CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT:
+               return "DFU manifestation tolerant";
+       case CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD:
+               return "DFU can upload";
+       case CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD:
+               return "DFU can download";
+       case CSR_PSKEY_UART_CONFIG_STOP_BITS:
+               return "UART: stop bits";
+       case CSR_PSKEY_UART_CONFIG_PARITY_BIT:
+               return "UART: parity bit";
+       case CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN:
+               return "UART: hardware flow control";
+       case CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN:
+               return "UART: RTS auto-enabled";
+       case CSR_PSKEY_UART_CONFIG_RTS:
+               return "UART: RTS asserted";
+       case CSR_PSKEY_UART_CONFIG_TX_ZERO_EN:
+               return "UART: TX zero enable";
+       case CSR_PSKEY_UART_CONFIG_NON_BCSP_EN:
+               return "UART: enable BCSP-specific hardware";
+       case CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY:
+               return "UART: RX rate delay";
+       case CSR_PSKEY_UART_SEQ_TIMEOUT:
+               return "UART: BCSP ack timeout";
+       case CSR_PSKEY_UART_SEQ_RETRIES:
+               return "UART: retry limit in sequencing layer";
+       case CSR_PSKEY_UART_SEQ_WINSIZE:
+               return "UART: BCSP transmit window size";
+       case CSR_PSKEY_UART_USE_CRC_ON_TX:
+               return "UART: use BCSP CRCs";
+       case CSR_PSKEY_UART_HOST_INITIAL_STATE:
+               return "UART: initial host state";
+       case CSR_PSKEY_UART_HOST_ATTENTION_SPAN:
+               return "UART: host attention span";
+       case CSR_PSKEY_UART_HOST_WAKEUP_TIME:
+               return "UART: host wakeup time";
+       case CSR_PSKEY_UART_HOST_WAKEUP_WAIT:
+               return "UART: host wakeup wait";
+       case CSR_PSKEY_BCSP_LM_MODE:
+               return "BCSP link establishment mode";
+       case CSR_PSKEY_BCSP_LM_SYNC_RETRIES:
+               return "BCSP link establishment sync retries";
+       case CSR_PSKEY_BCSP_LM_TSHY:
+               return "BCSP link establishment Tshy";
+       case CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS:
+               return "DFU mode UART: stop bits";
+       case CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT:
+               return "DFU mode UART: parity bit";
+       case CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN:
+               return "DFU mode UART: hardware flow control";
+       case CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN:
+               return "DFU mode UART: RTS auto-enabled";
+       case CSR_PSKEY_UART_DFU_CONFIG_RTS:
+               return "DFU mode UART: RTS asserted";
+       case CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN:
+               return "DFU mode UART: TX zero enable";
+       case CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN:
+               return "DFU mode UART: enable BCSP-specific hardware";
+       case CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY:
+               return "DFU mode UART: RX rate delay";
+       case CSR_PSKEY_AMUX_AIO0:
+               return "Multiplexer for AIO 0";
+       case CSR_PSKEY_AMUX_AIO1:
+               return "Multiplexer for AIO 1";
+       case CSR_PSKEY_AMUX_AIO2:
+               return "Multiplexer for AIO 2";
+       case CSR_PSKEY_AMUX_AIO3:
+               return "Multiplexer for AIO 3";
+       case CSR_PSKEY_LOCAL_NAME_SIMPLIFIED:
+               return "Local Name (simplified)";
+       case CSR_PSKEY_EXTENDED_STUB:
+               return "Extended stub";
+       default:
+               return "Unknown";
+       }
+}
+
+char *csr_pskeytoval(uint16_t pskey)
+{
+       switch (pskey) {
+       case CSR_PSKEY_BDADDR:
+               return "BDADDR";
+       case CSR_PSKEY_COUNTRYCODE:
+               return "COUNTRYCODE";
+       case CSR_PSKEY_CLASSOFDEVICE:
+               return "CLASSOFDEVICE";
+       case CSR_PSKEY_DEVICE_DRIFT:
+               return "DEVICE_DRIFT";
+       case CSR_PSKEY_DEVICE_JITTER:
+               return "DEVICE_JITTER";
+       case CSR_PSKEY_MAX_ACLS:
+               return "MAX_ACLS";
+       case CSR_PSKEY_MAX_SCOS:
+               return "MAX_SCOS";
+       case CSR_PSKEY_MAX_REMOTE_MASTERS:
+               return "MAX_REMOTE_MASTERS";
+       case CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY:
+               return "ENABLE_MASTERY_WITH_SLAVERY";
+       case CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN:
+               return "H_HC_FC_MAX_ACL_PKT_LEN";
+       case CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN:
+               return "H_HC_FC_MAX_SCO_PKT_LEN";
+       case CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS:
+               return "H_HC_FC_MAX_ACL_PKTS";
+       case CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS:
+               return "H_HC_FC_MAX_SCO_PKTS";
+       case CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK:
+               return "LC_FC_BUFFER_LOW_WATER_MARK";
+       case CSR_PSKEY_LC_MAX_TX_POWER:
+               return "LC_MAX_TX_POWER";
+       case CSR_PSKEY_TX_GAIN_RAMP:
+               return "TX_GAIN_RAMP";
+       case CSR_PSKEY_LC_POWER_TABLE:
+               return "LC_POWER_TABLE";
+       case CSR_PSKEY_LC_PEER_POWER_PERIOD:
+               return "LC_PEER_POWER_PERIOD";
+       case CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK:
+               return "LC_FC_POOLS_LOW_WATER_MARK";
+       case CSR_PSKEY_LC_DEFAULT_TX_POWER:
+               return "LC_DEFAULT_TX_POWER";
+       case CSR_PSKEY_LC_RSSI_GOLDEN_RANGE:
+               return "LC_RSSI_GOLDEN_RANGE";
+       case CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK:
+               return "LC_COMBO_DISABLE_PIO_MASK";
+       case CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK:
+               return "LC_COMBO_PRIORITY_PIO_MASK";
+       case CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE:
+               return "LC_COMBO_DOT11_CHANNEL_PIO_BASE";
+       case CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS:
+               return "LC_COMBO_DOT11_BLOCK_CHANNELS";
+       case CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI:
+               return "LC_MAX_TX_POWER_NO_RSSI";
+       case CSR_PSKEY_LC_CONNECTION_RX_WINDOW:
+               return "LC_CONNECTION_RX_WINDOW";
+       case CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE:
+               return "LC_COMBO_DOT11_TX_PROTECTION_MODE";
+       case CSR_PSKEY_LC_ENHANCED_POWER_TABLE:
+               return "LC_ENHANCED_POWER_TABLE";
+       case CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG:
+               return "LC_WIDEBAND_RSSI_CONFIG";
+       case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD:
+               return "LC_COMBO_DOT11_PRIORITY_LEAD";
+       case CSR_PSKEY_BT_CLOCK_INIT:
+               return "BT_CLOCK_INIT";
+       case CSR_PSKEY_TX_MR_MOD_DELAY:
+               return "TX_MR_MOD_DELAY";
+       case CSR_PSKEY_RX_MR_SYNC_TIMING:
+               return "RX_MR_SYNC_TIMING";
+       case CSR_PSKEY_RX_MR_SYNC_CONFIG:
+               return "RX_MR_SYNC_CONFIG";
+       case CSR_PSKEY_LC_LOST_SYNC_SLOTS:
+               return "LC_LOST_SYNC_SLOTS";
+       case CSR_PSKEY_RX_MR_SAMP_CONFIG:
+               return "RX_MR_SAMP_CONFIG";
+       case CSR_PSKEY_AGC_HYST_LEVELS:
+               return "AGC_HYST_LEVELS";
+       case CSR_PSKEY_RX_LEVEL_LOW_SIGNAL:
+               return "RX_LEVEL_LOW_SIGNAL";
+       case CSR_PSKEY_AGC_IQ_LVL_VALUES:
+               return "AGC_IQ_LVL_VALUES";
+       case CSR_PSKEY_MR_FTRIM_OFFSET_12DB:
+               return "MR_FTRIM_OFFSET_12DB";
+       case CSR_PSKEY_MR_FTRIM_OFFSET_6DB:
+               return "MR_FTRIM_OFFSET_6DB";
+       case CSR_PSKEY_NO_CAL_ON_BOOT:
+               return "NO_CAL_ON_BOOT";
+       case CSR_PSKEY_RSSI_HI_TARGET:
+               return "RSSI_HI_TARGET";
+       case CSR_PSKEY_PREFERRED_MIN_ATTENUATION:
+               return "PREFERRED_MIN_ATTENUATION";
+       case CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE:
+               return "LC_COMBO_DOT11_PRIORITY_OVERRIDE";
+       case CSR_PSKEY_LC_MULTISLOT_HOLDOFF:
+               return "LC_MULTISLOT_HOLDOFF";
+       case CSR_PSKEY_FREE_KEY_PIGEON_HOLE:
+               return "FREE_KEY_PIGEON_HOLE";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR0:
+               return "LINK_KEY_BD_ADDR0";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR1:
+               return "LINK_KEY_BD_ADDR1";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR2:
+               return "LINK_KEY_BD_ADDR2";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR3:
+               return "LINK_KEY_BD_ADDR3";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR4:
+               return "LINK_KEY_BD_ADDR4";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR5:
+               return "LINK_KEY_BD_ADDR5";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR6:
+               return "LINK_KEY_BD_ADDR6";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR7:
+               return "LINK_KEY_BD_ADDR7";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR8:
+               return "LINK_KEY_BD_ADDR8";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR9:
+               return "LINK_KEY_BD_ADDR9";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR10:
+               return "LINK_KEY_BD_ADDR10";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR11:
+               return "LINK_KEY_BD_ADDR11";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR12:
+               return "LINK_KEY_BD_ADDR12";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR13:
+               return "LINK_KEY_BD_ADDR13";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR14:
+               return "LINK_KEY_BD_ADDR14";
+       case CSR_PSKEY_LINK_KEY_BD_ADDR15:
+               return "LINK_KEY_BD_ADDR15";
+       case CSR_PSKEY_ENC_KEY_LMIN:
+               return "ENC_KEY_LMIN";
+       case CSR_PSKEY_ENC_KEY_LMAX:
+               return "ENC_KEY_LMAX";
+       case CSR_PSKEY_LOCAL_SUPPORTED_FEATURES:
+               return "LOCAL_SUPPORTED_FEATURES";
+       case CSR_PSKEY_LM_USE_UNIT_KEY:
+               return "LM_USE_UNIT_KEY";
+       case CSR_PSKEY_HCI_NOP_DISABLE:
+               return "HCI_NOP_DISABLE";
+       case CSR_PSKEY_LM_MAX_EVENT_FILTERS:
+               return "LM_MAX_EVENT_FILTERS";
+       case CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST:
+               return "LM_USE_ENC_MODE_BROADCAST";
+       case CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE:
+               return "LM_TEST_SEND_ACCEPTED_TWICE";
+       case CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME:
+               return "LM_MAX_PAGE_HOLD_TIME";
+       case CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME:
+               return "AFH_ADAPTATION_RESPONSE_TIME";
+       case CSR_PSKEY_AFH_OPTIONS:
+               return "AFH_OPTIONS";
+       case CSR_PSKEY_AFH_RSSI_RUN_PERIOD:
+               return "AFH_RSSI_RUN_PERIOD";
+       case CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME:
+               return "AFH_REENABLE_CHANNEL_TIME";
+       case CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL:
+               return "NO_DROP_ON_ACR_MS_FAIL";
+       case CSR_PSKEY_MAX_PRIVATE_KEYS:
+               return "MAX_PRIVATE_KEYS";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0:
+               return "PRIVATE_LINK_KEY_BD_ADDR0";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1:
+               return "PRIVATE_LINK_KEY_BD_ADDR1";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2:
+               return "PRIVATE_LINK_KEY_BD_ADDR2";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3:
+               return "PRIVATE_LINK_KEY_BD_ADDR3";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4:
+               return "PRIVATE_LINK_KEY_BD_ADDR4";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5:
+               return "PRIVATE_LINK_KEY_BD_ADDR5";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6:
+               return "PRIVATE_LINK_KEY_BD_ADDR6";
+       case CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7:
+               return "PRIVATE_LINK_KEY_BD_ADDR7";
+       case CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS:
+               return "LOCAL_SUPPORTED_COMMANDS";
+       case CSR_PSKEY_LM_MAX_ABSENCE_INDEX:
+               return "LM_MAX_ABSENCE_INDEX";
+       case CSR_PSKEY_DEVICE_NAME:
+               return "DEVICE_NAME";
+       case CSR_PSKEY_AFH_RSSI_THRESHOLD:
+               return "AFH_RSSI_THRESHOLD";
+       case CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL:
+               return "LM_CASUAL_SCAN_INTERVAL";
+       case CSR_PSKEY_AFH_MIN_MAP_CHANGE:
+               return "AFH_MIN_MAP_CHANGE";
+       case CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD:
+               return "AFH_RSSI_LP_RUN_PERIOD";
+       case CSR_PSKEY_HCI_LMP_LOCAL_VERSION:
+               return "HCI_LMP_LOCAL_VERSION";
+       case CSR_PSKEY_LMP_REMOTE_VERSION:
+               return "LMP_REMOTE_VERSION";
+       case CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER:
+               return "HOLD_ERROR_MESSAGE_NUMBER";
+       case CSR_PSKEY_DFU_ATTRIBUTES:
+               return "DFU_ATTRIBUTES";
+       case CSR_PSKEY_DFU_DETACH_TO:
+               return "DFU_DETACH_TO";
+       case CSR_PSKEY_DFU_TRANSFER_SIZE:
+               return "DFU_TRANSFER_SIZE";
+       case CSR_PSKEY_DFU_ENABLE:
+               return "DFU_ENABLE";
+       case CSR_PSKEY_DFU_LIN_REG_ENABLE:
+               return "DFU_LIN_REG_ENABLE";
+       case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB:
+               return "DFUENC_VMAPP_PK_MODULUS_MSB";
+       case CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB:
+               return "DFUENC_VMAPP_PK_MODULUS_LSB";
+       case CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH:
+               return "DFUENC_VMAPP_PK_M_DASH";
+       case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB:
+               return "DFUENC_VMAPP_PK_R2N_MSB";
+       case CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB:
+               return "DFUENC_VMAPP_PK_R2N_LSB";
+       case CSR_PSKEY_BCSP_LM_PS_BLOCK:
+               return "BCSP_LM_PS_BLOCK";
+       case CSR_PSKEY_HOSTIO_FC_PS_BLOCK:
+               return "HOSTIO_FC_PS_BLOCK";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO0:
+               return "HOSTIO_PROTOCOL_INFO0";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO1:
+               return "HOSTIO_PROTOCOL_INFO1";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO2:
+               return "HOSTIO_PROTOCOL_INFO2";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO3:
+               return "HOSTIO_PROTOCOL_INFO3";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO4:
+               return "HOSTIO_PROTOCOL_INFO4";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO5:
+               return "HOSTIO_PROTOCOL_INFO5";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO6:
+               return "HOSTIO_PROTOCOL_INFO6";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO7:
+               return "HOSTIO_PROTOCOL_INFO7";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO8:
+               return "HOSTIO_PROTOCOL_INFO8";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO9:
+               return "HOSTIO_PROTOCOL_INFO9";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO10:
+               return "HOSTIO_PROTOCOL_INFO10";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO11:
+               return "HOSTIO_PROTOCOL_INFO11";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO12:
+               return "HOSTIO_PROTOCOL_INFO12";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO13:
+               return "HOSTIO_PROTOCOL_INFO13";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO14:
+               return "HOSTIO_PROTOCOL_INFO14";
+       case CSR_PSKEY_HOSTIO_PROTOCOL_INFO15:
+               return "HOSTIO_PROTOCOL_INFO15";
+       case CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT:
+               return "HOSTIO_UART_RESET_TIMEOUT";
+       case CSR_PSKEY_HOSTIO_USE_HCI_EXTN:
+               return "HOSTIO_USE_HCI_EXTN";
+       case CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC:
+               return "HOSTIO_USE_HCI_EXTN_CCFC";
+       case CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE:
+               return "HOSTIO_HCI_EXTN_PAYLOAD_SIZE";
+       case CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT:
+               return "BCSP_LM_CNF_CNT_LIMIT";
+       case CSR_PSKEY_HOSTIO_MAP_SCO_PCM:
+               return "HOSTIO_MAP_SCO_PCM";
+       case CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC:
+               return "HOSTIO_AWKWARD_PCM_SYNC";
+       case CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD:
+               return "HOSTIO_BREAK_POLL_PERIOD";
+       case CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE:
+               return "HOSTIO_MIN_UART_HCI_SCO_SIZE";
+       case CSR_PSKEY_HOSTIO_MAP_SCO_CODEC:
+               return "HOSTIO_MAP_SCO_CODEC";
+       case CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST:
+               return "PCM_CVSD_TX_HI_FREQ_BOOST";
+       case CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST:
+               return "PCM_CVSD_RX_HI_FREQ_BOOST";
+       case CSR_PSKEY_PCM_CONFIG32:
+               return "PCM_CONFIG32";
+       case CSR_PSKEY_USE_OLD_BCSP_LE:
+               return "USE_OLD_BCSP_LE";
+       case CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER:
+               return "PCM_CVSD_USE_NEW_FILTER";
+       case CSR_PSKEY_PCM_FORMAT:
+               return "PCM_FORMAT";
+       case CSR_PSKEY_CODEC_OUT_GAIN:
+               return "CODEC_OUT_GAIN";
+       case CSR_PSKEY_CODEC_IN_GAIN:
+               return "CODEC_IN_GAIN";
+       case CSR_PSKEY_CODEC_PIO:
+               return "CODEC_PIO";
+       case CSR_PSKEY_PCM_LOW_JITTER_CONFIG:
+               return "PCM_LOW_JITTER_CONFIG";
+       case CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS:
+               return "HOSTIO_SCO_PCM_THRESHOLDS";
+       case CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS:
+               return "HOSTIO_SCO_HCI_THRESHOLDS";
+       case CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT:
+               return "HOSTIO_MAP_SCO_PCM_SLOT";
+       case CSR_PSKEY_UART_BAUDRATE:
+               return "UART_BAUDRATE";
+       case CSR_PSKEY_UART_CONFIG_BCSP:
+               return "UART_CONFIG_BCSP";
+       case CSR_PSKEY_UART_CONFIG_H4:
+               return "UART_CONFIG_H4";
+       case CSR_PSKEY_UART_CONFIG_H5:
+               return "UART_CONFIG_H5";
+       case CSR_PSKEY_UART_CONFIG_USR:
+               return "UART_CONFIG_USR";
+       case CSR_PSKEY_UART_TX_CRCS:
+               return "UART_TX_CRCS";
+       case CSR_PSKEY_UART_ACK_TIMEOUT:
+               return "UART_ACK_TIMEOUT";
+       case CSR_PSKEY_UART_TX_MAX_ATTEMPTS:
+               return "UART_TX_MAX_ATTEMPTS";
+       case CSR_PSKEY_UART_TX_WINDOW_SIZE:
+               return "UART_TX_WINDOW_SIZE";
+       case CSR_PSKEY_UART_HOST_WAKE:
+               return "UART_HOST_WAKE";
+       case CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT:
+               return "HOSTIO_THROTTLE_TIMEOUT";
+       case CSR_PSKEY_PCM_ALWAYS_ENABLE:
+               return "PCM_ALWAYS_ENABLE";
+       case CSR_PSKEY_UART_HOST_WAKE_SIGNAL:
+               return "UART_HOST_WAKE_SIGNAL";
+       case CSR_PSKEY_UART_CONFIG_H4DS:
+               return "UART_CONFIG_H4DS";
+       case CSR_PSKEY_H4DS_WAKE_DURATION:
+               return "H4DS_WAKE_DURATION";
+       case CSR_PSKEY_H4DS_MAXWU:
+               return "H4DS_MAXWU";
+       case CSR_PSKEY_H4DS_LE_TIMER_PERIOD:
+               return "H4DS_LE_TIMER_PERIOD";
+       case CSR_PSKEY_H4DS_TWU_TIMER_PERIOD:
+               return "H4DS_TWU_TIMER_PERIOD";
+       case CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD:
+               return "H4DS_UART_IDLE_TIMER_PERIOD";
+       case CSR_PSKEY_ANA_FTRIM:
+               return "ANA_FTRIM";
+       case CSR_PSKEY_WD_TIMEOUT:
+               return "WD_TIMEOUT";
+       case CSR_PSKEY_WD_PERIOD:
+               return "WD_PERIOD";
+       case CSR_PSKEY_HOST_INTERFACE:
+               return "HOST_INTERFACE";
+       case CSR_PSKEY_HQ_HOST_TIMEOUT:
+               return "HQ_HOST_TIMEOUT";
+       case CSR_PSKEY_HQ_ACTIVE:
+               return "HQ_ACTIVE";
+       case CSR_PSKEY_BCCMD_SECURITY_ACTIVE:
+               return "BCCMD_SECURITY_ACTIVE";
+       case CSR_PSKEY_ANA_FREQ:
+               return "ANA_FREQ";
+       case CSR_PSKEY_PIO_PROTECT_MASK:
+               return "PIO_PROTECT_MASK";
+       case CSR_PSKEY_PMALLOC_SIZES:
+               return "PMALLOC_SIZES";
+       case CSR_PSKEY_UART_BAUD_RATE:
+               return "UART_BAUD_RATE";
+       case CSR_PSKEY_UART_CONFIG:
+               return "UART_CONFIG";
+       case CSR_PSKEY_STUB:
+               return "STUB";
+       case CSR_PSKEY_TXRX_PIO_CONTROL:
+               return "TXRX_PIO_CONTROL";
+       case CSR_PSKEY_ANA_RX_LEVEL:
+               return "ANA_RX_LEVEL";
+       case CSR_PSKEY_ANA_RX_FTRIM:
+               return "ANA_RX_FTRIM";
+       case CSR_PSKEY_PSBC_DATA_VERSION:
+               return "PSBC_DATA_VERSION";
+       case CSR_PSKEY_PCM0_ATTENUATION:
+               return "PCM0_ATTENUATION";
+       case CSR_PSKEY_LO_LVL_MAX:
+               return "LO_LVL_MAX";
+       case CSR_PSKEY_LO_ADC_AMPL_MIN:
+               return "LO_ADC_AMPL_MIN";
+       case CSR_PSKEY_LO_ADC_AMPL_MAX:
+               return "LO_ADC_AMPL_MAX";
+       case CSR_PSKEY_IQ_TRIM_CHANNEL:
+               return "IQ_TRIM_CHANNEL";
+       case CSR_PSKEY_IQ_TRIM_GAIN:
+               return "IQ_TRIM_GAIN";
+       case CSR_PSKEY_IQ_TRIM_ENABLE:
+               return "IQ_TRIM_ENABLE";
+       case CSR_PSKEY_TX_OFFSET_HALF_MHZ:
+               return "TX_OFFSET_HALF_MHZ";
+       case CSR_PSKEY_GBL_MISC_ENABLES:
+               return "GBL_MISC_ENABLES";
+       case CSR_PSKEY_UART_SLEEP_TIMEOUT:
+               return "UART_SLEEP_TIMEOUT";
+       case CSR_PSKEY_DEEP_SLEEP_STATE:
+               return "DEEP_SLEEP_STATE";
+       case CSR_PSKEY_IQ_ENABLE_PHASE_TRIM:
+               return "IQ_ENABLE_PHASE_TRIM";
+       case CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD:
+               return "HCI_HANDLE_FREEZE_PERIOD";
+       case CSR_PSKEY_MAX_FROZEN_HCI_HANDLES:
+               return "MAX_FROZEN_HCI_HANDLES";
+       case CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY:
+               return "PAGETABLE_DESTRUCTION_DELAY";
+       case CSR_PSKEY_IQ_TRIM_PIO_SETTINGS:
+               return "IQ_TRIM_PIO_SETTINGS";
+       case CSR_PSKEY_USE_EXTERNAL_CLOCK:
+               return "USE_EXTERNAL_CLOCK";
+       case CSR_PSKEY_DEEP_SLEEP_WAKE_CTS:
+               return "DEEP_SLEEP_WAKE_CTS";
+       case CSR_PSKEY_FC_HC2H_FLUSH_DELAY:
+               return "FC_HC2H_FLUSH_DELAY";
+       case CSR_PSKEY_RX_HIGHSIDE:
+               return "RX_HIGHSIDE";
+       case CSR_PSKEY_TX_PRE_LVL:
+               return "TX_PRE_LVL";
+       case CSR_PSKEY_RX_SINGLE_ENDED:
+               return "RX_SINGLE_ENDED";
+       case CSR_PSKEY_TX_FILTER_CONFIG:
+               return "TX_FILTER_CONFIG";
+       case CSR_PSKEY_CLOCK_REQUEST_ENABLE:
+               return "CLOCK_REQUEST_ENABLE";
+       case CSR_PSKEY_RX_MIN_ATTEN:
+               return "RX_MIN_ATTEN";
+       case CSR_PSKEY_XTAL_TARGET_AMPLITUDE:
+               return "XTAL_TARGET_AMPLITUDE";
+       case CSR_PSKEY_PCM_MIN_CPU_CLOCK:
+               return "PCM_MIN_CPU_CLOCK";
+       case CSR_PSKEY_HOST_INTERFACE_PIO_USB:
+               return "HOST_INTERFACE_PIO_USB";
+       case CSR_PSKEY_CPU_IDLE_MODE:
+               return "CPU_IDLE_MODE";
+       case CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS:
+               return "DEEP_SLEEP_CLEAR_RTS";
+       case CSR_PSKEY_RF_RESONANCE_TRIM:
+               return "RF_RESONANCE_TRIM";
+       case CSR_PSKEY_DEEP_SLEEP_PIO_WAKE:
+               return "DEEP_SLEEP_PIO_WAKE";
+       case CSR_PSKEY_DRAIN_BORE_TIMERS:
+               return "DRAIN_BORE_TIMERS";
+       case CSR_PSKEY_DRAIN_TX_POWER_BASE:
+               return "DRAIN_TX_POWER_BASE";
+       case CSR_PSKEY_MODULE_ID:
+               return "MODULE_ID";
+       case CSR_PSKEY_MODULE_DESIGN:
+               return "MODULE_DESIGN";
+       case CSR_PSKEY_MODULE_SECURITY_CODE:
+               return "MODULE_SECURITY_CODE";
+       case CSR_PSKEY_VM_DISABLE:
+               return "VM_DISABLE";
+       case CSR_PSKEY_MOD_MANUF0:
+               return "MOD_MANUF0";
+       case CSR_PSKEY_MOD_MANUF1:
+               return "MOD_MANUF1";
+       case CSR_PSKEY_MOD_MANUF2:
+               return "MOD_MANUF2";
+       case CSR_PSKEY_MOD_MANUF3:
+               return "MOD_MANUF3";
+       case CSR_PSKEY_MOD_MANUF4:
+               return "MOD_MANUF4";
+       case CSR_PSKEY_MOD_MANUF5:
+               return "MOD_MANUF5";
+       case CSR_PSKEY_MOD_MANUF6:
+               return "MOD_MANUF6";
+       case CSR_PSKEY_MOD_MANUF7:
+               return "MOD_MANUF7";
+       case CSR_PSKEY_MOD_MANUF8:
+               return "MOD_MANUF8";
+       case CSR_PSKEY_MOD_MANUF9:
+               return "MOD_MANUF9";
+       case CSR_PSKEY_DUT_VM_DISABLE:
+               return "DUT_VM_DISABLE";
+       case CSR_PSKEY_USR0:
+               return "USR0";
+       case CSR_PSKEY_USR1:
+               return "USR1";
+       case CSR_PSKEY_USR2:
+               return "USR2";
+       case CSR_PSKEY_USR3:
+               return "USR3";
+       case CSR_PSKEY_USR4:
+               return "USR4";
+       case CSR_PSKEY_USR5:
+               return "USR5";
+       case CSR_PSKEY_USR6:
+               return "USR6";
+       case CSR_PSKEY_USR7:
+               return "USR7";
+       case CSR_PSKEY_USR8:
+               return "USR8";
+       case CSR_PSKEY_USR9:
+               return "USR9";
+       case CSR_PSKEY_USR10:
+               return "USR10";
+       case CSR_PSKEY_USR11:
+               return "USR11";
+       case CSR_PSKEY_USR12:
+               return "USR12";
+       case CSR_PSKEY_USR13:
+               return "USR13";
+       case CSR_PSKEY_USR14:
+               return "USR14";
+       case CSR_PSKEY_USR15:
+               return "USR15";
+       case CSR_PSKEY_USR16:
+               return "USR16";
+       case CSR_PSKEY_USR17:
+               return "USR17";
+       case CSR_PSKEY_USR18:
+               return "USR18";
+       case CSR_PSKEY_USR19:
+               return "USR19";
+       case CSR_PSKEY_USR20:
+               return "USR20";
+       case CSR_PSKEY_USR21:
+               return "USR21";
+       case CSR_PSKEY_USR22:
+               return "USR22";
+       case CSR_PSKEY_USR23:
+               return "USR23";
+       case CSR_PSKEY_USR24:
+               return "USR24";
+       case CSR_PSKEY_USR25:
+               return "USR25";
+       case CSR_PSKEY_USR26:
+               return "USR26";
+       case CSR_PSKEY_USR27:
+               return "USR27";
+       case CSR_PSKEY_USR28:
+               return "USR28";
+       case CSR_PSKEY_USR29:
+               return "USR29";
+       case CSR_PSKEY_USR30:
+               return "USR30";
+       case CSR_PSKEY_USR31:
+               return "USR31";
+       case CSR_PSKEY_USR32:
+               return "USR32";
+       case CSR_PSKEY_USR33:
+               return "USR33";
+       case CSR_PSKEY_USR34:
+               return "USR34";
+       case CSR_PSKEY_USR35:
+               return "USR35";
+       case CSR_PSKEY_USR36:
+               return "USR36";
+       case CSR_PSKEY_USR37:
+               return "USR37";
+       case CSR_PSKEY_USR38:
+               return "USR38";
+       case CSR_PSKEY_USR39:
+               return "USR39";
+       case CSR_PSKEY_USR40:
+               return "USR40";
+       case CSR_PSKEY_USR41:
+               return "USR41";
+       case CSR_PSKEY_USR42:
+               return "USR42";
+       case CSR_PSKEY_USR43:
+               return "USR43";
+       case CSR_PSKEY_USR44:
+               return "USR44";
+       case CSR_PSKEY_USR45:
+               return "USR45";
+       case CSR_PSKEY_USR46:
+               return "USR46";
+       case CSR_PSKEY_USR47:
+               return "USR47";
+       case CSR_PSKEY_USR48:
+               return "USR48";
+       case CSR_PSKEY_USR49:
+               return "USR49";
+       case CSR_PSKEY_USB_VERSION:
+               return "USB_VERSION";
+       case CSR_PSKEY_USB_DEVICE_CLASS_CODES:
+               return "USB_DEVICE_CLASS_CODES";
+       case CSR_PSKEY_USB_VENDOR_ID:
+               return "USB_VENDOR_ID";
+       case CSR_PSKEY_USB_PRODUCT_ID:
+               return "USB_PRODUCT_ID";
+       case CSR_PSKEY_USB_MANUF_STRING:
+               return "USB_MANUF_STRING";
+       case CSR_PSKEY_USB_PRODUCT_STRING:
+               return "USB_PRODUCT_STRING";
+       case CSR_PSKEY_USB_SERIAL_NUMBER_STRING:
+               return "USB_SERIAL_NUMBER_STRING";
+       case CSR_PSKEY_USB_CONFIG_STRING:
+               return "USB_CONFIG_STRING";
+       case CSR_PSKEY_USB_ATTRIBUTES:
+               return "USB_ATTRIBUTES";
+       case CSR_PSKEY_USB_MAX_POWER:
+               return "USB_MAX_POWER";
+       case CSR_PSKEY_USB_BT_IF_CLASS_CODES:
+               return "USB_BT_IF_CLASS_CODES";
+       case CSR_PSKEY_USB_LANGID:
+               return "USB_LANGID";
+       case CSR_PSKEY_USB_DFU_CLASS_CODES:
+               return "USB_DFU_CLASS_CODES";
+       case CSR_PSKEY_USB_DFU_PRODUCT_ID:
+               return "USB_DFU_PRODUCT_ID";
+       case CSR_PSKEY_USB_PIO_DETACH:
+               return "USB_PIO_DETACH";
+       case CSR_PSKEY_USB_PIO_WAKEUP:
+               return "USB_PIO_WAKEUP";
+       case CSR_PSKEY_USB_PIO_PULLUP:
+               return "USB_PIO_PULLUP";
+       case CSR_PSKEY_USB_PIO_VBUS:
+               return "USB_PIO_VBUS";
+       case CSR_PSKEY_USB_PIO_WAKE_TIMEOUT:
+               return "USB_PIO_WAKE_TIMEOUT";
+       case CSR_PSKEY_USB_PIO_RESUME:
+               return "USB_PIO_RESUME";
+       case CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES:
+               return "USB_BT_SCO_IF_CLASS_CODES";
+       case CSR_PSKEY_USB_SUSPEND_PIO_LEVEL:
+               return "USB_SUSPEND_PIO_LEVEL";
+       case CSR_PSKEY_USB_SUSPEND_PIO_DIR:
+               return "USB_SUSPEND_PIO_DIR";
+       case CSR_PSKEY_USB_SUSPEND_PIO_MASK:
+               return "USB_SUSPEND_PIO_MASK";
+       case CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE:
+               return "USB_ENDPOINT_0_MAX_PACKET_SIZE";
+       case CSR_PSKEY_USB_CONFIG:
+               return "USB_CONFIG";
+       case CSR_PSKEY_RADIOTEST_ATTEN_INIT:
+               return "RADIOTEST_ATTEN_INIT";
+       case CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME:
+               return "RADIOTEST_FIRST_TRIM_TIME";
+       case CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME:
+               return "RADIOTEST_SUBSEQUENT_TRIM_TIME";
+       case CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE:
+               return "RADIOTEST_LO_LVL_TRIM_ENABLE";
+       case CSR_PSKEY_RADIOTEST_DISABLE_MODULATION:
+               return "RADIOTEST_DISABLE_MODULATION";
+       case CSR_PSKEY_RFCOMM_FCON_THRESHOLD:
+               return "RFCOMM_FCON_THRESHOLD";
+       case CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD:
+               return "RFCOMM_FCOFF_THRESHOLD";
+       case CSR_PSKEY_IPV6_STATIC_ADDR:
+               return "IPV6_STATIC_ADDR";
+       case CSR_PSKEY_IPV4_STATIC_ADDR:
+               return "IPV4_STATIC_ADDR";
+       case CSR_PSKEY_IPV6_STATIC_PREFIX_LEN:
+               return "IPV6_STATIC_PREFIX_LEN";
+       case CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR:
+               return "IPV6_STATIC_ROUTER_ADDR";
+       case CSR_PSKEY_IPV4_STATIC_SUBNET_MASK:
+               return "IPV4_STATIC_SUBNET_MASK";
+       case CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR:
+               return "IPV4_STATIC_ROUTER_ADDR";
+       case CSR_PSKEY_MDNS_NAME:
+               return "MDNS_NAME";
+       case CSR_PSKEY_FIXED_PIN:
+               return "FIXED_PIN";
+       case CSR_PSKEY_MDNS_PORT:
+               return "MDNS_PORT";
+       case CSR_PSKEY_MDNS_TTL:
+               return "MDNS_TTL";
+       case CSR_PSKEY_MDNS_IPV4_ADDR:
+               return "MDNS_IPV4_ADDR";
+       case CSR_PSKEY_ARP_CACHE_TIMEOUT:
+               return "ARP_CACHE_TIMEOUT";
+       case CSR_PSKEY_HFP_POWER_TABLE:
+               return "HFP_POWER_TABLE";
+       case CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS:
+               return "DRAIN_BORE_TIMER_COUNTERS";
+       case CSR_PSKEY_DRAIN_BORE_COUNTERS:
+               return "DRAIN_BORE_COUNTERS";
+       case CSR_PSKEY_LOOP_FILTER_TRIM:
+               return "LOOP_FILTER_TRIM";
+       case CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK:
+               return "DRAIN_BORE_CURRENT_PEAK";
+       case CSR_PSKEY_VM_E2_CACHE_LIMIT:
+               return "VM_E2_CACHE_LIMIT";
+       case CSR_PSKEY_FORCE_16MHZ_REF_PIO:
+               return "FORCE_16MHZ_REF_PIO";
+       case CSR_PSKEY_CDMA_LO_REF_LIMITS:
+               return "CDMA_LO_REF_LIMITS";
+       case CSR_PSKEY_CDMA_LO_ERROR_LIMITS:
+               return "CDMA_LO_ERROR_LIMITS";
+       case CSR_PSKEY_CLOCK_STARTUP_DELAY:
+               return "CLOCK_STARTUP_DELAY";
+       case CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR:
+               return "DEEP_SLEEP_CORRECTION_FACTOR";
+       case CSR_PSKEY_TEMPERATURE_CALIBRATION:
+               return "TEMPERATURE_CALIBRATION";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA:
+               return "TEMPERATURE_VS_DELTA_INTERNAL_PA";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL:
+               return "TEMPERATURE_VS_DELTA_TX_PRE_LVL";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB:
+               return "TEMPERATURE_VS_DELTA_TX_BB";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM:
+               return "TEMPERATURE_VS_DELTA_ANA_FTRIM";
+       case CSR_PSKEY_TEST_DELTA_OFFSET:
+               return "TEST_DELTA_OFFSET";
+       case CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET:
+               return "RX_DYNAMIC_LVL_OFFSET";
+       case CSR_PSKEY_TEST_FORCE_OFFSET:
+               return "TEST_FORCE_OFFSET";
+       case CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS:
+               return "RF_TRAP_BAD_DIVISION_RATIOS";
+       case CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS:
+               return "RADIOTEST_CDMA_LO_REF_LIMITS";
+       case CSR_PSKEY_INITIAL_BOOTMODE:
+               return "INITIAL_BOOTMODE";
+       case CSR_PSKEY_ONCHIP_HCI_CLIENT:
+               return "ONCHIP_HCI_CLIENT";
+       case CSR_PSKEY_RX_ATTEN_BACKOFF:
+               return "RX_ATTEN_BACKOFF";
+       case CSR_PSKEY_RX_ATTEN_UPDATE_RATE:
+               return "RX_ATTEN_UPDATE_RATE";
+       case CSR_PSKEY_SYNTH_TXRX_THRESHOLDS:
+               return "SYNTH_TXRX_THRESHOLDS";
+       case CSR_PSKEY_MIN_WAIT_STATES:
+               return "MIN_WAIT_STATES";
+       case CSR_PSKEY_RSSI_CORRECTION:
+               return "RSSI_CORRECTION";
+       case CSR_PSKEY_SCHED_THROTTLE_TIMEOUT:
+               return "SCHED_THROTTLE_TIMEOUT";
+       case CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK:
+               return "DEEP_SLEEP_USE_EXTERNAL_CLOCK";
+       case CSR_PSKEY_TRIM_RADIO_FILTERS:
+               return "TRIM_RADIO_FILTERS";
+       case CSR_PSKEY_TRANSMIT_OFFSET:
+               return "TRANSMIT_OFFSET";
+       case CSR_PSKEY_USB_VM_CONTROL:
+               return "USB_VM_CONTROL";
+       case CSR_PSKEY_MR_ANA_RX_FTRIM:
+               return "MR_ANA_RX_FTRIM";
+       case CSR_PSKEY_I2C_CONFIG:
+               return "I2C_CONFIG";
+       case CSR_PSKEY_IQ_LVL_RX:
+               return "IQ_LVL_RX";
+       case CSR_PSKEY_MR_TX_FILTER_CONFIG:
+               return "MR_TX_FILTER_CONFIG";
+       case CSR_PSKEY_MR_TX_CONFIG2:
+               return "MR_TX_CONFIG2";
+       case CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET:
+               return "USB_DONT_RESET_BOOTMODE_ON_HOST_RESET";
+       case CSR_PSKEY_LC_USE_THROTTLING:
+               return "LC_USE_THROTTLING";
+       case CSR_PSKEY_CHARGER_TRIM:
+               return "CHARGER_TRIM";
+       case CSR_PSKEY_CLOCK_REQUEST_FEATURES:
+               return "CLOCK_REQUEST_FEATURES";
+       case CSR_PSKEY_TRANSMIT_OFFSET_CLASS1:
+               return "TRANSMIT_OFFSET_CLASS1";
+       case CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO:
+               return "TX_AVOID_PA_CLASS1_PIO";
+       case CSR_PSKEY_MR_PIO_CONFIG:
+               return "MR_PIO_CONFIG";
+       case CSR_PSKEY_UART_CONFIG2:
+               return "UART_CONFIG2";
+       case CSR_PSKEY_CLASS1_IQ_LVL:
+               return "CLASS1_IQ_LVL";
+       case CSR_PSKEY_CLASS1_TX_CONFIG2:
+               return "CLASS1_TX_CONFIG2";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1:
+               return "TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1:
+               return "TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR:
+               return "TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER:
+               return "TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER";
+       case CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD:
+               return "TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD";
+       case CSR_PSKEY_RX_MR_EQ_TAPS:
+               return "RX_MR_EQ_TAPS";
+       case CSR_PSKEY_TX_PRE_LVL_CLASS1:
+               return "TX_PRE_LVL_CLASS1";
+       case CSR_PSKEY_ANALOGUE_ATTENUATOR:
+               return "ANALOGUE_ATTENUATOR";
+       case CSR_PSKEY_MR_RX_FILTER_TRIM:
+               return "MR_RX_FILTER_TRIM";
+       case CSR_PSKEY_MR_RX_FILTER_RESPONSE:
+               return "MR_RX_FILTER_RESPONSE";
+       case CSR_PSKEY_PIO_WAKEUP_STATE:
+               return "PIO_WAKEUP_STATE";
+       case CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP:
+               return "MR_TX_IF_ATTEN_OFF_TEMP";
+       case CSR_PSKEY_LO_DIV_LATCH_BYPASS:
+               return "LO_DIV_LATCH_BYPASS";
+       case CSR_PSKEY_LO_VCO_STANDBY:
+               return "LO_VCO_STANDBY";
+       case CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT:
+               return "SLOW_CLOCK_FILTER_SHIFT";
+       case CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER:
+               return "SLOW_CLOCK_FILTER_DIVIDER";
+       case CSR_PSKEY_USB_ATTRIBUTES_POWER:
+               return "USB_ATTRIBUTES_POWER";
+       case CSR_PSKEY_USB_ATTRIBUTES_WAKEUP:
+               return "USB_ATTRIBUTES_WAKEUP";
+       case CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT:
+               return "DFU_ATTRIBUTES_MANIFESTATION_TOLERANT";
+       case CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD:
+               return "DFU_ATTRIBUTES_CAN_UPLOAD";
+       case CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD:
+               return "DFU_ATTRIBUTES_CAN_DOWNLOAD";
+       case CSR_PSKEY_UART_CONFIG_STOP_BITS:
+               return "UART_CONFIG_STOP_BITS";
+       case CSR_PSKEY_UART_CONFIG_PARITY_BIT:
+               return "UART_CONFIG_PARITY_BIT";
+       case CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN:
+               return "UART_CONFIG_FLOW_CTRL_EN";
+       case CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN:
+               return "UART_CONFIG_RTS_AUTO_EN";
+       case CSR_PSKEY_UART_CONFIG_RTS:
+               return "UART_CONFIG_RTS";
+       case CSR_PSKEY_UART_CONFIG_TX_ZERO_EN:
+               return "UART_CONFIG_TX_ZERO_EN";
+       case CSR_PSKEY_UART_CONFIG_NON_BCSP_EN:
+               return "UART_CONFIG_NON_BCSP_EN";
+       case CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY:
+               return "UART_CONFIG_RX_RATE_DELAY";
+       case CSR_PSKEY_UART_SEQ_TIMEOUT:
+               return "UART_SEQ_TIMEOUT";
+       case CSR_PSKEY_UART_SEQ_RETRIES:
+               return "UART_SEQ_RETRIES";
+       case CSR_PSKEY_UART_SEQ_WINSIZE:
+               return "UART_SEQ_WINSIZE";
+       case CSR_PSKEY_UART_USE_CRC_ON_TX:
+               return "UART_USE_CRC_ON_TX";
+       case CSR_PSKEY_UART_HOST_INITIAL_STATE:
+               return "UART_HOST_INITIAL_STATE";
+       case CSR_PSKEY_UART_HOST_ATTENTION_SPAN:
+               return "UART_HOST_ATTENTION_SPAN";
+       case CSR_PSKEY_UART_HOST_WAKEUP_TIME:
+               return "UART_HOST_WAKEUP_TIME";
+       case CSR_PSKEY_UART_HOST_WAKEUP_WAIT:
+               return "UART_HOST_WAKEUP_WAIT";
+       case CSR_PSKEY_BCSP_LM_MODE:
+               return "BCSP_LM_MODE";
+       case CSR_PSKEY_BCSP_LM_SYNC_RETRIES:
+               return "BCSP_LM_SYNC_RETRIES";
+       case CSR_PSKEY_BCSP_LM_TSHY:
+               return "BCSP_LM_TSHY";
+       case CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS:
+               return "UART_DFU_CONFIG_STOP_BITS";
+       case CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT:
+               return "UART_DFU_CONFIG_PARITY_BIT";
+       case CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN:
+               return "UART_DFU_CONFIG_FLOW_CTRL_EN";
+       case CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN:
+               return "UART_DFU_CONFIG_RTS_AUTO_EN";
+       case CSR_PSKEY_UART_DFU_CONFIG_RTS:
+               return "UART_DFU_CONFIG_RTS";
+       case CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN:
+               return "UART_DFU_CONFIG_TX_ZERO_EN";
+       case CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN:
+               return "UART_DFU_CONFIG_NON_BCSP_EN";
+       case CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY:
+               return "UART_DFU_CONFIG_RX_RATE_DELAY";
+       case CSR_PSKEY_AMUX_AIO0:
+               return "AMUX_AIO0";
+       case CSR_PSKEY_AMUX_AIO1:
+               return "AMUX_AIO1";
+       case CSR_PSKEY_AMUX_AIO2:
+               return "AMUX_AIO2";
+       case CSR_PSKEY_AMUX_AIO3:
+               return "AMUX_AIO3";
+       case CSR_PSKEY_LOCAL_NAME_SIMPLIFIED:
+               return "LOCAL_NAME_SIMPLIFIED";
+       case CSR_PSKEY_EXTENDED_STUB:
+               return "EXTENDED_STUB";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+int csr_write_varid_valueless(int dd, uint16_t seqnum, uint16_t varid)
+{
+       unsigned char cmd[] = { 0x02, 0x00, 0x09, 0x00,
+                               seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       unsigned char cp[254], rp[254];
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp[0] = 0xc2;
+       memcpy(cp + 1, cmd, sizeof(cmd));
+
+       switch (varid) {
+       case CSR_VARID_COLD_RESET:
+       case CSR_VARID_WARM_RESET:
+       case CSR_VARID_COLD_HALT:
+       case CSR_VARID_WARM_HALT:
+               return hci_send_cmd(dd, OGF_VENDOR_CMD, 0x00, sizeof(cmd) + 1, cp);
+       }
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x00;
+       rq.event  = EVT_VENDOR;
+       rq.cparam = cp;
+       rq.clen   = sizeof(cmd) + 1;
+       rq.rparam = rp;
+       rq.rlen   = sizeof(rp);
+
+       if (hci_send_req(dd, &rq, 2000) < 0)
+               return -1;
+
+       if (rp[0] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[9] + (rp[10] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int csr_write_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+       unsigned char cmd[] = { 0x02, 0x00, ((length / 2) + 5) & 0xff, ((length / 2) + 5) >> 8,
+                               seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       unsigned char cp[254], rp[254];
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp[0] = 0xc2;
+       memcpy(cp + 1, cmd, sizeof(cmd));
+       memcpy(cp + 11, value, length);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x00;
+       rq.event  = EVT_VENDOR;
+       rq.cparam = cp;
+       rq.clen   = sizeof(cmd) + length + 1;
+       rq.rparam = rp;
+       rq.rlen   = sizeof(rp);
+
+       if (hci_send_req(dd, &rq, 2000) < 0)
+               return -1;
+
+       if (rp[0] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[9] + (rp[10] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int csr_read_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+       unsigned char cmd[] = { 0x00, 0x00, ((length / 2) + 5) & 0xff, ((length / 2) + 5) >> 8,
+                               seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       unsigned char cp[254], rp[254];
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp[0] = 0xc2;
+       memcpy(cp + 1, cmd, sizeof(cmd));
+       memcpy(cp + 11, value, length);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x00;
+       rq.event  = EVT_VENDOR;
+       rq.cparam = cp;
+       rq.clen   = sizeof(cmd) + length + 1;
+       rq.rparam = rp;
+       rq.rlen   = sizeof(rp);
+
+       if (hci_send_req(dd, &rq, 2000) < 0)
+               return -1;
+
+       if (rp[0] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[9] + (rp[10] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       memcpy(value, rp + 11, length);
+
+       return 0;
+}
+
+int csr_read_varid_uint16(int dd, uint16_t seqnum, uint16_t varid, uint16_t *value)
+{
+       unsigned char cmd[] = { 0x00, 0x00, 0x09, 0x00,
+                               seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       unsigned char cp[254], rp[254];
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp[0] = 0xc2;
+       memcpy(cp + 1, cmd, sizeof(cmd));
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x00;
+       rq.event  = EVT_VENDOR;
+       rq.cparam = cp;
+       rq.clen   = sizeof(cmd) + 1;
+       rq.rparam = rp;
+       rq.rlen   = sizeof(rp);
+
+       if (hci_send_req(dd, &rq, 2000) < 0)
+               return -1;
+
+       if (rp[0] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[9] + (rp[10] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       *value = rp[11] + (rp[12] << 8);
+
+       return 0;
+}
+
+int csr_read_varid_uint32(int dd, uint16_t seqnum, uint16_t varid, uint32_t *value)
+{
+       unsigned char cmd[] = { 0x00, 0x00, 0x09, 0x00,
+                               seqnum & 0xff, seqnum >> 8, varid & 0xff, varid >> 8, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       unsigned char cp[254], rp[254];
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp[0] = 0xc2;
+       memcpy(cp + 1, cmd, sizeof(cmd));
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x00;
+       rq.event  = EVT_VENDOR;
+       rq.cparam = cp;
+       rq.clen   = sizeof(cmd) + 1;
+       rq.rparam = rp;
+       rq.rlen   = sizeof(rp);
+
+       if (hci_send_req(dd, &rq, 2000) < 0)
+               return -1;
+
+       if (rp[0] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[9] + (rp[10] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       *value = ((rp[11] + (rp[12] << 8)) << 16) + (rp[13] + (rp[14] << 8));
+
+       return 0;
+}
+
+int csr_read_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length)
+{
+       unsigned char cmd[] = { 0x00, 0x00, ((length / 2) + 8) & 0xff, ((length / 2) + 8) >> 8,
+                               seqnum & 0xff, seqnum >> 8, 0x03, 0x70, 0x00, 0x00,
+                               pskey & 0xff, pskey >> 8,
+                               (length / 2) & 0xff, (length / 2) >> 8,
+                               stores & 0xff, stores >> 8, 0x00, 0x00 };
+
+       unsigned char cp[254], rp[254];
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp[0] = 0xc2;
+       memcpy(cp + 1, cmd, sizeof(cmd));
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x00;
+       rq.event  = EVT_VENDOR;
+       rq.cparam = cp;
+       rq.clen   = sizeof(cmd) + length - 1;
+       rq.rparam = rp;
+       rq.rlen   = sizeof(rp);
+
+       if (hci_send_req(dd, &rq, 2000) < 0)
+               return -1;
+
+       if (rp[0] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[9] + (rp[10] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       memcpy(value, rp + 17, length);
+
+       return 0;
+}
+
+int csr_write_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length)
+{
+       unsigned char cmd[] = { 0x02, 0x00, ((length / 2) + 8) & 0xff, ((length / 2) + 8) >> 8,
+                               seqnum & 0xff, seqnum >> 8, 0x03, 0x70, 0x00, 0x00,
+                               pskey & 0xff, pskey >> 8,
+                               (length / 2) & 0xff, (length / 2) >> 8,
+                               stores & 0xff, stores >> 8, 0x00, 0x00 };
+
+       unsigned char cp[254], rp[254];
+       struct hci_request rq;
+
+       memset(&cp, 0, sizeof(cp));
+       cp[0] = 0xc2;
+       memcpy(cp + 1, cmd, sizeof(cmd));
+
+       memcpy(cp + 17, value, length);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x00;
+       rq.event  = EVT_VENDOR;
+       rq.cparam = cp;
+       rq.clen   = sizeof(cmd) + length - 1;
+       rq.rparam = rp;
+       rq.rlen   = sizeof(rp);
+
+       if (hci_send_req(dd, &rq, 2000) < 0)
+               return -1;
+
+       if (rp[0] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[9] + (rp[10] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+int csr_read_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t *value)
+{
+       uint8_t array[2] = { 0x00, 0x00 };
+       int err;
+
+       err = csr_read_pskey_complex(dd, seqnum, pskey, stores, array, 2);
+
+       *value = array[0] + (array[1] << 8);
+
+       return err;
+}
+
+int csr_write_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t value)
+{
+       uint8_t array[2] = { value & 0xff, value >> 8 };
+
+       return csr_write_pskey_complex(dd, seqnum, pskey, stores, array, 2);
+}
+
+int csr_read_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t *value)
+{
+       uint8_t array[4] = { 0x00, 0x00, 0x00, 0x00 };
+       int err;
+
+       err = csr_read_pskey_complex(dd, seqnum, pskey, stores, array, 4);
+
+       *value = ((array[0] + (array[1] << 8)) << 16) +
+                                               (array[2] + (array[3] << 8));
+
+       return err;
+}
+
+int csr_write_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t value)
+{
+       uint8_t array[4] = { (value & 0xff0000) >> 16, value >> 24,
+                                       value & 0xff, (value & 0xff00) >> 8 };
+
+       return csr_write_pskey_complex(dd, seqnum, pskey, stores, array, 4);
+}
+
+int psr_put(uint16_t pskey, uint8_t *value, uint16_t size)
+{
+       struct psr_data *item;
+
+       item = malloc(sizeof(*item));
+       if (!item)
+               return -ENOMEM;
+
+       item->pskey = pskey;
+
+       if (size > 0) {
+               item->value = malloc(size);
+               if (!item->value) {
+                       free(item);
+                       return -ENOMEM;
+               }
+
+               memcpy(item->value, value, size);
+               item->size = size;
+       } else {
+               item->value = NULL;
+               item->size = 0;
+       }
+
+       item->next = NULL;
+
+       if (!head)
+               head = item;
+       else
+               tail->next = item;
+
+       tail = item;
+
+       return 0;
+}
+
+int psr_get(uint16_t *pskey, uint8_t *value, uint16_t *size)
+{
+       struct psr_data *item = head;
+
+       if (!head)
+               return -ENOENT;
+
+       *pskey = item->pskey;
+
+       if (item->value) {
+               if (value && item->size > 0)
+                       memcpy(value, item->value, item->size);
+               free(item->value);
+               *size = item->size;
+       } else
+               *size = 0;
+
+       if (head == tail)
+               tail = NULL;
+
+       head = head->next;
+       free(item);
+
+       return 0;
+}
+
+static int parse_line(char *str)
+{
+       uint8_t array[256];
+       uint16_t value, pskey, length = 0;
+       char *off, *end;
+
+       pskey = strtol(str + 1, NULL, 16);
+       off = strstr(str, "=") + 1;
+       if (!off)
+               return -EIO;
+
+       while (1) {
+               value = strtol(off, &end, 16);
+               if (value == 0 && off == end)
+                       break;
+
+               array[length++] = value & 0xff;
+               array[length++] = value >> 8;
+
+               if (*end == '\0')
+                       break;
+
+               off = end + 1;
+       }
+
+       return psr_put(pskey, array, length);
+}
+
+int psr_read(const char *filename)
+{
+       struct stat st;
+       char *str, *map, *off, *end;
+       int fd, err = 0;
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0)
+               return fd;
+
+       if (fstat(fd, &st) < 0) {
+               err = -errno;
+               goto close;
+       }
+
+       map = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+       if (!map || map == MAP_FAILED) {
+               err = -errno;
+               goto close;
+       }
+
+       off = map;
+
+       while (1) {
+               if (*off == '\r' || *off == '\n') {
+                       off++;
+                       continue;
+               }
+
+               end = strpbrk(off, "\r\n");
+               if (!end)
+                       break;
+
+               str = malloc(end - off + 1);
+               if (!str)
+                       break;
+
+               memset(str, 0, end - off + 1);
+               strncpy(str, off, end - off);
+               if (*str == '&')
+                       parse_line(str);
+
+               free(str);
+               off = end + 1;
+       }
+
+       munmap(map, st.st_size);
+
+close:
+       close(fd);
+
+       return err;
+}
+
+int psr_print(void)
+{
+       uint8_t array[256];
+       uint16_t pskey, length;
+       char *str, val[7];
+       int i;
+
+       while (1) {
+               if (psr_get(&pskey, array, &length) < 0)
+                       break;
+
+               str = csr_pskeytoval(pskey);
+               if (!strcasecmp(str, "UNKNOWN")) {
+                       sprintf(val, "0x%04x", pskey);
+                       str = NULL;
+               }
+
+               printf("// %s%s\n&%04x =", str ? "PSKEY_" : "",
+                                               str ? str : val, pskey);
+               for (i = 0; i < length / 2; i++)
+                       printf(" %02x%02x", array[i * 2 + 1], array[i * 2]);
+               printf("\n");
+       }
+
+       return 0;
+}
diff --git a/tools/csr.h b/tools/csr.h
new file mode 100644 (file)
index 0000000..8b94d7b
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+#include <termios.h>
+
+#define CSR_VARID_PS_CLR_ALL                   0x000b  /* valueless */
+#define CSR_VARID_PS_FACTORY_SET               0x000c  /* valueless */
+#define CSR_VARID_PS_CLR_ALL_STORES            0x082d  /* uint16 */
+#define CSR_VARID_BC01_STATUS                  0x2801  /* uint16 */
+#define CSR_VARID_BUILDID                      0x2819  /* uint16 */
+#define CSR_VARID_CHIPVER                      0x281a  /* uint16 */
+#define CSR_VARID_CHIPREV                      0x281b  /* uint16 */
+#define CSR_VARID_INTERFACE_VERSION            0x2825  /* uint16 */
+#define CSR_VARID_RAND                         0x282a  /* uint16 */
+#define CSR_VARID_MAX_CRYPT_KEY_LENGTH         0x282c  /* uint16 */
+#define CSR_VARID_CHIPANAREV                   0x2836  /* uint16 */
+#define CSR_VARID_BUILDID_LOADER               0x2838  /* uint16 */
+#define CSR_VARID_BT_CLOCK                     0x2c00  /* uint32 */
+#define CSR_VARID_PS_NEXT                      0x3005  /* complex */
+#define CSR_VARID_PS_SIZE                      0x3006  /* complex */
+#define CSR_VARID_CRYPT_KEY_LENGTH             0x3008  /* complex */
+#define CSR_VARID_PICONET_INSTANCE             0x3009  /* complex */
+#define CSR_VARID_GET_CLR_EVT                  0x300a  /* complex */
+#define CSR_VARID_GET_NEXT_BUILDDEF            0x300b  /* complex */
+#define CSR_VARID_PS_MEMORY_TYPE               0x3012  /* complex */
+#define CSR_VARID_READ_BUILD_NAME              0x301c  /* complex */
+#define CSR_VARID_COLD_RESET                   0x4001  /* valueless */
+#define CSR_VARID_WARM_RESET                   0x4002  /* valueless */
+#define CSR_VARID_COLD_HALT                    0x4003  /* valueless */
+#define CSR_VARID_WARM_HALT                    0x4004  /* valueless */
+#define CSR_VARID_INIT_BT_STACK                        0x4005  /* valueless */
+#define CSR_VARID_ACTIVATE_BT_STACK            0x4006  /* valueless */
+#define CSR_VARID_ENABLE_TX                    0x4007  /* valueless */
+#define CSR_VARID_DISABLE_TX                   0x4008  /* valueless */
+#define CSR_VARID_RECAL                                0x4009  /* valueless */
+#define CSR_VARID_PS_FACTORY_RESTORE           0x400d  /* valueless */
+#define CSR_VARID_PS_FACTORY_RESTORE_ALL       0x400e  /* valueless */
+#define CSR_VARID_PS_DEFRAG_RESET              0x400f  /* valueless */
+#define CSR_VARID_KILL_VM_APPLICATION          0x4010  /* valueless */
+#define CSR_VARID_HOPPING_ON                   0x4011  /* valueless */
+#define CSR_VARID_CANCEL_PAGE                  0x4012  /* valueless */
+#define CSR_VARID_PS_CLR                       0x4818  /* uint16 */
+#define CSR_VARID_MAP_SCO_PCM                  0x481c  /* uint16 */
+#define CSR_VARID_SINGLE_CHAN                  0x482e  /* uint16 */
+#define CSR_VARID_RADIOTEST                    0x5004  /* complex */
+#define CSR_VARID_PS_CLR_STORES                        0x500c  /* complex */
+#define CSR_VARID_NO_VARIABLE                  0x6000  /* valueless */
+#define CSR_VARID_CONFIG_UART                  0x6802  /* uint16 */
+#define CSR_VARID_PANIC_ARG                    0x6805  /* uint16 */
+#define CSR_VARID_FAULT_ARG                    0x6806  /* uint16 */
+#define CSR_VARID_MAX_TX_POWER                 0x6827  /* int8 */
+#define CSR_VARID_DEFAULT_TX_POWER             0x682b  /* int8 */
+#define CSR_VARID_PS                           0x7003  /* complex */
+
+#define CSR_PSKEY_BDADDR                                       0x0001  /* bdaddr / uint16[] = { 0x00A5A5, 0x5b, 0x0002 } */
+#define CSR_PSKEY_COUNTRYCODE                                  0x0002  /* uint16 */
+#define CSR_PSKEY_CLASSOFDEVICE                                        0x0003  /* bdcod */
+#define CSR_PSKEY_DEVICE_DRIFT                                 0x0004  /* uint16 */
+#define CSR_PSKEY_DEVICE_JITTER                                        0x0005  /* uint16 */
+#define CSR_PSKEY_MAX_ACLS                                     0x000d  /* uint16 */
+#define CSR_PSKEY_MAX_SCOS                                     0x000e  /* uint16 */
+#define CSR_PSKEY_MAX_REMOTE_MASTERS                           0x000f  /* uint16 */
+#define CSR_PSKEY_ENABLE_MASTERY_WITH_SLAVERY                  0x0010  /* bool */
+#define CSR_PSKEY_H_HC_FC_MAX_ACL_PKT_LEN                      0x0011  /* uint16 */
+#define CSR_PSKEY_H_HC_FC_MAX_SCO_PKT_LEN                      0x0012  /* uint8 */
+#define CSR_PSKEY_H_HC_FC_MAX_ACL_PKTS                         0x0013  /* uint16 */
+#define CSR_PSKEY_H_HC_FC_MAX_SCO_PKTS                         0x0014  /* uint16 */
+#define CSR_PSKEY_LC_FC_BUFFER_LOW_WATER_MARK                  0x0015  /* lc_fc_lwm */
+#define CSR_PSKEY_LC_MAX_TX_POWER                              0x0017  /* int16 */
+#define CSR_PSKEY_TX_GAIN_RAMP                                 0x001d  /* uint16 */
+#define CSR_PSKEY_LC_POWER_TABLE                               0x001e  /* power_setting[] */
+#define CSR_PSKEY_LC_PEER_POWER_PERIOD                         0x001f  /* TIME */
+#define CSR_PSKEY_LC_FC_POOLS_LOW_WATER_MARK                   0x0020  /* lc_fc_lwm */
+#define CSR_PSKEY_LC_DEFAULT_TX_POWER                          0x0021  /* int16 */
+#define CSR_PSKEY_LC_RSSI_GOLDEN_RANGE                         0x0022  /* uint8 */
+#define CSR_PSKEY_LC_COMBO_DISABLE_PIO_MASK                    0x0028  /* uint16[] */
+#define CSR_PSKEY_LC_COMBO_PRIORITY_PIO_MASK                   0x0029  /* uint16[] */
+#define CSR_PSKEY_LC_COMBO_DOT11_CHANNEL_PIO_BASE              0x002a  /* uint16 */
+#define CSR_PSKEY_LC_COMBO_DOT11_BLOCK_CHANNELS                        0x002b  /* uint16 */
+#define CSR_PSKEY_LC_MAX_TX_POWER_NO_RSSI                      0x002d  /* int8 */
+#define CSR_PSKEY_LC_CONNECTION_RX_WINDOW                      0x002e  /* uint16 */
+#define CSR_PSKEY_LC_COMBO_DOT11_TX_PROTECTION_MODE            0x0030  /* uint16 */
+#define CSR_PSKEY_LC_ENHANCED_POWER_TABLE                      0x0031  /* enhanced_power_setting[] */
+#define CSR_PSKEY_LC_WIDEBAND_RSSI_CONFIG                      0x0032  /* wideband_rssi_config */
+#define CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_LEAD                 0x0033  /* uint16 */
+#define CSR_PSKEY_BT_CLOCK_INIT                                        0x0034  /* uint32 */
+#define CSR_PSKEY_TX_MR_MOD_DELAY                              0x0038  /* uint8 */
+#define CSR_PSKEY_RX_MR_SYNC_TIMING                            0x0039  /* uint16 */
+#define CSR_PSKEY_RX_MR_SYNC_CONFIG                            0x003a  /* uint16 */
+#define CSR_PSKEY_LC_LOST_SYNC_SLOTS                           0x003b  /* uint16 */
+#define CSR_PSKEY_RX_MR_SAMP_CONFIG                            0x003c  /* uint16 */
+#define CSR_PSKEY_AGC_HYST_LEVELS                              0x003d  /* agc_hyst_config */
+#define CSR_PSKEY_RX_LEVEL_LOW_SIGNAL                          0x003e  /* uint16 */
+#define CSR_PSKEY_AGC_IQ_LVL_VALUES                            0x003f  /* IQ_LVL_VAL[] */
+#define CSR_PSKEY_MR_FTRIM_OFFSET_12DB                         0x0040  /* uint16 */
+#define CSR_PSKEY_MR_FTRIM_OFFSET_6DB                          0x0041  /* uint16 */
+#define CSR_PSKEY_NO_CAL_ON_BOOT                               0x0042  /* bool */
+#define CSR_PSKEY_RSSI_HI_TARGET                               0x0043  /* uint8 */
+#define CSR_PSKEY_PREFERRED_MIN_ATTENUATION                    0x0044  /* uint8 */
+#define CSR_PSKEY_LC_COMBO_DOT11_PRIORITY_OVERRIDE             0x0045  /* bool */
+#define CSR_PSKEY_LC_MULTISLOT_HOLDOFF                         0x0047  /* TIME */
+#define CSR_PSKEY_FREE_KEY_PIGEON_HOLE                         0x00c9  /* uint16 */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR0                            0x00ca  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR1                            0x00cb  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR2                            0x00cc  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR3                            0x00cd  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR4                            0x00ce  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR5                            0x00cf  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR6                            0x00d0  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR7                            0x00d1  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR8                            0x00d2  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR9                            0x00d3  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR10                           0x00d4  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR11                           0x00d5  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR12                           0x00d6  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR13                           0x00d7  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR14                           0x00d8  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LINK_KEY_BD_ADDR15                           0x00d9  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_ENC_KEY_LMIN                                 0x00da  /* uint16 */
+#define CSR_PSKEY_ENC_KEY_LMAX                                 0x00db  /* uint16 */
+#define CSR_PSKEY_LOCAL_SUPPORTED_FEATURES                     0x00ef  /* uint16[] = { 0xffff, 0xfe8f, 0xf99b, 0x8000 }*/
+#define CSR_PSKEY_LM_USE_UNIT_KEY                              0x00f0  /* bool */
+#define CSR_PSKEY_HCI_NOP_DISABLE                              0x00f2  /* bool */
+#define CSR_PSKEY_LM_MAX_EVENT_FILTERS                         0x00f4  /* uint8 */
+#define CSR_PSKEY_LM_USE_ENC_MODE_BROADCAST                    0x00f5  /* bool */
+#define CSR_PSKEY_LM_TEST_SEND_ACCEPTED_TWICE                  0x00f6  /* bool */
+#define CSR_PSKEY_LM_MAX_PAGE_HOLD_TIME                                0x00f7  /* uint16 */
+#define CSR_PSKEY_AFH_ADAPTATION_RESPONSE_TIME                 0x00f8  /* uint16 */
+#define CSR_PSKEY_AFH_OPTIONS                                  0x00f9  /* uint16 */
+#define CSR_PSKEY_AFH_RSSI_RUN_PERIOD                          0x00fa  /* uint16 */
+#define CSR_PSKEY_AFH_REENABLE_CHANNEL_TIME                    0x00fb  /* uint16 */
+#define CSR_PSKEY_NO_DROP_ON_ACR_MS_FAIL                       0x00fc  /* bool */
+#define CSR_PSKEY_MAX_PRIVATE_KEYS                             0x00fd  /* uint8 */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR0                    0x00fe  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR1                    0x00ff  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR2                    0x0100  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR3                    0x0101  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR4                    0x0102  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR5                    0x0103  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR6                    0x0104  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_PRIVATE_LINK_KEY_BD_ADDR7                    0x0105  /* LM_LINK_KEY_BD_ADDR_T */
+#define CSR_PSKEY_LOCAL_SUPPORTED_COMMANDS                     0x0106  /* uint16[] = { 0xffff, 0x03ff, 0xfffe, 0xffff, 0xffff, 0xffff, 0x0ff3, 0xfff8, 0x003f } */
+#define CSR_PSKEY_LM_MAX_ABSENCE_INDEX                         0x0107  /* uint8 */
+#define CSR_PSKEY_DEVICE_NAME                                  0x0108  /* uint16[] */
+#define CSR_PSKEY_AFH_RSSI_THRESHOLD                           0x0109  /* uint16 */
+#define CSR_PSKEY_LM_CASUAL_SCAN_INTERVAL                      0x010a  /* uint16 */
+#define CSR_PSKEY_AFH_MIN_MAP_CHANGE                           0x010b  /* uint16[] */
+#define CSR_PSKEY_AFH_RSSI_LP_RUN_PERIOD                       0x010c  /* uint16 */
+#define CSR_PSKEY_HCI_LMP_LOCAL_VERSION                                0x010d  /* uint16 */
+#define CSR_PSKEY_LMP_REMOTE_VERSION                           0x010e  /* uint8 */
+#define CSR_PSKEY_HOLD_ERROR_MESSAGE_NUMBER                    0x0113  /* uint16 */
+#define CSR_PSKEY_DFU_ATTRIBUTES                               0x0136  /* uint8 */
+#define CSR_PSKEY_DFU_DETACH_TO                                        0x0137  /* uint16 */
+#define CSR_PSKEY_DFU_TRANSFER_SIZE                            0x0138  /* uint16 */
+#define CSR_PSKEY_DFU_ENABLE                                   0x0139  /* bool */
+#define CSR_PSKEY_DFU_LIN_REG_ENABLE                           0x013a  /* bool */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_MSB                  0x015e  /* uint16[] */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_MODULUS_LSB                  0x015f  /* uint16[] */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_M_DASH                       0x0160  /* uint16 */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_R2N_MSB                      0x0161  /* uint16[] */
+#define CSR_PSKEY_DFUENC_VMAPP_PK_R2N_LSB                      0x0162  /* uint16[] */
+#define CSR_PSKEY_BCSP_LM_PS_BLOCK                             0x0192  /* BCSP_LM_PS_BLOCK */
+#define CSR_PSKEY_HOSTIO_FC_PS_BLOCK                           0x0193  /* HOSTIO_FC_PS_BLOCK */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO0                                0x0194  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO1                                0x0195  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO2                                0x0196  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO3                                0x0197  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO4                                0x0198  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO5                                0x0199  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO6                                0x019a  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO7                                0x019b  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO8                                0x019c  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO9                                0x019d  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO10                       0x019e  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO11                       0x019f  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO12                       0x01a0  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO13                       0x01a1  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO14                       0x01a2  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_PROTOCOL_INFO15                       0x01a3  /* PROTOCOL_INFO */
+#define CSR_PSKEY_HOSTIO_UART_RESET_TIMEOUT                    0x01a4  /* TIME */
+#define CSR_PSKEY_HOSTIO_USE_HCI_EXTN                          0x01a5  /* bool */
+#define CSR_PSKEY_HOSTIO_USE_HCI_EXTN_CCFC                     0x01a6  /* bool */
+#define CSR_PSKEY_HOSTIO_HCI_EXTN_PAYLOAD_SIZE                 0x01a7  /* uint16 */
+#define CSR_PSKEY_BCSP_LM_CNF_CNT_LIMIT                                0x01aa  /* uint16 */
+#define CSR_PSKEY_HOSTIO_MAP_SCO_PCM                           0x01ab  /* bool */
+#define CSR_PSKEY_HOSTIO_AWKWARD_PCM_SYNC                      0x01ac  /* bool */
+#define CSR_PSKEY_HOSTIO_BREAK_POLL_PERIOD                     0x01ad  /* TIME */
+#define CSR_PSKEY_HOSTIO_MIN_UART_HCI_SCO_SIZE                 0x01ae  /* uint16 */
+#define CSR_PSKEY_HOSTIO_MAP_SCO_CODEC                         0x01b0  /* bool */
+#define CSR_PSKEY_PCM_CVSD_TX_HI_FREQ_BOOST                    0x01b1  /* uint16 */
+#define CSR_PSKEY_PCM_CVSD_RX_HI_FREQ_BOOST                    0x01b2  /* uint16 */
+#define CSR_PSKEY_PCM_CONFIG32                                 0x01b3  /* uint32 */
+#define CSR_PSKEY_USE_OLD_BCSP_LE                              0x01b4  /* uint16 */
+#define CSR_PSKEY_PCM_CVSD_USE_NEW_FILTER                      0x01b5  /* bool */
+#define CSR_PSKEY_PCM_FORMAT                                   0x01b6  /* uint16 */
+#define CSR_PSKEY_CODEC_OUT_GAIN                               0x01b7  /* uint16 */
+#define CSR_PSKEY_CODEC_IN_GAIN                                        0x01b8  /* uint16 */
+#define CSR_PSKEY_CODEC_PIO                                    0x01b9  /* uint16 */
+#define CSR_PSKEY_PCM_LOW_JITTER_CONFIG                                0x01ba  /* uint32 */
+#define CSR_PSKEY_HOSTIO_SCO_PCM_THRESHOLDS                    0x01bb  /* uint16[] */
+#define CSR_PSKEY_HOSTIO_SCO_HCI_THRESHOLDS                    0x01bc  /* uint16[] */
+#define CSR_PSKEY_HOSTIO_MAP_SCO_PCM_SLOT                      0x01bd  /* uint16 */
+#define CSR_PSKEY_UART_BAUDRATE                                        0x01be  /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_BCSP                             0x01bf  /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_H4                               0x01c0  /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_H5                               0x01c1  /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_USR                              0x01c2  /* uint16 */
+#define CSR_PSKEY_UART_TX_CRCS                                 0x01c3  /* bool */
+#define CSR_PSKEY_UART_ACK_TIMEOUT                             0x01c4  /* uint16 */
+#define CSR_PSKEY_UART_TX_MAX_ATTEMPTS                         0x01c5  /* uint16 */
+#define CSR_PSKEY_UART_TX_WINDOW_SIZE                          0x01c6  /* uint16 */
+#define CSR_PSKEY_UART_HOST_WAKE                               0x01c7  /* uint16[] */
+#define CSR_PSKEY_HOSTIO_THROTTLE_TIMEOUT                      0x01c8  /* TIME */
+#define CSR_PSKEY_PCM_ALWAYS_ENABLE                            0x01c9  /* bool */
+#define CSR_PSKEY_UART_HOST_WAKE_SIGNAL                                0x01ca  /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_H4DS                             0x01cb  /* uint16 */
+#define CSR_PSKEY_H4DS_WAKE_DURATION                           0x01cc  /* uint16 */
+#define CSR_PSKEY_H4DS_MAXWU                                   0x01cd  /* uint16 */
+#define CSR_PSKEY_H4DS_LE_TIMER_PERIOD                         0x01cf  /* uint16 */
+#define CSR_PSKEY_H4DS_TWU_TIMER_PERIOD                                0x01d0  /* uint16 */
+#define CSR_PSKEY_H4DS_UART_IDLE_TIMER_PERIOD                  0x01d1  /* uint16 */
+#define CSR_PSKEY_ANA_FTRIM                                    0x01f6  /* uint16 */
+#define CSR_PSKEY_WD_TIMEOUT                                   0x01f7  /* TIME */
+#define CSR_PSKEY_WD_PERIOD                                    0x01f8  /* TIME */
+#define CSR_PSKEY_HOST_INTERFACE                               0x01f9  /* phys_bus */
+#define CSR_PSKEY_HQ_HOST_TIMEOUT                              0x01fb  /* TIME */
+#define CSR_PSKEY_HQ_ACTIVE                                    0x01fc  /* bool */
+#define CSR_PSKEY_BCCMD_SECURITY_ACTIVE                                0x01fd  /* bool */
+#define CSR_PSKEY_ANA_FREQ                                     0x01fe  /* uint16 */
+#define CSR_PSKEY_PIO_PROTECT_MASK                             0x0202  /* uint16 */
+#define CSR_PSKEY_PMALLOC_SIZES                                        0x0203  /* uint16[] */
+#define CSR_PSKEY_UART_BAUD_RATE                               0x0204  /* uint16 */
+#define CSR_PSKEY_UART_CONFIG                                  0x0205  /* uint16 */
+#define CSR_PSKEY_STUB                                         0x0207  /* uint16 */
+#define CSR_PSKEY_TXRX_PIO_CONTROL                             0x0209  /* uint16 */
+#define CSR_PSKEY_ANA_RX_LEVEL                                 0x020b  /* uint16 */
+#define CSR_PSKEY_ANA_RX_FTRIM                                 0x020c  /* uint16 */
+#define CSR_PSKEY_PSBC_DATA_VERSION                            0x020d  /* uint16 */
+#define CSR_PSKEY_PCM0_ATTENUATION                             0x020f  /* uint16 */
+#define CSR_PSKEY_LO_LVL_MAX                                   0x0211  /* uint16 */
+#define CSR_PSKEY_LO_ADC_AMPL_MIN                              0x0212  /* uint16 */
+#define CSR_PSKEY_LO_ADC_AMPL_MAX                              0x0213  /* uint16 */
+#define CSR_PSKEY_IQ_TRIM_CHANNEL                              0x0214  /* uint16 */
+#define CSR_PSKEY_IQ_TRIM_GAIN                                 0x0215  /* uint16 */
+#define CSR_PSKEY_IQ_TRIM_ENABLE                               0x0216  /* iq_trim_enable_flag */
+#define CSR_PSKEY_TX_OFFSET_HALF_MHZ                           0x0217  /* int16 */
+#define CSR_PSKEY_GBL_MISC_ENABLES                             0x0221  /* uint16 */
+#define CSR_PSKEY_UART_SLEEP_TIMEOUT                           0x0222  /* uint16 */
+#define CSR_PSKEY_DEEP_SLEEP_STATE                             0x0229  /* deep_sleep_state */
+#define CSR_PSKEY_IQ_ENABLE_PHASE_TRIM                         0x022d  /* bool */
+#define CSR_PSKEY_HCI_HANDLE_FREEZE_PERIOD                     0x0237  /* TIME */
+#define CSR_PSKEY_MAX_FROZEN_HCI_HANDLES                       0x0238  /* uint16 */
+#define CSR_PSKEY_PAGETABLE_DESTRUCTION_DELAY                  0x0239  /* TIME */
+#define CSR_PSKEY_IQ_TRIM_PIO_SETTINGS                         0x023a  /* uint8 */
+#define CSR_PSKEY_USE_EXTERNAL_CLOCK                           0x023b  /* bool */
+#define CSR_PSKEY_DEEP_SLEEP_WAKE_CTS                          0x023c  /* uint16 */
+#define CSR_PSKEY_FC_HC2H_FLUSH_DELAY                          0x023d  /* TIME */
+#define CSR_PSKEY_RX_HIGHSIDE                                  0x023e  /* bool */
+#define CSR_PSKEY_TX_PRE_LVL                                   0x0240  /* uint8 */
+#define CSR_PSKEY_RX_SINGLE_ENDED                              0x0242  /* bool */
+#define CSR_PSKEY_TX_FILTER_CONFIG                             0x0243  /* uint32 */
+#define CSR_PSKEY_CLOCK_REQUEST_ENABLE                         0x0246  /* uint16 */
+#define CSR_PSKEY_RX_MIN_ATTEN                                 0x0249  /* uint16 */
+#define CSR_PSKEY_XTAL_TARGET_AMPLITUDE                                0x024b  /* uint8 */
+#define CSR_PSKEY_PCM_MIN_CPU_CLOCK                            0x024d  /* uint16 */
+#define CSR_PSKEY_HOST_INTERFACE_PIO_USB                       0x0250  /* uint16 */
+#define CSR_PSKEY_CPU_IDLE_MODE                                        0x0251  /* cpu_idle_mode */
+#define CSR_PSKEY_DEEP_SLEEP_CLEAR_RTS                         0x0252  /* bool */
+#define CSR_PSKEY_RF_RESONANCE_TRIM                            0x0254  /* uint16 */
+#define CSR_PSKEY_DEEP_SLEEP_PIO_WAKE                          0x0255  /* uint16 */
+#define CSR_PSKEY_DRAIN_BORE_TIMERS                            0x0256  /* uint32[] */
+#define CSR_PSKEY_DRAIN_TX_POWER_BASE                          0x0257  /* uint16 */
+#define CSR_PSKEY_MODULE_ID                                    0x0259  /* uint32 */
+#define CSR_PSKEY_MODULE_DESIGN                                        0x025a  /* uint16 */
+#define CSR_PSKEY_MODULE_SECURITY_CODE                         0x025c  /* uint16[] */
+#define CSR_PSKEY_VM_DISABLE                                   0x025d  /* bool */
+#define CSR_PSKEY_MOD_MANUF0                                   0x025e  /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF1                                   0x025f  /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF2                                   0x0260  /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF3                                   0x0261  /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF4                                   0x0262  /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF5                                   0x0263  /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF6                                   0x0264  /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF7                                   0x0265  /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF8                                   0x0266  /* uint16[] */
+#define CSR_PSKEY_MOD_MANUF9                                   0x0267  /* uint16[] */
+#define CSR_PSKEY_DUT_VM_DISABLE                               0x0268  /* bool */
+#define CSR_PSKEY_USR0                                         0x028a  /* uint16[] */
+#define CSR_PSKEY_USR1                                         0x028b  /* uint16[] */
+#define CSR_PSKEY_USR2                                         0x028c  /* uint16[] */
+#define CSR_PSKEY_USR3                                         0x028d  /* uint16[] */
+#define CSR_PSKEY_USR4                                         0x028e  /* uint16[] */
+#define CSR_PSKEY_USR5                                         0x028f  /* uint16[] */
+#define CSR_PSKEY_USR6                                         0x0290  /* uint16[] */
+#define CSR_PSKEY_USR7                                         0x0291  /* uint16[] */
+#define CSR_PSKEY_USR8                                         0x0292  /* uint16[] */
+#define CSR_PSKEY_USR9                                         0x0293  /* uint16[] */
+#define CSR_PSKEY_USR10                                                0x0294  /* uint16[] */
+#define CSR_PSKEY_USR11                                                0x0295  /* uint16[] */
+#define CSR_PSKEY_USR12                                                0x0296  /* uint16[] */
+#define CSR_PSKEY_USR13                                                0x0297  /* uint16[] */
+#define CSR_PSKEY_USR14                                                0x0298  /* uint16[] */
+#define CSR_PSKEY_USR15                                                0x0299  /* uint16[] */
+#define CSR_PSKEY_USR16                                                0x029a  /* uint16[] */
+#define CSR_PSKEY_USR17                                                0x029b  /* uint16[] */
+#define CSR_PSKEY_USR18                                                0x029c  /* uint16[] */
+#define CSR_PSKEY_USR19                                                0x029d  /* uint16[] */
+#define CSR_PSKEY_USR20                                                0x029e  /* uint16[] */
+#define CSR_PSKEY_USR21                                                0x029f  /* uint16[] */
+#define CSR_PSKEY_USR22                                                0x02a0  /* uint16[] */
+#define CSR_PSKEY_USR23                                                0x02a1  /* uint16[] */
+#define CSR_PSKEY_USR24                                                0x02a2  /* uint16[] */
+#define CSR_PSKEY_USR25                                                0x02a3  /* uint16[] */
+#define CSR_PSKEY_USR26                                                0x02a4  /* uint16[] */
+#define CSR_PSKEY_USR27                                                0x02a5  /* uint16[] */
+#define CSR_PSKEY_USR28                                                0x02a6  /* uint16[] */
+#define CSR_PSKEY_USR29                                                0x02a7  /* uint16[] */
+#define CSR_PSKEY_USR30                                                0x02a8  /* uint16[] */
+#define CSR_PSKEY_USR31                                                0x02a9  /* uint16[] */
+#define CSR_PSKEY_USR32                                                0x02aa  /* uint16[] */
+#define CSR_PSKEY_USR33                                                0x02ab  /* uint16[] */
+#define CSR_PSKEY_USR34                                                0x02ac  /* uint16[] */
+#define CSR_PSKEY_USR35                                                0x02ad  /* uint16[] */
+#define CSR_PSKEY_USR36                                                0x02ae  /* uint16[] */
+#define CSR_PSKEY_USR37                                                0x02af  /* uint16[] */
+#define CSR_PSKEY_USR38                                                0x02b0  /* uint16[] */
+#define CSR_PSKEY_USR39                                                0x02b1  /* uint16[] */
+#define CSR_PSKEY_USR40                                                0x02b2  /* uint16[] */
+#define CSR_PSKEY_USR41                                                0x02b3  /* uint16[] */
+#define CSR_PSKEY_USR42                                                0x02b4  /* uint16[] */
+#define CSR_PSKEY_USR43                                                0x02b5  /* uint16[] */
+#define CSR_PSKEY_USR44                                                0x02b6  /* uint16[] */
+#define CSR_PSKEY_USR45                                                0x02b7  /* uint16[] */
+#define CSR_PSKEY_USR46                                                0x02b8  /* uint16[] */
+#define CSR_PSKEY_USR47                                                0x02b9  /* uint16[] */
+#define CSR_PSKEY_USR48                                                0x02ba  /* uint16[] */
+#define CSR_PSKEY_USR49                                                0x02bb  /* uint16[] */
+#define CSR_PSKEY_USB_VERSION                                  0x02bc  /* uint16 */
+#define CSR_PSKEY_USB_DEVICE_CLASS_CODES                       0x02bd  /* usbclass */
+#define CSR_PSKEY_USB_VENDOR_ID                                        0x02be  /* uint16 */
+#define CSR_PSKEY_USB_PRODUCT_ID                               0x02bf  /* uint16 */
+#define CSR_PSKEY_USB_MANUF_STRING                             0x02c1  /* unicodestring */
+#define CSR_PSKEY_USB_PRODUCT_STRING                           0x02c2  /* unicodestring */
+#define CSR_PSKEY_USB_SERIAL_NUMBER_STRING                     0x02c3  /* unicodestring */
+#define CSR_PSKEY_USB_CONFIG_STRING                            0x02c4  /* unicodestring */
+#define CSR_PSKEY_USB_ATTRIBUTES                               0x02c5  /* uint8 */
+#define CSR_PSKEY_USB_MAX_POWER                                        0x02c6  /* uint16 */
+#define CSR_PSKEY_USB_BT_IF_CLASS_CODES                                0x02c7  /* usbclass */
+#define CSR_PSKEY_USB_LANGID                                   0x02c9  /* uint16 */
+#define CSR_PSKEY_USB_DFU_CLASS_CODES                          0x02ca  /* usbclass */
+#define CSR_PSKEY_USB_DFU_PRODUCT_ID                           0x02cb  /* uint16 */
+#define CSR_PSKEY_USB_PIO_DETACH                               0x02ce  /* uint16 */
+#define CSR_PSKEY_USB_PIO_WAKEUP                               0x02cf  /* uint16 */
+#define CSR_PSKEY_USB_PIO_PULLUP                               0x02d0  /* uint16 */
+#define CSR_PSKEY_USB_PIO_VBUS                                 0x02d1  /* uint16 */
+#define CSR_PSKEY_USB_PIO_WAKE_TIMEOUT                         0x02d2  /* uint16 */
+#define CSR_PSKEY_USB_PIO_RESUME                               0x02d3  /* uint16 */
+#define CSR_PSKEY_USB_BT_SCO_IF_CLASS_CODES                    0x02d4  /* usbclass */
+#define CSR_PSKEY_USB_SUSPEND_PIO_LEVEL                                0x02d5  /* uint16 */
+#define CSR_PSKEY_USB_SUSPEND_PIO_DIR                          0x02d6  /* uint16 */
+#define CSR_PSKEY_USB_SUSPEND_PIO_MASK                         0x02d7  /* uint16 */
+#define CSR_PSKEY_USB_ENDPOINT_0_MAX_PACKET_SIZE               0x02d8  /* uint8 */
+#define CSR_PSKEY_USB_CONFIG                                   0x02d9  /* uint16 */
+#define CSR_PSKEY_RADIOTEST_ATTEN_INIT                         0x0320  /* uint16 */
+#define CSR_PSKEY_RADIOTEST_FIRST_TRIM_TIME                    0x0326  /* TIME */
+#define CSR_PSKEY_RADIOTEST_SUBSEQUENT_TRIM_TIME               0x0327  /* TIME */
+#define CSR_PSKEY_RADIOTEST_LO_LVL_TRIM_ENABLE                 0x0328  /* bool */
+#define CSR_PSKEY_RADIOTEST_DISABLE_MODULATION                 0x032c  /* bool */
+#define CSR_PSKEY_RFCOMM_FCON_THRESHOLD                                0x0352  /* uint16 */
+#define CSR_PSKEY_RFCOMM_FCOFF_THRESHOLD                       0x0353  /* uint16 */
+#define CSR_PSKEY_IPV6_STATIC_ADDR                             0x0354  /* uint16[] */
+#define CSR_PSKEY_IPV4_STATIC_ADDR                             0x0355  /* uint32 */
+#define CSR_PSKEY_IPV6_STATIC_PREFIX_LEN                       0x0356  /* uint8 */
+#define CSR_PSKEY_IPV6_STATIC_ROUTER_ADDR                      0x0357  /* uint16[] */
+#define CSR_PSKEY_IPV4_STATIC_SUBNET_MASK                      0x0358  /* uint32 */
+#define CSR_PSKEY_IPV4_STATIC_ROUTER_ADDR                      0x0359  /* uint32 */
+#define CSR_PSKEY_MDNS_NAME                                    0x035a  /* char[] */
+#define CSR_PSKEY_FIXED_PIN                                    0x035b  /* uint8[] */
+#define CSR_PSKEY_MDNS_PORT                                    0x035c  /* uint16 */
+#define CSR_PSKEY_MDNS_TTL                                     0x035d  /* uint8 */
+#define CSR_PSKEY_MDNS_IPV4_ADDR                               0x035e  /* uint32 */
+#define CSR_PSKEY_ARP_CACHE_TIMEOUT                            0x035f  /* uint16 */
+#define CSR_PSKEY_HFP_POWER_TABLE                              0x0360  /* uint16[] */
+#define CSR_PSKEY_DRAIN_BORE_TIMER_COUNTERS                    0x03e7  /* uint32[] */
+#define CSR_PSKEY_DRAIN_BORE_COUNTERS                          0x03e6  /* uint32[] */
+#define CSR_PSKEY_LOOP_FILTER_TRIM                             0x03e4  /* uint16 */
+#define CSR_PSKEY_DRAIN_BORE_CURRENT_PEAK                      0x03e3  /* uint32[] */
+#define CSR_PSKEY_VM_E2_CACHE_LIMIT                            0x03e2  /* uint16 */
+#define CSR_PSKEY_FORCE_16MHZ_REF_PIO                          0x03e1  /* uint16 */
+#define CSR_PSKEY_CDMA_LO_REF_LIMITS                           0x03df  /* uint16 */
+#define CSR_PSKEY_CDMA_LO_ERROR_LIMITS                         0x03de  /* uint16 */
+#define CSR_PSKEY_CLOCK_STARTUP_DELAY                          0x03dd  /* uint16 */
+#define CSR_PSKEY_DEEP_SLEEP_CORRECTION_FACTOR                 0x03dc  /* int16 */
+#define CSR_PSKEY_TEMPERATURE_CALIBRATION                      0x03db  /* temperature_calibration */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA             0x03da  /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL              0x03d9  /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB                   0x03d8  /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_ANA_FTRIM               0x03d7  /* temperature_calibration[] */
+#define CSR_PSKEY_TEST_DELTA_OFFSET                            0x03d6  /* uint16 */
+#define CSR_PSKEY_RX_DYNAMIC_LVL_OFFSET                                0x03d4  /* uint16 */
+#define CSR_PSKEY_TEST_FORCE_OFFSET                            0x03d3  /* bool */
+#define CSR_PSKEY_RF_TRAP_BAD_DIVISION_RATIOS                  0x03cf  /* uint16 */
+#define CSR_PSKEY_RADIOTEST_CDMA_LO_REF_LIMITS                 0x03ce  /* uint16 */
+#define CSR_PSKEY_INITIAL_BOOTMODE                             0x03cd  /* int16 */
+#define CSR_PSKEY_ONCHIP_HCI_CLIENT                            0x03cc  /* bool */
+#define CSR_PSKEY_RX_ATTEN_BACKOFF                             0x03ca  /* uint16 */
+#define CSR_PSKEY_RX_ATTEN_UPDATE_RATE                         0x03c9  /* uint16 */
+#define CSR_PSKEY_SYNTH_TXRX_THRESHOLDS                                0x03c7  /* uint16 */
+#define CSR_PSKEY_MIN_WAIT_STATES                              0x03c6  /* uint16 */
+#define CSR_PSKEY_RSSI_CORRECTION                              0x03c5  /* int8 */
+#define CSR_PSKEY_SCHED_THROTTLE_TIMEOUT                       0x03c4  /* TIME */
+#define CSR_PSKEY_DEEP_SLEEP_USE_EXTERNAL_CLOCK                        0x03c3  /* bool */
+#define CSR_PSKEY_TRIM_RADIO_FILTERS                           0x03c2  /* uint16 */
+#define CSR_PSKEY_TRANSMIT_OFFSET                              0x03c1  /* int16 */
+#define CSR_PSKEY_USB_VM_CONTROL                               0x03c0  /* bool */
+#define CSR_PSKEY_MR_ANA_RX_FTRIM                              0x03bf  /* uint16 */
+#define CSR_PSKEY_I2C_CONFIG                                   0x03be  /* uint16 */
+#define CSR_PSKEY_IQ_LVL_RX                                    0x03bd  /* uint16 */
+#define CSR_PSKEY_MR_TX_FILTER_CONFIG                          0x03bb  /* uint32 */
+#define CSR_PSKEY_MR_TX_CONFIG2                                        0x03ba  /* uint16 */
+#define CSR_PSKEY_USB_DONT_RESET_BOOTMODE_ON_HOST_RESET                0x03b9  /* bool */
+#define CSR_PSKEY_LC_USE_THROTTLING                            0x03b8  /* bool */
+#define CSR_PSKEY_CHARGER_TRIM                                 0x03b7  /* uint16 */
+#define CSR_PSKEY_CLOCK_REQUEST_FEATURES                       0x03b6  /* uint16 */
+#define CSR_PSKEY_TRANSMIT_OFFSET_CLASS1                       0x03b4  /* int16 */
+#define CSR_PSKEY_TX_AVOID_PA_CLASS1_PIO                       0x03b3  /* uint16 */
+#define CSR_PSKEY_MR_PIO_CONFIG                                        0x03b2  /* uint16 */
+#define CSR_PSKEY_UART_CONFIG2                                 0x03b1  /* uint8 */
+#define CSR_PSKEY_CLASS1_IQ_LVL                                        0x03b0  /* uint16 */
+#define CSR_PSKEY_CLASS1_TX_CONFIG2                            0x03af  /* uint16 */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_INTERNAL_PA_CLASS1      0x03ae  /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_EXTERNAL_PA_CLASS1      0x03ad  /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_PRE_LVL_MR           0x03ac  /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_HEADER         0x03ab  /* temperature_calibration[] */
+#define CSR_PSKEY_TEMPERATURE_VS_DELTA_TX_BB_MR_PAYLOAD                0x03aa  /* temperature_calibration[] */
+#define CSR_PSKEY_RX_MR_EQ_TAPS                                        0x03a9  /* uint16[] */
+#define CSR_PSKEY_TX_PRE_LVL_CLASS1                            0x03a8  /* uint8 */
+#define CSR_PSKEY_ANALOGUE_ATTENUATOR                          0x03a7  /* bool */
+#define CSR_PSKEY_MR_RX_FILTER_TRIM                            0x03a6  /* uint16 */
+#define CSR_PSKEY_MR_RX_FILTER_RESPONSE                                0x03a5  /* int16[] */
+#define CSR_PSKEY_PIO_WAKEUP_STATE                             0x039f  /* uint16 */
+#define CSR_PSKEY_MR_TX_IF_ATTEN_OFF_TEMP                      0x0394  /* int16 */
+#define CSR_PSKEY_LO_DIV_LATCH_BYPASS                          0x0393  /* bool */
+#define CSR_PSKEY_LO_VCO_STANDBY                               0x0392  /* bool */
+#define CSR_PSKEY_SLOW_CLOCK_FILTER_SHIFT                      0x0391  /* uint16 */
+#define CSR_PSKEY_SLOW_CLOCK_FILTER_DIVIDER                    0x0390  /* uint16 */
+#define CSR_PSKEY_USB_ATTRIBUTES_POWER                         0x03f2  /* bool */
+#define CSR_PSKEY_USB_ATTRIBUTES_WAKEUP                                0x03f3  /* bool */
+#define CSR_PSKEY_DFU_ATTRIBUTES_MANIFESTATION_TOLERANT                0x03f4  /* bool */
+#define CSR_PSKEY_DFU_ATTRIBUTES_CAN_UPLOAD                    0x03f5  /* bool */
+#define CSR_PSKEY_DFU_ATTRIBUTES_CAN_DOWNLOAD                  0x03f6  /* bool */
+#define CSR_PSKEY_UART_CONFIG_STOP_BITS                                0x03fc  /* bool */
+#define CSR_PSKEY_UART_CONFIG_PARITY_BIT                       0x03fd  /* uint16 */
+#define CSR_PSKEY_UART_CONFIG_FLOW_CTRL_EN                     0x03fe  /* bool */
+#define CSR_PSKEY_UART_CONFIG_RTS_AUTO_EN                      0x03ff  /* bool */
+#define CSR_PSKEY_UART_CONFIG_RTS                              0x0400  /* bool */
+#define CSR_PSKEY_UART_CONFIG_TX_ZERO_EN                       0x0401  /* bool */
+#define CSR_PSKEY_UART_CONFIG_NON_BCSP_EN                      0x0402  /* bool */
+#define CSR_PSKEY_UART_CONFIG_RX_RATE_DELAY                    0x0403  /* uint16 */
+#define CSR_PSKEY_UART_SEQ_TIMEOUT                             0x0405  /* uint16 */
+#define CSR_PSKEY_UART_SEQ_RETRIES                             0x0406  /* uint16 */
+#define CSR_PSKEY_UART_SEQ_WINSIZE                             0x0407  /* uint16 */
+#define CSR_PSKEY_UART_USE_CRC_ON_TX                           0x0408  /* bool */
+#define CSR_PSKEY_UART_HOST_INITIAL_STATE                      0x0409  /* hwakeup_state */
+#define CSR_PSKEY_UART_HOST_ATTENTION_SPAN                     0x040a  /* uint16 */
+#define CSR_PSKEY_UART_HOST_WAKEUP_TIME                                0x040b  /* uint16 */
+#define CSR_PSKEY_UART_HOST_WAKEUP_WAIT                                0x040c  /* uint16 */
+#define CSR_PSKEY_BCSP_LM_MODE                                 0x0410  /* uint16 */
+#define CSR_PSKEY_BCSP_LM_SYNC_RETRIES                         0x0411  /* uint16 */
+#define CSR_PSKEY_BCSP_LM_TSHY                                 0x0412  /* uint16 */
+#define CSR_PSKEY_UART_DFU_CONFIG_STOP_BITS                    0x0417  /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_PARITY_BIT                   0x0418  /* uint16 */
+#define CSR_PSKEY_UART_DFU_CONFIG_FLOW_CTRL_EN                 0x0419  /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_RTS_AUTO_EN                  0x041a  /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_RTS                          0x041b  /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_TX_ZERO_EN                   0x041c  /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_NON_BCSP_EN                  0x041d  /* bool */
+#define CSR_PSKEY_UART_DFU_CONFIG_RX_RATE_DELAY                        0x041e  /* uint16 */
+#define CSR_PSKEY_AMUX_AIO0                                    0x041f  /* ana_amux_sel */
+#define CSR_PSKEY_AMUX_AIO1                                    0x0420  /* ana_amux_sel */
+#define CSR_PSKEY_AMUX_AIO2                                    0x0421  /* ana_amux_sel */
+#define CSR_PSKEY_AMUX_AIO3                                    0x0422  /* ana_amux_sel */
+#define CSR_PSKEY_LOCAL_NAME_SIMPLIFIED                                0x0423  /* local_name_complete */
+#define CSR_PSKEY_EXTENDED_STUB                                        0x2001  /* uint16 */
+
+char *csr_builddeftostr(uint16_t def);
+char *csr_buildidtostr(uint16_t id);
+char *csr_chipvertostr(uint16_t ver, uint16_t rev);
+char *csr_pskeytostr(uint16_t pskey);
+char *csr_pskeytoval(uint16_t pskey);
+
+int csr_open_hci(char *device);
+int csr_read_hci(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_hci(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_hci(void);
+
+int csr_open_usb(char *device);
+int csr_read_usb(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_usb(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_usb(void);
+
+int csr_open_bcsp(char *device, speed_t bcsp_rate);
+int csr_read_bcsp(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_bcsp(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_bcsp(void);
+
+int csr_open_h4(char *device);
+int csr_read_h4(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_h4(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_h4(void);
+
+int csr_open_3wire(char *device);
+int csr_read_3wire(uint16_t varid, uint8_t *value, uint16_t length);
+int csr_write_3wire(uint16_t varid, uint8_t *value, uint16_t length);
+void csr_close_3wire(void);
+
+int csr_write_varid_valueless(int dd, uint16_t seqnum, uint16_t varid);
+int csr_write_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length);
+int csr_read_varid_complex(int dd, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length);
+int csr_read_varid_uint16(int dd, uint16_t seqnum, uint16_t varid, uint16_t *value);
+int csr_read_varid_uint32(int dd, uint16_t seqnum, uint16_t varid, uint32_t *value);
+int csr_read_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length);
+int csr_write_pskey_complex(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint8_t *value, uint16_t length);
+int csr_read_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t *value);
+int csr_write_pskey_uint16(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint16_t value);
+int csr_read_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t *value);
+int csr_write_pskey_uint32(int dd, uint16_t seqnum, uint16_t pskey, uint16_t stores, uint32_t value);
+
+int psr_put(uint16_t pskey, uint8_t *value, uint16_t size);
+int psr_get(uint16_t *pskey, uint8_t *value, uint16_t *size);
+int psr_read(const char *filename);
+int psr_print(void);
diff --git a/tools/csr_3wire.c b/tools/csr_3wire.c
new file mode 100644 (file)
index 0000000..33fcf38
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include "csr.h"
+
+static uint16_t seqnum = 0x0000;
+
+int csr_open_3wire(char *device)
+{
+       fprintf(stderr, "Transport not implemented\n");
+
+       return -1;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+       errno = EIO;
+
+       return -1;
+}
+
+int csr_read_3wire(uint16_t varid, uint8_t *value, uint16_t length)
+{
+       return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_3wire(uint16_t varid, uint8_t *value, uint16_t length)
+{
+       return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_3wire(void)
+{
+}
diff --git a/tools/csr_bcsp.c b/tools/csr_bcsp.c
new file mode 100644 (file)
index 0000000..f7afe53
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <termios.h>
+
+#include "csr.h"
+#include "ubcsp.h"
+
+static uint16_t seqnum = 0x0000;
+
+static int fd = -1;
+
+static struct ubcsp_packet send_packet;
+static uint8_t send_buffer[512];
+
+static struct ubcsp_packet receive_packet;
+static uint8_t receive_buffer[512];
+
+int csr_open_bcsp(char *device, speed_t bcsp_rate)
+{
+       struct termios ti;
+       uint8_t delay, activity = 0x00;
+       int timeout = 0;
+
+       if (!device)
+               device = "/dev/ttyS0";
+
+       fd = open(device, O_RDWR | O_NOCTTY);
+       if (fd < 0) {
+               fprintf(stderr, "Can't open serial port: %s (%d)\n",
+                                               strerror(errno), errno);
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (tcgetattr(fd, &ti) < 0) {
+               fprintf(stderr, "Can't get port settings: %s (%d)\n",
+                                               strerror(errno), errno);
+               close(fd);
+               return -1;
+       }
+
+       cfmakeraw(&ti);
+
+       ti.c_cflag |=  CLOCAL;
+       ti.c_cflag &= ~CRTSCTS;
+       ti.c_cflag |=  PARENB;
+       ti.c_cflag &= ~PARODD;
+       ti.c_cflag &= ~CSIZE;
+       ti.c_cflag |=  CS8;
+       ti.c_cflag &= ~CSTOPB;
+
+       ti.c_cc[VMIN] = 1;
+       ti.c_cc[VTIME] = 0;
+
+       cfsetospeed(&ti, bcsp_rate);
+
+       if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+               fprintf(stderr, "Can't change port settings: %s (%d)\n",
+                                               strerror(errno), errno);
+               close(fd);
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) {
+               fprintf(stderr, "Can't set non blocking mode: %s (%d)\n",
+                                               strerror(errno), errno);
+               close(fd);
+               return -1;
+       }
+
+       memset(&send_packet, 0, sizeof(send_packet));
+       memset(&receive_packet, 0, sizeof(receive_packet));
+
+       ubcsp_initialize();
+
+       send_packet.length = 512;
+       send_packet.payload = send_buffer;
+
+       receive_packet.length = 512;
+       receive_packet.payload = receive_buffer;
+
+       ubcsp_receive_packet(&receive_packet);
+
+       while (1) {
+               delay = ubcsp_poll(&activity);
+
+               if (activity & UBCSP_PACKET_SENT)
+                       break;
+
+               if (delay) {
+                       usleep(delay * 100);
+
+                       if (timeout++ > 5000) {
+                               fprintf(stderr, "Initialization timed out\n");
+                               return -1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+void put_uart(uint8_t ch)
+{
+       if (write(fd, &ch, 1) < 0)
+               fprintf(stderr, "UART write error\n");
+}
+
+uint8_t get_uart(uint8_t *ch)
+{
+       int res = read(fd, ch, 1);
+       return res > 0 ? res : 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+       unsigned char cp[254], rp[254];
+       uint8_t cmd[10];
+       uint16_t size;
+       uint8_t delay, activity = 0x00;
+       int timeout = 0, sent = 0;
+
+       size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+       cmd[0] = command & 0xff;
+       cmd[1] = command >> 8;
+       cmd[2] = size & 0xff;
+       cmd[3] = size >> 8;
+       cmd[4] = seqnum & 0xff;
+       cmd[5] = seqnum >> 8;
+       cmd[6] = varid & 0xff;
+       cmd[7] = varid >> 8;
+       cmd[8] = 0x00;
+       cmd[9] = 0x00;
+
+       memset(cp, 0, sizeof(cp));
+       cp[0] = 0x00;
+       cp[1] = 0xfc;
+       cp[2] = (size * 2) + 1;
+       cp[3] = 0xc2;
+       memcpy(cp + 4, cmd, sizeof(cmd));
+       memcpy(cp + 14, value, length);
+
+       receive_packet.length = 512;
+       ubcsp_receive_packet(&receive_packet);
+
+       send_packet.channel  = 5;
+       send_packet.reliable = 1;
+       send_packet.length   = (size * 2) + 4;
+       memcpy(send_packet.payload, cp, (size * 2) + 4);
+
+       ubcsp_send_packet(&send_packet);
+
+       while (1) {
+               delay = ubcsp_poll(&activity);
+
+               if (activity & UBCSP_PACKET_SENT) {
+                       switch (varid) {
+                       case CSR_VARID_COLD_RESET:
+                       case CSR_VARID_WARM_RESET:
+                       case CSR_VARID_COLD_HALT:
+                       case CSR_VARID_WARM_HALT:
+                               return 0;
+                       }
+
+                       sent = 1;
+                       timeout = 0;
+               }
+
+               if (activity & UBCSP_PACKET_RECEIVED) {
+                       if (sent && receive_packet.channel == 5 &&
+                                       receive_packet.payload[0] == 0xff) {
+                               memcpy(rp, receive_packet.payload,
+                                                       receive_packet.length);
+                               break;
+                       }
+
+                       receive_packet.length = 512;
+                       ubcsp_receive_packet(&receive_packet);
+                       timeout = 0;
+               }
+
+               if (delay) {
+                       usleep(delay * 100);
+
+                       if (timeout++ > 5000) {
+                               fprintf(stderr, "Operation timed out\n");
+                               errno = ETIMEDOUT;
+                               return -1;
+                       }
+               }
+       }
+
+       if (rp[0] != 0xff || rp[2] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[11] + (rp[12] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       memcpy(value, rp + 13, length);
+
+       return 0;
+}
+
+int csr_read_bcsp(uint16_t varid, uint8_t *value, uint16_t length)
+{
+       return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_bcsp(uint16_t varid, uint8_t *value, uint16_t length)
+{
+       return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_bcsp(void)
+{
+       close(fd);
+}
diff --git a/tools/csr_h4.c b/tools/csr_h4.c
new file mode 100644 (file)
index 0000000..3371770
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <termios.h>
+
+#include "csr.h"
+
+static uint16_t seqnum = 0x0000;
+
+static int fd = -1;
+
+int csr_open_h4(char *device)
+{
+       struct termios ti;
+
+       if (!device)
+               device = "/dev/ttyS0";
+
+       fd = open(device, O_RDWR | O_NOCTTY);
+       if (fd < 0) {
+               fprintf(stderr, "Can't open serial port: %s (%d)\n",
+                                               strerror(errno), errno);
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (tcgetattr(fd, &ti) < 0) {
+               fprintf(stderr, "Can't get port settings: %s (%d)\n",
+                                               strerror(errno), errno);
+               close(fd);
+               return -1;
+       }
+
+       cfmakeraw(&ti);
+
+       ti.c_cflag |= CLOCAL;
+       ti.c_cflag |= CRTSCTS;
+
+       cfsetospeed(&ti, B38400);
+
+       if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+               fprintf(stderr, "Can't change port settings: %s (%d)\n",
+                                               strerror(errno), errno);
+               close(fd);
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       return 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+       unsigned char cp[254], rp[254];
+       uint8_t cmd[10];
+       uint16_t size;
+       int len, offset = 3;
+
+       size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+       cmd[0] = command & 0xff;
+       cmd[1] = command >> 8;
+       cmd[2] = size & 0xff;
+       cmd[3] = size >> 8;
+       cmd[4] = seqnum & 0xff;
+       cmd[5] = seqnum >> 8;
+       cmd[6] = varid & 0xff;
+       cmd[7] = varid >> 8;
+       cmd[8] = 0x00;
+       cmd[9] = 0x00;
+
+       memset(cp, 0, sizeof(cp));
+       cp[0] = 0x01;
+       cp[1] = 0x00;
+       cp[2] = 0xfc;
+       cp[3] = (size * 2) + 1;
+       cp[4] = 0xc2;
+       memcpy(cp + 5, cmd, sizeof(cmd));
+       memcpy(cp + 15, value, length);
+
+       if (write(fd, cp, (size * 2) + 5) < 0)
+               return -1;
+
+       switch (varid) {
+       case CSR_VARID_COLD_RESET:
+       case CSR_VARID_WARM_RESET:
+       case CSR_VARID_COLD_HALT:
+       case CSR_VARID_WARM_HALT:
+               return 0;
+       }
+
+       do {
+               if (read(fd, rp, 1) < 1)
+                       return -1;
+       } while (rp[0] != 0x04);
+
+       if (read(fd, rp + 1, 2) < 2)
+               return -1;
+
+       do {
+               len = read(fd, rp + offset, sizeof(rp) - offset);
+               offset += len;
+       } while (offset < rp[2] + 3);
+
+       if (rp[0] != 0x04 || rp[1] != 0xff || rp[3] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[12] + (rp[13] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       memcpy(value, rp + 14, length);
+
+       return 0;
+}
+
+int csr_read_h4(uint16_t varid, uint8_t *value, uint16_t length)
+{
+       return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_h4(uint16_t varid, uint8_t *value, uint16_t length)
+{
+       return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_h4(void)
+{
+       close(fd);
+}
diff --git a/tools/csr_hci.c b/tools/csr_hci.c
new file mode 100644 (file)
index 0000000..6bd37c3
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "csr.h"
+
+static uint16_t seqnum = 0x0000;
+
+static int dd = -1;
+
+int csr_open_hci(char *device)
+{
+       struct hci_dev_info di;
+       struct hci_version ver;
+       int dev = 0;
+
+       if (device) {
+               dev = hci_devid(device);
+               if (dev < 0) {
+                       fprintf(stderr, "Device not available\n");
+                       return -1;
+               }
+       }
+
+       dd = hci_open_dev(dev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               return -1;
+       }
+
+       if (hci_devinfo(dev, &di) < 0) {
+               fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               hci_close_dev(dd);
+               return -1;
+       }
+
+       if (hci_read_local_version(dd, &ver, 1000) < 0) {
+               fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               hci_close_dev(dd);
+               return -1;
+       }
+
+       if (ver.manufacturer != 10) {
+               fprintf(stderr, "Unsupported manufacturer\n");
+               hci_close_dev(dd);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+       unsigned char cp[254], rp[254];
+       struct hci_request rq;
+       uint8_t cmd[10];
+       uint16_t size;
+
+       size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+       cmd[0] = command & 0xff;
+       cmd[1] = command >> 8;
+       cmd[2] = size & 0xff;
+       cmd[3] = size >> 8;
+       cmd[4] = seqnum & 0xff;
+       cmd[5] = seqnum >> 8;
+       cmd[6] = varid & 0xff;
+       cmd[7] = varid >> 8;
+       cmd[8] = 0x00;
+       cmd[9] = 0x00;
+
+       memset(cp, 0, sizeof(cp));
+       cp[0] = 0xc2;
+       memcpy(cp + 1, cmd, sizeof(cmd));
+       memcpy(cp + 11, value, length);
+
+       switch (varid) {
+       case CSR_VARID_COLD_RESET:
+       case CSR_VARID_WARM_RESET:
+       case CSR_VARID_COLD_HALT:
+       case CSR_VARID_WARM_HALT:
+               return hci_send_cmd(dd, OGF_VENDOR_CMD, 0x00, (size * 2) + 1, cp);
+       }
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x00;
+       rq.event  = EVT_VENDOR;
+       rq.cparam = cp;
+       rq.clen   = (size * 2) + 1;
+       rq.rparam = rp;
+       rq.rlen   = sizeof(rp);
+
+       if (hci_send_req(dd, &rq, 2000) < 0)
+               return -1;
+
+       if (rp[0] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[9] + (rp[10] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       memcpy(value, rp + 11, length);
+
+       return 0;
+}
+
+int csr_read_hci(uint16_t varid, uint8_t *value, uint16_t length)
+{
+       return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_hci(uint16_t varid, uint8_t *value, uint16_t length)
+{
+       return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_hci(void)
+{
+       hci_close_dev(dd);
+}
diff --git a/tools/csr_usb.c b/tools/csr_usb.c
new file mode 100644 (file)
index 0000000..19903b0
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <usb.h>
+
+#include "csr.h"
+
+#ifdef NEED_USB_GET_BUSSES
+static inline struct usb_bus *usb_get_busses(void)
+{
+       return usb_busses;
+}
+#endif
+
+#ifdef NEED_USB_INTERRUPT_READ
+static inline int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout)
+{
+       return usb_bulk_read(dev, ep, bytes, size, timeout);
+}
+#endif
+
+#ifndef USB_DIR_OUT
+#define USB_DIR_OUT    0x00
+#endif
+
+static uint16_t seqnum = 0x0000;
+
+static struct usb_dev_handle *udev = NULL;
+
+int csr_open_usb(char *device)
+{
+       struct usb_bus *bus;
+       struct usb_device *dev;
+
+       usb_init();
+
+       usb_find_busses();
+       usb_find_devices();
+
+       for (bus = usb_get_busses(); bus; bus = bus->next) {
+               for (dev = bus->devices; dev; dev = dev->next) {
+                       if (dev->descriptor.bDeviceClass == USB_CLASS_HUB)
+                               continue;
+
+                       if (dev->descriptor.idVendor != 0x0a12 ||
+                                       dev->descriptor.idProduct != 0x0001)
+                               continue;
+
+                       goto found;
+               }
+       }
+
+       fprintf(stderr, "Device not available\n");
+
+       return -1;
+
+found:
+       udev = usb_open(dev);
+       if (!udev) {
+               fprintf(stderr, "Can't open device: %s (%d)\n",
+                                               strerror(errno), errno);
+               return -1;
+       }
+
+       if (usb_claim_interface(udev, 0) < 0) {
+               fprintf(stderr, "Can't claim interface: %s (%d)\n",
+                                               strerror(errno), errno);
+               usb_close(udev);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int do_command(uint16_t command, uint16_t seqnum, uint16_t varid, uint8_t *value, uint16_t length)
+{
+       unsigned char cp[254], rp[254];
+       uint8_t cmd[10];
+       uint16_t size;
+       int len, offset = 0;
+
+       size = (length < 8) ? 9 : ((length + 1) / 2) + 5;
+
+       cmd[0] = command & 0xff;
+       cmd[1] = command >> 8;
+       cmd[2] = size & 0xff;
+       cmd[3] = size >> 8;
+       cmd[4] = seqnum & 0xff;
+       cmd[5] = seqnum >> 8;
+       cmd[6] = varid & 0xff;
+       cmd[7] = varid >> 8;
+       cmd[8] = 0x00;
+       cmd[9] = 0x00;
+
+       memset(cp, 0, sizeof(cp));
+       cp[0] = 0x00;
+       cp[1] = 0xfc;
+       cp[2] = (size * 2) + 1;
+       cp[3] = 0xc2;
+       memcpy(cp + 4, cmd, sizeof(cmd));
+       memcpy(cp + 14, value, length);
+
+       usb_interrupt_read(udev, 0x81, (void *) rp, sizeof(rp), 2);
+
+       if (usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_DEVICE,
+                               0, 0, 0, (void *) cp, (size * 2) + 4, 1000) < 0)
+               return -1;
+
+       switch (varid) {
+       case CSR_VARID_COLD_RESET:
+       case CSR_VARID_WARM_RESET:
+       case CSR_VARID_COLD_HALT:
+       case CSR_VARID_WARM_HALT:
+               return 0;
+       }
+
+       do {
+               len = usb_interrupt_read(udev, 0x81,
+                       (void *) (rp + offset), sizeof(rp) - offset, 10);
+               offset += len;
+       } while (len > 0);
+
+       if (rp[0] != 0xff || rp[2] != 0xc2) {
+               errno = EIO;
+               return -1;
+       }
+
+       if ((rp[11] + (rp[12] << 8)) != 0) {
+               errno = ENXIO;
+               return -1;
+       }
+
+       memcpy(value, rp + 13, length);
+
+       return 0;
+}
+
+int csr_read_usb(uint16_t varid, uint8_t *value, uint16_t length)
+{
+       return do_command(0x0000, seqnum++, varid, value, length);
+}
+
+int csr_write_usb(uint16_t varid, uint8_t *value, uint16_t length)
+{
+       return do_command(0x0002, seqnum++, varid, value, length);
+}
+
+void csr_close_usb(void)
+{
+       usb_release_interface(udev, 0);
+       usb_close(udev);
+}
diff --git a/tools/dfu.c b/tools/dfu.c
new file mode 100644 (file)
index 0000000..39ec088
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <usb.h>
+
+#include "dfu.h"
+
+#ifndef USB_DIR_OUT
+#define USB_DIR_OUT    0x00
+#endif
+
+#ifndef USB_DIR_IN
+#define USB_DIR_IN     0x80
+#endif
+
+#ifndef USB_DT_DFU
+#define USB_DT_DFU     0x21
+#endif
+
+#define DFU_PACKETSIZE         0x03ff          /* CSR default value: 1023 */
+#define DFU_TIMEOUT            10000
+
+static uint32_t dfu_crc32_table[] = {
+       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+       0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+       0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+       0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+       0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+       0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+       0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+       0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+       0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+       0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+       0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+       0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+       0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+       0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+       0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+       0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+       0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+       0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+       0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+       0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+       0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+       0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+       0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+       0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+       0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+       0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+       0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+       0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+       0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+       0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+       0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+       0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+       0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+       0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+       0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+       0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+       0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+       0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+       0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+       0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+       0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+uint32_t crc32_init(void)
+{
+       return 0xffffffff;
+}
+
+uint32_t crc32_byte(uint32_t accum, uint8_t delta)
+{
+       return dfu_crc32_table[(accum ^ delta) & 0xff] ^ (accum >> 8);
+}
+
+int dfu_detach(struct usb_dev_handle *udev, int intf)
+{
+       if (!udev)
+               return -EIO;
+
+       return usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+               DFU_DETACH, 0x1388, intf, NULL, 0, DFU_TIMEOUT);
+}
+
+int dfu_upload(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size)
+{
+       if (!udev)
+               return -EIO;
+
+       return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+               DFU_UPLOAD, block, intf, buffer, size, DFU_TIMEOUT);
+}
+
+int dfu_download(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size)
+{
+       if (!udev)
+               return -EIO;
+
+       return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
+               DFU_DNLOAD, block, intf, buffer, size, DFU_TIMEOUT);
+}
+
+int dfu_get_status(struct usb_dev_handle *udev, int intf, struct dfu_status *status)
+{
+       if (!udev || !status)
+               return -EIO;
+
+       return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+               DFU_GETSTATUS, 0, intf, (char *) status, DFU_STATUS_SIZE, DFU_TIMEOUT);
+}
+
+int dfu_clear_status(struct usb_dev_handle *udev, int intf)
+{
+       if (!udev)
+               return -EIO;
+
+       return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
+               DFU_CLRSTATUS, 0, intf, NULL, 0, DFU_TIMEOUT);
+}
+
+int dfu_get_state(struct usb_dev_handle *udev, int intf, uint8_t *state)
+{
+       if (!udev || !state)
+               return -EIO;
+
+       return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+               DFU_GETSTATE, 0, intf, (char *) state, 1, DFU_TIMEOUT);
+}
+
+int dfu_abort(struct usb_dev_handle *udev, int intf)
+{
+       if (!udev)
+               return -EIO;
+
+       return usb_control_msg(udev, USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE,
+               DFU_ABORT, 0, intf, NULL, 0, DFU_TIMEOUT);
+}
diff --git a/tools/dfu.h b/tools/dfu.h
new file mode 100644 (file)
index 0000000..7f999f4
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdint.h>
+
+/* CRC interface */
+uint32_t crc32_init(void);
+uint32_t crc32_byte(uint32_t accum, uint8_t delta);
+
+/* DFU descriptor */
+struct usb_dfu_descriptor {
+       u_int8_t  bLength;
+       u_int8_t  bDescriptorType;
+       u_int8_t  bmAttributes;
+       u_int16_t wDetachTimeout;
+       u_int16_t wTransferSize;
+};
+
+/* DFU commands */
+#define DFU_DETACH             0
+#define DFU_DNLOAD             1
+#define DFU_UPLOAD             2
+#define DFU_GETSTATUS          3
+#define DFU_CLRSTATUS          4
+#define DFU_GETSTATE           5
+#define DFU_ABORT              6
+
+/* DFU status */
+struct dfu_status {
+       uint8_t bStatus;
+       uint8_t bwPollTimeout[3];
+       uint8_t bState;
+       uint8_t iString;
+} __attribute__ ((packed));
+#define DFU_STATUS_SIZE 6
+
+/* DFU status */
+#define DFU_OK                 0x00
+#define DFU_ERR_TARGET         0x01
+#define DFU_ERR_FILE           0x02
+#define DFU_ERR_WRITE          0x03
+#define DFU_ERR_ERASE          0x04
+#define DFU_ERR_CHECK_ERASED   0x05
+#define DFU_ERR_PROG           0x06
+#define DFU_ERR_VERIFY         0x07
+#define DFU_ERR_ADDRESS                0x08
+#define DFU_ERR_NOTDONE                0x09
+#define DFU_ERR_FIRMWARE       0x0a
+#define DFU_ERR_VENDOR         0x0b
+#define DFU_ERR_USBR           0x0c
+#define DFU_ERR_POR            0x0d
+#define DFU_ERR_UNKNOWN                0x0e
+#define DFU_ERR_STALLEDPKT     0x0f
+
+/* DFU state */
+#define DFU_STATE_APP_IDLE             0
+#define DFU_STATE_APP_DETACH           1
+#define DFU_STATE_DFU_IDLE             2
+#define DFU_STATE_DFU_DNLOAD_SYNC      3
+#define DFU_STATE_DFU_DNLOAD_BUSY      4
+#define DFU_STATE_DFU_DNLOAD_IDLE      5
+#define DFU_STATE_DFU_MANIFEST_SYNC    6
+#define DFU_STATE_DFU_MANIFEST         7
+#define DFU_STATE_MANIFEST_WAIT_RESET  8
+#define DFU_STATE_UPLOAD_IDLE          9
+#define DFU_STATE_ERROR                        10
+
+/* DFU suffix */
+struct dfu_suffix {
+       uint16_t bcdDevice;
+       uint16_t idProduct;
+       uint16_t idVendor;
+       uint16_t bcdDFU;
+       uint8_t  ucDfuSignature[3];
+       uint8_t  bLength;
+       uint32_t dwCRC;
+} __attribute__ ((packed));
+#define DFU_SUFFIX_SIZE 16
+
+/* DFU interface */
+int dfu_detach(struct usb_dev_handle *udev, int intf);
+int dfu_upload(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size);
+int dfu_download(struct usb_dev_handle *udev, int intf, int block, char *buffer, int size);
+int dfu_get_status(struct usb_dev_handle *udev, int intf, struct dfu_status *status);
+int dfu_clear_status(struct usb_dev_handle *udev, int intf);
+int dfu_get_state(struct usb_dev_handle *udev, int intf, uint8_t *state);
+int dfu_abort(struct usb_dev_handle *udev, int intf);
diff --git a/tools/dfubabel.1 b/tools/dfubabel.1
new file mode 100644 (file)
index 0000000..5e0f805
--- /dev/null
@@ -0,0 +1,38 @@
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH DFUBABEL 8 "JUNE 6, 2005" "" ""
+
+.SH NAME
+dfubabel \- Babel DFU mode switching utility
+.SH SYNOPSIS
+.BR "dfubabel
+[
+.I options
+]
+.SH DESCRIPTION
+.B dfubabel
+is used to switch Babel devices into DFU mode.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible options.
+.TP
+.BI -q
+Don't display any messages.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/dfubabel.c b/tools/dfubabel.c
new file mode 100644 (file)
index 0000000..612accc
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <usb.h>
+
+#ifdef NEED_USB_GET_BUSSES
+static inline struct usb_bus *usb_get_busses(void)
+{
+       return usb_busses;
+}
+#endif
+
+struct device_info;
+
+struct device_id {
+       uint16_t vendor;
+       uint16_t product;
+       int (*func)(struct device_info *dev, int argc, char *argv[]);
+};
+
+struct device_info {
+       struct usb_device *dev;
+       struct device_id *id;
+};
+
+static int switch_babel(struct device_info *devinfo, int argc, char *argv[])
+{
+       char buf[3];
+       struct usb_dev_handle *udev;
+       int err;
+
+       memset(buf, 0, sizeof(buf));
+
+       buf[0] = 0x00;
+       buf[1] = 0x06;
+       buf[2] = 0x00;
+
+       udev = usb_open(devinfo->dev);
+       if (!udev)
+               return -errno;
+
+       if (usb_claim_interface(udev, 0) < 0) {
+               err = -errno;
+               usb_close(udev);
+               return err;
+       }
+
+       err = usb_bulk_write(udev, 0x02, buf, sizeof(buf), 10000);
+
+       if (err == 0) {
+               err = -1;
+               errno = EALREADY;
+       } else {
+               if (errno == ETIMEDOUT)
+                       err = 0;
+       }
+
+       usb_release_interface(udev, 0);
+       usb_close(udev);
+
+       return err;
+}
+
+static struct device_id device_list[] = {
+       { 0x0a12, 0x0042, switch_babel },
+       { -1 }
+};
+
+static struct device_id *match_device(uint16_t vendor, uint16_t product)
+{
+       int i;
+
+       for (i = 0; device_list[i].func; i++) {
+               if (vendor == device_list[i].vendor &&
+                               product == device_list[i].product)
+                       return &device_list[i];
+       }
+
+       return NULL;
+}
+
+static int find_devices(struct device_info *devinfo, size_t size)
+{
+       struct usb_bus *bus;
+       struct usb_device *dev;
+       struct device_id *id;
+       unsigned int count = 0;
+
+       usb_find_busses();
+       usb_find_devices();
+
+       for (bus = usb_get_busses(); bus; bus = bus->next)
+               for (dev = bus->devices; dev; dev = dev->next) {
+                       id = match_device(dev->descriptor.idVendor,
+                                               dev->descriptor.idProduct);
+                       if (!id)
+                               continue;
+
+                       if (count < size) {
+                               devinfo[count].dev = dev;
+                               devinfo[count].id = id;
+                               count++;
+                       }
+               }
+
+       return count;
+}
+
+static void usage(void)
+{
+       printf("dfubabel - Babel DFU mode switching utility\n\n");
+
+       printf("Usage:\n"
+               "\tdfubabel [options]\n"
+               "\n");
+
+       printf("Options:\n"
+               "\t-h, --help           Display help\n"
+               "\t-q, --quiet          Don't display any messages\n"
+               "\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "quiet",      0, 0, 'q' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       struct device_info dev[16];
+       int i, opt, num, quiet = 0;
+
+       while ((opt = getopt_long(argc, argv, "+qh", main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'q':
+                       quiet = 1;
+                       break;
+               case 'h':
+                       usage();
+                       exit(0);
+               default:
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       usb_init();
+
+       num = find_devices(dev, sizeof(dev) / sizeof(dev[0]));
+       if (num <= 0) {
+               if (!quiet)
+                       fprintf(stderr, "No Babel devices found\n");
+               exit(1);
+       }
+
+       for (i = 0; i < num; i++) {
+               struct device_id *id = dev[i].id;
+               int err;
+
+               if (!quiet)
+                       printf("Switching device %04x:%04x ",
+                                               id->vendor, id->product);
+               fflush(stdout);
+
+               err = id->func(&dev[i], argc, argv);
+               if (err < 0) {
+                       if (!quiet)
+                               printf("failed (%s)\n", strerror(-err));
+               } else {
+                       if (!quiet)
+                               printf("was successful\n");
+               }
+       }
+
+       return 0;
+}
diff --git a/tools/dfutool.1 b/tools/dfutool.1
new file mode 100644 (file)
index 0000000..115114b
--- /dev/null
@@ -0,0 +1,53 @@
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH DFUTOOL 1 "APRIL 21, 2005" "" ""
+
+.SH NAME
+dfutool \- Device Firmware Upgrade utility
+.SH SYNOPSIS
+.BR "dfutool
+[
+.I options
+] <
+.I command
+>
+.SH DESCRIPTION
+.B dfutool
+is used to verify, archive and upgrade firmware files.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -d " <device>"
+The command specifies the device to use.
+.SH COMMANDS
+.TP
+.BI verify " <dfu-file>"
+Display information about the firmware file.
+.TP
+.BI modify " <dfu-file>"
+Change DFU specific values in the firmware file.
+.TP
+.BI upgrade " <dfu-file>"
+Upgrade the device with a new firmware.
+.TP
+.BI archive " <dfu-file>"
+Archive the current firmware of the device.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/dfutool.c b/tools/dfutool.c
new file mode 100644 (file)
index 0000000..16dd62e
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <libgen.h>
+#include <endian.h>
+#include <byteswap.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <usb.h>
+
+#include "dfu.h"
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le16(d)  (d)
+#define cpu_to_le32(d)  (d)
+#define le16_to_cpu(d)  (d)
+#define le32_to_cpu(d)  (d)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_le16(d)  bswap_16(d)
+#define cpu_to_le32(d)  bswap_32(d)
+#define le16_to_cpu(d)  bswap_16(d)
+#define le32_to_cpu(d)  bswap_32(d)
+#else
+#error "Unknown byte order"
+#endif
+
+#ifdef NEED_USB_GET_BUSSES
+static inline struct usb_bus *usb_get_busses(void)
+{
+       return usb_busses;
+}
+#endif
+
+#ifndef USB_CLASS_WIRELESS
+#define USB_CLASS_WIRELESS     0xe0
+#endif
+
+#ifndef USB_CLASS_APPLICATION
+#define USB_CLASS_APPLICATION  0xfe
+#endif
+
+static int get_interface_number(struct usb_device *dev)
+{
+       int c, i, a;
+
+       for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
+               struct usb_config_descriptor *config = &dev->config[c];
+
+               for (i = 0; i < config->bNumInterfaces; i++) {
+                       struct usb_interface *interface = &config->interface[i];
+
+                       for (a = 0; a < interface->num_altsetting; a++) {
+                               struct usb_interface_descriptor *desc = &interface->altsetting[a];
+
+                               if (desc->bInterfaceClass != USB_CLASS_APPLICATION)
+                                       continue;
+                               if (desc->bInterfaceSubClass != 0x01)
+                                       continue;
+                               if (desc->bInterfaceProtocol != 0x00)
+                                       continue;
+
+                               return desc->bInterfaceNumber;
+                       }
+               }
+       }
+
+       return -1;
+}
+
+static void print_device(struct usb_device *dev)
+{
+       printf("Bus %s Device %s: ID %04x:%04x Interface %d%s\n",
+               dev->bus->dirname, dev->filename,
+               dev->descriptor.idVendor, dev->descriptor.idProduct,
+               get_interface_number(dev),
+               dev->descriptor.bDeviceClass == USB_CLASS_APPLICATION ? " (DFU mode)" : "");
+}
+
+static struct usb_dev_handle *open_device(char *device, struct dfu_suffix *suffix)
+{
+       struct usb_bus *bus;
+       struct usb_device *dev, *dfu_dev[10];
+       struct usb_dev_handle *udev;
+       struct dfu_status status;
+       char str[8];
+       int i, intf, sel = 0, num = 0, try = 5, bus_id = -1, dev_id = -1;
+
+       printf("Scanning USB busses ... ");
+       fflush(stdout);
+
+       usb_find_busses();
+       usb_find_devices();
+
+       for (bus = usb_get_busses(); bus; bus = bus->next) {
+               if (bus_id > 0) {
+                       snprintf(str, sizeof(str) - 1, "%03i", bus_id);
+                       if (strcmp(str, bus->dirname))
+                               continue;
+               }
+
+               for (dev = bus->devices; dev; dev = dev->next) {
+                       if (bus_id > 0 && dev_id > 0) {
+                               snprintf(str, sizeof(str) - 1, "%03i", dev_id);
+                               if (strcmp(str, dev->filename))
+                                       continue;
+                       }
+
+                       if (dev->descriptor.bDeviceClass == USB_CLASS_HUB)
+                               continue;
+
+                       if (num > 9 || get_interface_number(dev) < 0)
+                               continue;
+
+                       dfu_dev[num++] = dev;
+               }
+       }
+
+       if (num < 1) {
+               printf("\rCan't find any DFU devices\n");
+               return NULL;
+       }
+
+       printf("\rAvailable devices with DFU support:\n\n");
+       for (i = 0; i < num; i++) {
+               printf("\t%2d) ", i + 1);
+               print_device(dfu_dev[i]);
+       }
+       printf("\n");
+
+       do {
+               printf("\rSelect device (abort with 0): ");
+               fflush(stdout);
+               memset(str, 0, sizeof(str));
+               if (!fgets(str, sizeof(str) - 1, stdin))
+                       continue;
+               sel = atoi(str);
+       } while (!isdigit(str[0]) || sel < 0 || sel > num );
+
+       if (sel < 1)
+               return NULL;
+
+       sel--;
+       intf = get_interface_number(dfu_dev[sel]);
+       printf("\n");
+
+       udev = usb_open(dfu_dev[sel]);
+       if (!udev) {
+               printf("Can't open device: %s (%d)\n", strerror(errno), errno);
+               return NULL;
+       }
+
+       if (usb_claim_interface(udev, intf) < 0) {
+               printf("Can't claim interface: %s (%d)\n", strerror(errno), errno);
+               usb_close(udev);
+               return NULL;
+       }
+
+       if (dfu_get_status(udev, intf, &status) < 0) {
+               printf("Can't get status: %s (%d)\n", strerror(errno), errno);
+               goto error;
+       }
+
+       if (status.bState == DFU_STATE_ERROR) {
+               if (dfu_clear_status(udev, intf) < 0) {
+                       printf("Can't clear status: %s (%d)\n", strerror(errno), errno);
+                       goto error;
+               }
+               if (dfu_abort(udev, intf) < 0) {
+                       printf("Can't abort previous action: %s (%d)\n", strerror(errno), errno);
+                       goto error;
+               }
+               if (dfu_get_status(udev, intf, &status) < 0) {
+                       printf("Can't get status: %s (%d)\n", strerror(errno), errno);
+                       goto error;
+               }
+       }
+
+       if (status.bState == DFU_STATE_DFU_IDLE) {
+               if (suffix) {
+                       suffix->idVendor  = cpu_to_le16(0x0000);
+                       suffix->idProduct = cpu_to_le16(0x0000);
+                       suffix->bcdDevice = cpu_to_le16(0x0000);
+               }
+               return udev;
+       }
+
+       if (status.bState != DFU_STATE_APP_IDLE) {
+               printf("Device is not idle, can't detach it (state %d)\n", status.bState);
+               goto error;
+       }
+
+       printf("Switching device into DFU mode ... ");
+       fflush(stdout);
+
+       if (suffix) {
+               suffix->idVendor  = cpu_to_le16(dfu_dev[sel]->descriptor.idVendor);
+               suffix->idProduct = cpu_to_le16(dfu_dev[sel]->descriptor.idProduct);
+               suffix->bcdDevice = cpu_to_le16(dfu_dev[sel]->descriptor.bcdDevice);
+       }
+
+       if (dfu_detach(udev, intf) < 0) {
+               printf("\rCan't detach device: %s (%d)\n", strerror(errno), errno);
+               goto error;
+       }
+
+       if (dfu_get_status(udev, intf, &status) < 0) {
+               printf("\rCan't get status: %s (%d)\n", strerror(errno), errno);
+               goto error;
+       }
+
+       if (status.bState != DFU_STATE_APP_DETACH) {
+               printf("\rDevice is not in detach mode, try again\n");
+               goto error;
+       }
+
+       usb_release_interface(udev, intf);
+       usb_reset(udev);
+       usb_close(udev);
+
+       bus = dfu_dev[sel]->bus;
+       num = 0;
+
+       while (num != 1 && try-- > 0) {
+               sleep(1);
+               usb_find_devices();
+
+               for (dev = bus->devices; dev; dev = dev->next) {
+                       if (dev->descriptor.bDeviceClass != USB_CLASS_APPLICATION)
+                               continue;
+
+                       if (suffix && dev->descriptor.idVendor != le16_to_cpu(suffix->idVendor))
+                               continue;
+
+                       if (num > 9 || get_interface_number(dev) != 0)
+                               continue;
+
+                       dfu_dev[num++] = dev;
+               }
+       }
+
+       if (num != 1) {
+               printf("\rCan't identify device with DFU mode\n");
+               goto error;
+       }
+
+       printf("\r");
+
+       intf = 0;
+
+       udev = usb_open(dfu_dev[0]);
+       if (!udev) {
+               printf("Can't open device: %s (%d)\n", strerror(errno), errno);
+               return NULL;
+       }
+
+       if (usb_claim_interface(udev, intf) < 0) {
+               printf("Can't claim interface: %s (%d)\n", strerror(errno), errno);
+               usb_close(udev);
+               return NULL;
+       }
+
+       if (dfu_get_status(udev, intf, &status) < 0) {
+               printf("Can't get status: %s (%d)\n", strerror(errno), errno);
+               goto error;
+       }
+
+       if (status.bState != DFU_STATE_DFU_IDLE) {
+               printf("Device is not in DFU mode, can't use it\n");
+               goto error;
+       }
+
+       return udev;
+
+error:
+       usb_release_interface(udev, intf);
+       usb_close(udev);
+       return NULL;
+}
+
+static void usage(void);
+
+static void cmd_verify(char *device, int argc, char **argv)
+{
+       struct stat st;
+       struct dfu_suffix *suffix;
+       uint32_t crc;
+       uint16_t bcd;
+       char str[16];
+       unsigned char *buf;
+       size_t size;
+       char *filename;
+       unsigned int i, len;
+       int fd;
+
+       if (argc < 2) {
+               usage();
+               exit(1);
+       }
+
+       filename = argv[1];
+
+       if (stat(filename, &st) < 0) {
+               perror("Can't access firmware");
+               exit(1);
+       }
+
+       size = st.st_size;
+
+       if (!(buf = malloc(size))) {
+               perror("Unable to allocate file buffer");
+               exit(1);
+       }
+
+       if ((fd = open(filename, O_RDONLY)) < 0) {
+               perror("Can't open firmware");
+               free(buf);
+               exit(1);
+       }
+
+       if (read(fd, buf, size) < (ssize_t) size) {
+               perror("Can't load firmware");
+               free(buf);
+               close(fd);
+               exit(1);
+       }
+
+       printf("Filename\t%s\n", basename(filename));
+       printf("Filesize\t%zd\n", size);
+
+       crc = crc32_init();
+       for (i = 0; i < size - 4; i++)
+               crc = crc32_byte(crc, buf[i]);
+       printf("Checksum\t%08x\n", crc);
+
+       printf("\n");
+       len = buf[size - 5];
+       printf("DFU suffix\t");
+       for (i = 0; i < len; i++) {
+               printf("%02x ", buf[size - len + i]);
+       }
+       printf("\n\n");
+
+       suffix = (struct dfu_suffix *) (buf + size - DFU_SUFFIX_SIZE);
+
+       printf("idVendor\t%04x\n", le16_to_cpu(suffix->idVendor));
+       printf("idProduct\t%04x\n", le16_to_cpu(suffix->idProduct));
+       printf("bcdDevice\t%x\n", le16_to_cpu(suffix->bcdDevice));
+
+       printf("\n");
+
+       bcd = le16_to_cpu(suffix->bcdDFU);
+
+       printf("bcdDFU\t\t%x.%x\n", bcd >> 8, bcd & 0xff);
+       printf("ucDfuSignature\t%c%c%c\n", suffix->ucDfuSignature[2],
+               suffix->ucDfuSignature[1], suffix->ucDfuSignature[0]);
+       printf("bLength\t\t%d\n", suffix->bLength);
+       printf("dwCRC\t\t%08x\n", le32_to_cpu(suffix->dwCRC));
+       printf("\n");
+
+       memset(str, 0, sizeof(str));
+       memcpy(str, buf, 8);
+
+       if (!strcmp(str, "CSR-dfu1") || !strcmp(str, "CSR-dfu2")) {
+               crc = crc32_init();
+               for (i = 0; i < size - DFU_SUFFIX_SIZE; i++)
+                       crc = crc32_byte(crc, buf[i]);
+
+               printf("Firmware type\t%s\n", str);
+               printf("Firmware check\t%s checksum\n", crc == 0 ? "valid" : "corrupt");
+               printf("\n");
+       }
+
+       free(buf);
+
+       close(fd);
+}
+
+static void cmd_modify(char *device, int argc, char **argv)
+{
+}
+
+static void cmd_upgrade(char *device, int argc, char **argv)
+{
+       struct usb_dev_handle *udev;
+       struct dfu_status status;
+       struct dfu_suffix suffix;
+       struct stat st;
+       char *buf;
+       size_t filesize;
+       unsigned long count, timeout = 0;
+       char *filename;
+       uint32_t crc, dwCRC;
+       unsigned int i;
+       int fd, block, len, size, sent = 0, try = 10;
+
+       if (argc < 2) {
+               usage();
+               exit(1);
+       }
+
+       filename = argv[1];
+
+       if (stat(filename, &st) < 0) {
+               perror("Can't access firmware");
+               exit(1);
+       }
+
+       filesize = st.st_size;
+
+       if (!(buf = malloc(filesize))) {
+               perror("Unable to allocate file buffer");
+               exit(1);
+       }
+
+       if ((fd = open(filename, O_RDONLY)) < 0) {
+               perror("Can't open firmware");
+               free(buf);
+               exit(1);
+       }
+
+       if (read(fd, buf, filesize) < (ssize_t) filesize) {
+               perror("Can't load firmware");
+               free(buf);
+               close(fd);
+               exit(1);
+       }
+
+       memcpy(&suffix, buf + filesize - DFU_SUFFIX_SIZE, sizeof(suffix));
+       dwCRC = le32_to_cpu(suffix.dwCRC);
+
+       printf("Filename\t%s\n", basename(filename));
+       printf("Filesize\t%zd\n", filesize);
+
+       crc = crc32_init();
+       for (i = 0; i < filesize - 4; i++)
+               crc = crc32_byte(crc, buf[i]);
+
+       printf("Checksum\t%08x (%s)\n", crc,
+                       crc == dwCRC ? "valid" : "corrupt");
+
+       if (crc != dwCRC) {
+               free(buf);
+               close(fd);
+               exit(1);
+       }
+
+       printf("\n");
+
+       udev = open_device(device, &suffix);
+       if (!udev)
+               exit(1);
+
+       printf("\r" "          " "          " "          " "          " "          ");
+       printf("\rFirmware download ... ");
+       fflush(stdout);
+
+       count = filesize - DFU_SUFFIX_SIZE;
+       block = 0;
+
+       while (count) {
+               size = (count > 1023) ? 1023 : count;
+
+               if (dfu_get_status(udev, 0, &status) < 0) {
+                       if (try-- > 0) {
+                               sleep(1);
+                               continue;
+                       }
+                       printf("\rCan't get status: %s (%d)\n", strerror(errno), errno);
+                       goto done;
+               }
+
+               if (status.bStatus != DFU_OK) {
+                       if (try-- > 0) {
+                               dfu_clear_status(udev, 0);
+                               sleep(1);
+                               continue;
+                       }
+                       printf("\rFirmware download ... aborting (status %d state %d)\n",
+                                               status.bStatus, status.bState);
+                       goto done;
+               }
+
+               if (status.bState != DFU_STATE_DFU_IDLE &&
+                               status.bState != DFU_STATE_DFU_DNLOAD_IDLE) {
+                       sleep(1);
+                       continue;
+               }
+
+               timeout = (status.bwPollTimeout[2] << 16) |
+                               (status.bwPollTimeout[1] << 8) |
+                                       status.bwPollTimeout[0];
+
+               usleep(timeout * 1000);
+
+               len = dfu_download(udev, 0, block, buf + sent, size);
+               if (len < 0) {
+                       if (try-- > 0) {
+                               sleep(1);
+                               continue;
+                       }
+                       printf("\rCan't upload next block: %s (%d)\n", strerror(errno), errno);
+                       goto done;
+               }
+
+               printf("\rFirmware download ... %d bytes ", block * 1023 + len);
+               fflush(stdout);
+
+               sent  += len;
+               count -= len;
+               block++;
+       }
+
+       printf("\r" "          " "          " "          " "          " "          ");
+       printf("\rFinishing firmware download ... ");
+       fflush(stdout);
+
+       sleep(1);
+
+       if (dfu_get_status(udev, 0, &status) < 0) {
+               printf("\rCan't get status: %s (%d)\n", strerror(errno), errno);
+               goto done;
+       }
+
+       timeout = (status.bwPollTimeout[2] << 16) |
+                       (status.bwPollTimeout[1] << 8) |
+                               status.bwPollTimeout[0];
+
+       usleep(timeout * 1000);
+
+       if (count == 0) {
+               len = dfu_download(udev, 0, block, NULL, 0);
+               if (len < 0) {
+                       printf("\rCan't send final block: %s (%d)\n", strerror(errno), errno);
+                       goto done;
+               }
+       }
+
+       printf("\r" "          " "          " "          " "          " "          ");
+       printf("\rWaiting for device ... ");
+       fflush(stdout);
+
+       sleep(10);
+
+       printf("\n");
+
+done:
+       free(buf);
+       close(fd);
+
+       usb_release_interface(udev, 0);
+       usb_reset(udev);
+       usb_close(udev);
+}
+
+static void cmd_archive(char *device, int argc, char **argv)
+{
+       struct usb_dev_handle *udev;
+       struct dfu_status status;
+       struct dfu_suffix suffix;
+       char buf[2048];
+       unsigned long timeout = 0;
+       char *filename;
+       uint32_t crc;
+       int fd, i, n, len, try = 8;
+
+       if (argc < 2) {
+               usage();
+               exit(1);
+       }
+
+       filename = argv[1];
+
+       udev = open_device(device, &suffix);
+       if (!udev)
+               exit(1);
+
+       fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+       if (fd < 0) {
+               printf("Can't open firmware file: %s (%d)\n", strerror(errno), errno);
+               goto done;
+       }
+
+       printf("\r" "          " "          " "          " "          " "          ");
+       printf("\rFirmware upload ... ");
+       fflush(stdout);
+
+       crc = crc32_init();
+       n = 0;
+       while (1) {
+               if (dfu_get_status(udev, 0, &status) < 0) {
+                       if (try-- > 0) {
+                               sleep(1);
+                               continue;
+                       }
+                       printf("\rCan't get status: %s (%d)\n", strerror(errno), errno);
+                       goto done;
+               }
+
+               if (status.bStatus != DFU_OK) {
+                       if (try-- > 0) {
+                               dfu_clear_status(udev, 0);
+                               sleep(1);
+                               continue;
+                       }
+                       printf("\rFirmware upload ... aborting (status %d state %d)\n",
+                                               status.bStatus, status.bState);
+                       goto done;
+               }
+
+               if (status.bState != DFU_STATE_DFU_IDLE &&
+                               status.bState != DFU_STATE_UPLOAD_IDLE) {
+                       sleep(1);
+                       continue;
+               }
+
+               timeout = (status.bwPollTimeout[2] << 16) |
+                               (status.bwPollTimeout[1] << 8) |
+                                       status.bwPollTimeout[0];
+
+               usleep(timeout * 1000);
+
+               len = dfu_upload(udev, 0, n, buf, 1023);
+               if (len < 0) {
+                       if (try-- > 0) {
+                               sleep(1);
+                               continue;
+                       }
+                       printf("\rCan't upload next block: %s (%d)\n", strerror(errno), errno);
+                       goto done;
+               }
+
+               printf("\rFirmware upload ... %d bytes ", n * 1023 + len);
+               fflush(stdout);
+
+               for (i = 0; i < len; i++)
+                       crc = crc32_byte(crc, buf[i]);
+
+               if (len > 0) {
+                       if (write(fd, buf, len) < 0) {
+                               printf("\rCan't write next block: %s (%d)\n", strerror(errno), errno);
+                               goto done;
+                       }
+               }
+
+               n++;
+               if (len != 1023)
+                       break;
+       }
+       printf("\n");
+
+       suffix.bcdDFU = cpu_to_le16(0x0100);
+       suffix.ucDfuSignature[0] = 'U';
+       suffix.ucDfuSignature[1] = 'F';
+       suffix.ucDfuSignature[2] = 'D';
+       suffix.bLength = DFU_SUFFIX_SIZE;
+
+       memcpy(buf, &suffix, DFU_SUFFIX_SIZE);
+       for (i = 0; i < DFU_SUFFIX_SIZE - 4; i++)
+               crc = crc32_byte(crc, buf[i]);
+
+       suffix.dwCRC = cpu_to_le32(crc);
+
+       if (write(fd, &suffix, DFU_SUFFIX_SIZE) < 0)
+               printf("Can't write suffix block: %s (%d)\n", strerror(errno), errno);
+
+done:
+       close(fd);
+
+       usb_release_interface(udev, 0);
+       usb_reset(udev);
+       usb_close(udev);
+}
+
+struct {
+       char *cmd;
+       char *alt;
+       void (*func)(char *device, int argc, char **argv);
+       char *opt;
+       char *doc;
+} command[] = {
+       { "verify",  "check",    cmd_verify,  "<dfu-file>", "Check firmware file"         },
+       { "modify",  "change",   cmd_modify,  "<dfu-file>", "Change firmware attributes"  },
+       { "upgrade", "download", cmd_upgrade, "<dfu-file>", "Download a new firmware"     },
+       { "archive", "upload",   cmd_archive, "<dfu-file>", "Upload the current firmware" },
+       { NULL, NULL, NULL, 0, 0 }
+};
+
+static void usage(void)
+{
+       int i;
+
+       printf("dfutool - Device Firmware Upgrade utility ver %s\n\n", VERSION);
+
+       printf("Usage:\n"
+               "\tdfutool [options] <command>\n"
+               "\n");
+
+       printf("Options:\n"
+               "\t-d, --device <device>   USB device\n"
+               "\t-h, --help              Display help\n"
+               "\n");
+
+       printf("Commands:\n");
+       for (i = 0; command[i].cmd; i++)
+               printf("\t%-8s %-10s\t%s\n", command[i].cmd,
+               command[i].opt ? command[i].opt : " ",
+               command[i].doc);
+       printf("\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "device",     1, 0, 'd' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       char *device = NULL;
+       int i, opt;
+
+       while ((opt = getopt_long(argc, argv, "+d:h", main_options, NULL)) != -1) {
+               switch(opt) {
+               case 'd':
+                       device = strdup(optarg);
+                       break;
+
+               case 'h':
+                       usage();
+                       exit(0);
+
+               default:
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       usb_init();
+
+       for (i = 0; command[i].cmd; i++) {
+               if (strcmp(command[i].cmd, argv[0]) && strcmp(command[i].alt, argv[0]))
+                       continue;
+               command[i].func(device, argc, argv);
+               exit(0);
+       }
+
+       usage();
+       exit(1);
+}
diff --git a/tools/hciattach.8 b/tools/hciattach.8
new file mode 100644 (file)
index 0000000..cc97cad
--- /dev/null
@@ -0,0 +1,158 @@
+.TH HCIATTACH 8 "Jan 22 2002" BlueZ "Linux System Administration"
+.SH NAME
+hciattach \- attach serial devices via UART HCI to BlueZ stack
+.SH SYNOPSIS
+.B hciattach
+.RB [\| \-b \|]
+.RB [\| \-n \|]
+.RB [\| \-p \|]
+.RB [\| \-t
+.IR timeout \|]
+.RB [\| \-s
+.IR speed \|]
+.RB [\| \-l \|]
+.RB [\| \-r \|]
+.I tty
+.IR type \||\| id
+.I speed
+.I flow
+.I bdaddr
+.SH DESCRIPTION
+.LP
+Hciattach is used to attach a serial UART to the Bluetooth stack as HCI
+transport interface.
+.SH OPTIONS
+.TP
+.B \-b
+Send break.
+.TP
+.B \-n
+Don't detach from controlling terminal.
+.TP
+.B \-p
+Print the PID when detaching.
+.TP
+.BI \-t " timeout"
+Specify an initialization timeout.  (Default is 5 seconds.)
+.TP
+.BI \-s " speed"
+Specify an initial speed instead of the hardware default.
+.TP
+.B \-l
+List all available configurations.
+.TP
+.B \-r
+Set the HCI device into raw mode (the kernel and bluetoothd will ignore it).
+.TP
+.I tty
+This specifies the serial device to attach. A leading
+.B /dev
+can be omitted. Examples:
+.B /dev/ttyS1
+.B ttyS2
+.TP
+.IR type \||\| id
+The
+.I type
+or
+.I id
+of the Bluetooth device that is to be attached, i.e. vendor or other device
+specific identifier. Currently supported types are
+.RS
+.TP
+.B type
+.B description
+.TP
+.B any
+Unspecified HCI_UART interface, no vendor specific options
+.TP
+.B ericsson
+Ericsson based modules
+.TP
+.B digi
+Digianswer based cards
+.TP
+.B xircom
+Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter
+.TP
+.B csr
+CSR Casira serial adapter or BrainBoxes serial dongle (BL642)
+.TP
+.B bboxes
+BrainBoxes PCMCIA card (BL620)
+.TP
+.B swave
+Silicon Wave kits
+.TP
+.B bcsp
+Serial adapters using CSR chips with BCSP serial protocol
+.TP
+.B ath3k
+Atheros AR300x based serial Bluetooth device
+.TP
+.B intel
+Intel Bluetooth device
+.RE
+
+Supported IDs are (manufacturer id, product id)
+.RS
+.TP
+.B 0x0105, 0x080a
+Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter
+.TP
+.B 0x0160, 0x0002
+BrainBoxes PCMCIA card (BL620)
+.RE
+
+.TP
+.I speed
+The
+.I speed
+specifies the UART speed to use. Baudrates higher than 115.200bps require
+vendor specific initializations that are not implemented for all types of
+devices. In general the following speeds are supported:
+
+.B 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
+
+Supported vendor devices are automatically initialised to their respective
+best settings.
+.TP
+.I flow
+If the keyword
+.I flow
+is appended to the list of options then hardware flow control is forced on
+the serial link (
+.B CRTSCTS
+). All above mentioned device types have
+.B flow
+set by default. To force no flow control use
+.B noflow
+instead.
+.TP
+.I sleep
+Enables hardware specific power management feature. If
+.I sleep
+is appended to the list of options then this feature is enabled. To disable
+this feature use
+.B nosleep
+instead.
+All above mentioned device types have
+.B nosleep
+set by default.
+
+Note: This option will only be valid for hardware which support
+hardware specific power management enable option from host.
+.TP
+.I bdaddr
+The
+.I bdaddr
+specifies the Bluetooth Address to use.  Some devices (like the STLC2500)
+do not store the Bluetooth address in hardware memory.  Instead it must
+be uploaded during the initialization process.  If this argument
+is specified, then the address will be used to initialize the device.
+Otherwise, a default address will be used.
+
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com>
+.PP
+Manual page by Nils Faerber <nils@kernelconcepts.de>
diff --git a/tools/hciattach.c b/tools/hciattach.c
new file mode 100644 (file)
index 0000000..d19fa33
--- /dev/null
@@ -0,0 +1,1467 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+struct uart_t {
+       char *type;
+       int  m_id;
+       int  p_id;
+       int  proto;
+       int  init_speed;
+       int  speed;
+       int  flags;
+       int  pm;
+       char *bdaddr;
+       int  (*init) (int fd, struct uart_t *u, struct termios *ti);
+       int  (*post) (int fd, struct uart_t *u, struct termios *ti);
+};
+
+#define FLOW_CTL       0x0001
+#define ENABLE_PM      1
+#define DISABLE_PM     0
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+}
+
+static void sig_term(int sig)
+{
+       __io_canceled = 1;
+}
+
+static void sig_alarm(int sig)
+{
+       fprintf(stderr, "Initialization timed out.\n");
+       exit(1);
+}
+
+static int uart_speed(int s)
+{
+       switch (s) {
+       case 9600:
+               return B9600;
+       case 19200:
+               return B19200;
+       case 38400:
+               return B38400;
+       case 57600:
+               return B57600;
+       case 115200:
+               return B115200;
+       case 230400:
+               return B230400;
+       case 460800:
+               return B460800;
+       case 500000:
+               return B500000;
+       case 576000:
+               return B576000;
+       case 921600:
+               return B921600;
+       case 1000000:
+               return B1000000;
+       case 1152000:
+               return B1152000;
+       case 1500000:
+               return B1500000;
+       case 2000000:
+               return B2000000;
+#ifdef B2500000
+       case 2500000:
+               return B2500000;
+#endif
+#ifdef B3000000
+       case 3000000:
+               return B3000000;
+#endif
+#ifdef B3500000
+       case 3500000:
+               return B3500000;
+#endif
+#ifdef B3710000
+       case 3710000
+               return B3710000;
+#endif
+#ifdef B4000000
+       case 4000000:
+               return B4000000;
+#endif
+       default:
+               return B57600;
+       }
+}
+
+int set_speed(int fd, struct termios *ti, int speed)
+{
+       if (cfsetospeed(ti, uart_speed(speed)) < 0)
+               return -errno;
+
+       if (cfsetispeed(ti, uart_speed(speed)) < 0)
+               return -errno;
+
+       if (tcsetattr(fd, TCSANOW, ti) < 0)
+               return -errno;
+
+       return 0;
+}
+
+/*
+ * Read an HCI event from the given file descriptor.
+ */
+int read_hci_event(int fd, unsigned char* buf, int size)
+{
+       int remain, r;
+       int count = 0;
+
+       if (size <= 0)
+               return -1;
+
+       /* The first byte identifies the packet type. For HCI event packets, it
+        * should be 0x04, so we read until we get to the 0x04. */
+       while (1) {
+               r = read(fd, buf, 1);
+               if (r <= 0)
+                       return -1;
+               if (buf[0] == 0x04)
+                       break;
+       }
+       count++;
+
+       /* The next two bytes are the event code and parameter total length. */
+       while (count < 3) {
+               r = read(fd, buf + count, 3 - count);
+               if (r <= 0)
+                       return -1;
+               count += r;
+       }
+
+       /* Now we read the parameters. */
+       if (buf[2] < (size - 3))
+               remain = buf[2];
+       else
+               remain = size - 3;
+
+       while ((count - 3) < remain) {
+               r = read(fd, buf + count, remain - (count - 3));
+               if (r <= 0)
+                       return -1;
+               count += r;
+       }
+
+       return count;
+}
+
+/*
+ * Ericsson specific initialization
+ */
+static int ericsson(int fd, struct uart_t *u, struct termios *ti)
+{
+       struct timespec tm = {0, 50000};
+       char cmd[5];
+
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x09;
+       cmd[2] = 0xfc;
+       cmd[3] = 0x01;
+
+       switch (u->speed) {
+       case 57600:
+               cmd[4] = 0x03;
+               break;
+       case 115200:
+               cmd[4] = 0x02;
+               break;
+       case 230400:
+               cmd[4] = 0x01;
+               break;
+       case 460800:
+               cmd[4] = 0x00;
+               break;
+       case 921600:
+               cmd[4] = 0x20;
+               break;
+       case 2000000:
+               cmd[4] = 0x25;
+               break;
+       case 3000000:
+               cmd[4] = 0x27;
+               break;
+       case 4000000:
+               cmd[4] = 0x2B;
+               break;
+       default:
+               cmd[4] = 0x03;
+               u->speed = 57600;
+               fprintf(stderr, "Invalid speed requested, using %d bps instead\n", u->speed);
+               break;
+       }
+
+       /* Send initialization command */
+       if (write(fd, cmd, 5) != 5) {
+               perror("Failed to write init command");
+               return -1;
+       }
+
+       nanosleep(&tm, NULL);
+       return 0;
+}
+
+/*
+ * Digianswer specific initialization
+ */
+static int digi(int fd, struct uart_t *u, struct termios *ti)
+{
+       struct timespec tm = {0, 50000};
+       char cmd[5];
+
+       /* DigiAnswer set baud rate command */
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x07;
+       cmd[2] = 0xfc;
+       cmd[3] = 0x01;
+
+       switch (u->speed) {
+       case 57600:
+               cmd[4] = 0x08;
+               break;
+       case 115200:
+               cmd[4] = 0x09;
+               break;
+       default:
+               cmd[4] = 0x09;
+               u->speed = 115200;
+               break;
+       }
+
+       /* Send initialization command */
+       if (write(fd, cmd, 5) != 5) {
+               perror("Failed to write init command");
+               return -1;
+       }
+
+       nanosleep(&tm, NULL);
+       return 0;
+}
+
+static int texas(int fd, struct uart_t *u, struct termios *ti)
+{
+       return texas_init(fd, &u->speed, ti);
+}
+
+static int texas2(int fd, struct uart_t *u, struct termios *ti)
+{
+       return texas_post(fd, ti);
+}
+
+static int texasalt(int fd, struct uart_t *u, struct termios *ti)
+{
+       return texasalt_init(fd, u->speed, ti);
+}
+
+static int ath3k_ps(int fd, struct uart_t *u, struct termios *ti)
+{
+       return ath3k_init(fd, u->speed, u->init_speed, u->bdaddr, ti);
+}
+
+static int ath3k_pm(int fd, struct uart_t *u, struct termios *ti)
+{
+       return ath3k_post(fd, u->pm);
+}
+
+static int qualcomm(int fd, struct uart_t *u, struct termios *ti)
+{
+       return qualcomm_init(fd, u->speed, ti, u->bdaddr);
+}
+
+static int intel(int fd, struct uart_t *u, struct termios *ti)
+{
+       return intel_init(fd, u->init_speed, &u->speed, ti);
+}
+
+static int read_check(int fd, void *buf, int count)
+{
+       int res;
+
+       do {
+               res = read(fd, buf, count);
+               if (res != -1) {
+                       buf += res;
+                       count -= res;
+               }
+       } while (count && (errno == 0 || errno == EINTR));
+
+       if (count)
+               return -1;
+
+       return 0;
+}
+
+/*
+ * BCSP specific initialization
+ */
+static int serial_fd;
+static int bcsp_max_retries = 10;
+
+static void bcsp_tshy_sig_alarm(int sig)
+{
+       unsigned char bcsp_sync_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xda,0xdc,0xed,0xed,0xc0};
+       static int retries = 0;
+
+       if (retries < bcsp_max_retries) {
+               retries++;
+               if (write(serial_fd, &bcsp_sync_pkt, 10) < 0)
+                       return;
+               alarm(1);
+               return;
+       }
+
+       tcflush(serial_fd, TCIOFLUSH);
+       fprintf(stderr, "BCSP initialization timed out\n");
+       exit(1);
+}
+
+static void bcsp_tconf_sig_alarm(int sig)
+{
+       unsigned char bcsp_conf_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xad,0xef,0xac,0xed,0xc0};
+       static int retries = 0;
+
+       if (retries < bcsp_max_retries){
+               retries++;
+               if (write(serial_fd, &bcsp_conf_pkt, 10) < 0)
+                       return;
+               alarm(1);
+               return;
+       }
+
+       tcflush(serial_fd, TCIOFLUSH);
+       fprintf(stderr, "BCSP initialization timed out\n");
+       exit(1);
+}
+
+static int bcsp(int fd, struct uart_t *u, struct termios *ti)
+{
+       unsigned char byte, bcsph[4], bcspp[4],
+               bcsp_sync_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xac,0xaf,0xef,0xee,0xc0},
+               bcsp_conf_resp_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xde,0xad,0xd0,0xd0,0xc0},
+               bcspsync[4]     = {0xda, 0xdc, 0xed, 0xed},
+               bcspsyncresp[4] = {0xac,0xaf,0xef,0xee},
+               bcspconf[4]     = {0xad,0xef,0xac,0xed},
+               bcspconfresp[4] = {0xde,0xad,0xd0,0xd0};
+       struct sigaction sa;
+       int len;
+
+       if (set_speed(fd, ti, u->speed) < 0) {
+               perror("Can't set default baud rate");
+               return -1;
+       }
+
+       ti->c_cflag |= PARENB;
+       ti->c_cflag &= ~(PARODD);
+
+       if (tcsetattr(fd, TCSANOW, ti) < 0) {
+               perror("Can't set port settings");
+               return -1;
+       }
+
+       alarm(0);
+
+       serial_fd = fd;
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags = SA_NOCLDSTOP;
+       sa.sa_handler = bcsp_tshy_sig_alarm;
+       sigaction(SIGALRM, &sa, NULL);
+
+       /* State = shy */
+
+       bcsp_tshy_sig_alarm(0);
+       while (1) {
+               do {
+                       if (read_check(fd, &byte, 1) == -1){
+                               perror("Failed to read");
+                               return -1;
+                       }
+               } while (byte != 0xC0);
+
+               do {
+                       if ( read_check(fd, &bcsph[0], 1) == -1){
+                               perror("Failed to read");
+                               return -1;
+                       }
+               } while (bcsph[0] == 0xC0);
+
+               if ( read_check(fd, &bcsph[1], 3) == -1){
+                       perror("Failed to read");
+                       return -1;
+               }
+
+               if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3])
+                       continue;
+               if (bcsph[1] != 0x41 || bcsph[2] != 0x00)
+                       continue;
+
+               if (read_check(fd, &bcspp, 4) == -1){
+                       perror("Failed to read");
+                       return -1;
+               }
+
+               if (!memcmp(bcspp, bcspsync, 4)) {
+                       if (write(fd, &bcsp_sync_resp_pkt,10) < 0)
+                               return -1;
+               } else if (!memcmp(bcspp, bcspsyncresp, 4))
+                       break;
+       }
+
+       /* State = curious */
+
+       alarm(0);
+       sa.sa_handler = bcsp_tconf_sig_alarm;
+       sigaction(SIGALRM, &sa, NULL);
+       alarm(1);
+
+       while (1) {
+               do {
+                       if (read_check(fd, &byte, 1) == -1){
+                               perror("Failed to read");
+                               return -1;
+                       }
+               } while (byte != 0xC0);
+
+               do {
+                       if (read_check(fd, &bcsph[0], 1) == -1){
+                               perror("Failed to read");
+                               return -1;
+                       }
+               } while (bcsph[0] == 0xC0);
+
+               if (read_check(fd, &bcsph[1], 3) == -1){
+                       perror("Failed to read");
+                       return -1;
+               }
+
+               if (((bcsph[0] + bcsph[1] + bcsph[2]) & 0xFF) != (unsigned char)~bcsph[3])
+                       continue;
+
+               if (bcsph[1] != 0x41 || bcsph[2] != 0x00)
+                       continue;
+
+               if (read_check(fd, &bcspp, 4) == -1){
+                       perror("Failed to read");
+                       return -1;
+               }
+
+               if (!memcmp(bcspp, bcspsync, 4))
+                       len = write(fd, &bcsp_sync_resp_pkt, 10);
+               else if (!memcmp(bcspp, bcspconf, 4))
+                       len = write(fd, &bcsp_conf_resp_pkt, 10);
+               else if (!memcmp(bcspp, bcspconfresp,  4))
+                       break;
+               else
+                       continue;
+
+               if (len < 0)
+                       return -errno;
+       }
+
+       /* State = garrulous */
+
+       return 0;
+}
+
+/*
+ * CSR specific initialization
+ * Inspired strongly by code in OpenBT and experimentations with Brainboxes
+ * Pcmcia card.
+ * Jean Tourrilhes <jt@hpl.hp.com> - 14.11.01
+ */
+static int csr(int fd, struct uart_t *u, struct termios *ti)
+{
+       struct timespec tm = {0, 10000000};     /* 10ms - be generous */
+       unsigned char cmd[30];          /* Command */
+       unsigned char resp[30];         /* Response */
+       int  clen = 0;          /* Command len */
+       static int csr_seq = 0; /* Sequence number of command */
+       int  divisor;
+
+       /* It seems that if we set the CSR UART speed straight away, it
+        * won't work, the CSR UART gets into a state where we can't talk
+        * to it anymore.
+        * On the other hand, doing a read before setting the CSR speed
+        * seems to be ok.
+        * Therefore, the strategy is to read the build ID (useful for
+        * debugging) and only then set the CSR UART speed. Doing like
+        * this is more complex but at least it works ;-)
+        * The CSR UART control may be slow to wake up or something because
+        * every time I read its speed, its bogus...
+        * Jean II */
+
+       /* Try to read the build ID of the CSR chip */
+       clen = 5 + (5 + 6) * 2;
+       /* HCI header */
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x00;          /* CSR command */
+       cmd[2] = 0xfc;          /* MANUFACTURER_SPEC */
+       cmd[3] = 1 + (5 + 6) * 2;       /* len */
+       /* CSR MSG header */
+       cmd[4] = 0xC2;          /* first+last+channel=BCC */
+       /* CSR BCC header */
+       cmd[5] = 0x00;          /* type = GET-REQ */
+       cmd[6] = 0x00;          /* - msB */
+       cmd[7] = 5 + 4;         /* len */
+       cmd[8] = 0x00;          /* - msB */
+       cmd[9] = csr_seq & 0xFF;/* seq num */
+       cmd[10] = (csr_seq >> 8) & 0xFF;        /* - msB */
+       csr_seq++;
+       cmd[11] = 0x19;         /* var_id = CSR_CMD_BUILD_ID */
+       cmd[12] = 0x28;         /* - msB */
+       cmd[13] = 0x00;         /* status = STATUS_OK */
+       cmd[14] = 0x00;         /* - msB */
+       /* CSR BCC payload */
+       memset(cmd + 15, 0, 6 * 2);
+
+       /* Send command */
+       do {
+               if (write(fd, cmd, clen) != clen) {
+                       perror("Failed to write init command (GET_BUILD_ID)");
+                       return -1;
+               }
+
+               /* Read reply. */
+               if (read_hci_event(fd, resp, 100) < 0) {
+                       perror("Failed to read init response (GET_BUILD_ID)");
+                       return -1;
+               }
+
+       /* Event code 0xFF is for vendor-specific events, which is
+        * what we're looking for. */
+       } while (resp[1] != 0xFF);
+
+#ifdef CSR_DEBUG
+       {
+       char temp[512];
+       int i;
+       for (i=0; i < rlen; i++)
+               sprintf(temp + (i*3), "-%02X", resp[i]);
+       fprintf(stderr, "Reading CSR build ID %d [%s]\n", rlen, temp + 1);
+       // In theory, it should look like :
+       // 04-FF-13-FF-01-00-09-00-00-00-19-28-00-00-73-00-00-00-00-00-00-00
+       }
+#endif
+       /* Display that to user */
+       fprintf(stderr, "CSR build ID 0x%02X-0x%02X\n",
+               resp[15] & 0xFF, resp[14] & 0xFF);
+
+       /* Try to read the current speed of the CSR chip */
+       clen = 5 + (5 + 4)*2;
+       /* -- HCI header */
+       cmd[3] = 1 + (5 + 4)*2; /* len */
+       /* -- CSR BCC header -- */
+       cmd[9] = csr_seq & 0xFF;        /* seq num */
+       cmd[10] = (csr_seq >> 8) & 0xFF;        /* - msB */
+       csr_seq++;
+       cmd[11] = 0x02;         /* var_id = CONFIG_UART */
+       cmd[12] = 0x68;         /* - msB */
+
+#ifdef CSR_DEBUG
+       /* Send command */
+       do {
+               if (write(fd, cmd, clen) != clen) {
+                       perror("Failed to write init command (GET_BUILD_ID)");
+                       return -1;
+               }
+
+               /* Read reply. */
+               if (read_hci_event(fd, resp, 100) < 0) {
+                       perror("Failed to read init response (GET_BUILD_ID)");
+                       return -1;
+               }
+
+       /* Event code 0xFF is for vendor-specific events, which is
+        * what we're looking for. */
+       } while (resp[1] != 0xFF);
+
+       {
+       char temp[512];
+       int i;
+       for (i=0; i < rlen; i++)
+               sprintf(temp + (i*3), "-%02X", resp[i]);
+       fprintf(stderr, "Reading CSR UART speed %d [%s]\n", rlen, temp+1);
+       }
+#endif
+
+       if (u->speed > 1500000) {
+               fprintf(stderr, "Speed %d too high. Remaining at %d baud\n",
+                       u->speed, u->init_speed);
+               u->speed = u->init_speed;
+       } else if (u->speed != 57600 && uart_speed(u->speed) == B57600) {
+               /* Unknown speed. Why oh why can't we just pass an int to the kernel? */
+               fprintf(stderr, "Speed %d unrecognised. Remaining at %d baud\n",
+                       u->speed, u->init_speed);
+               u->speed = u->init_speed;
+       }
+       if (u->speed == u->init_speed)
+               return 0;
+
+       /* Now, create the command that will set the UART speed */
+       /* CSR BCC header */
+       cmd[5] = 0x02;                  /* type = SET-REQ */
+       cmd[6] = 0x00;                  /* - msB */
+       cmd[9] = csr_seq & 0xFF;        /* seq num */
+       cmd[10] = (csr_seq >> 8) & 0xFF;/* - msB */
+       csr_seq++;
+
+       divisor = (u->speed*64+7812)/15625;
+
+       /* No parity, one stop bit -> divisor |= 0x0000; */
+       cmd[15] = (divisor) & 0xFF;             /* divider */
+       cmd[16] = (divisor >> 8) & 0xFF;        /* - msB */
+       /* The rest of the payload will be 0x00 */
+
+#ifdef CSR_DEBUG
+       {
+       char temp[512];
+       int i;
+       for(i = 0; i < clen; i++)
+               sprintf(temp + (i*3), "-%02X", cmd[i]);
+       fprintf(stderr, "Writing CSR UART speed %d [%s]\n", clen, temp + 1);
+       // In theory, it should look like :
+       // 01-00-FC-13-C2-02-00-09-00-03-00-02-68-00-00-BF-0E-00-00-00-00-00-00
+       // 01-00-FC-13-C2-02-00-09-00-01-00-02-68-00-00-D8-01-00-00-00-00-00-00
+       }
+#endif
+
+       /* Send the command to set the CSR UART speed */
+       if (write(fd, cmd, clen) != clen) {
+               perror("Failed to write init command (SET_UART_SPEED)");
+               return -1;
+       }
+
+       nanosleep(&tm, NULL);
+       return 0;
+}
+
+/*
+ * Silicon Wave specific initialization
+ * Thomas Moser <thomas.moser@tmoser.ch>
+ */
+static int swave(int fd, struct uart_t *u, struct termios *ti)
+{
+       struct timespec tm = { 0, 500000 };
+       char cmd[10], rsp[100];
+       int r;
+
+       // Silicon Wave set baud rate command
+       // see HCI Vendor Specific Interface from Silicon Wave
+       // first send a "param access set" command to set the
+       // appropriate data fields in RAM. Then send a "HCI Reset
+       // Subcommand", e.g. "soft reset" to make the changes effective.
+
+       cmd[0] = HCI_COMMAND_PKT;       // it's a command packet
+       cmd[1] = 0x0B;                  // OCF 0x0B     = param access set
+       cmd[2] = 0xfc;                  // OGF bx111111 = vendor specific
+       cmd[3] = 0x06;                  // 6 bytes of data following
+       cmd[4] = 0x01;                  // param sub command
+       cmd[5] = 0x11;                  // tag 17 = 0x11 = HCI Transport Params
+       cmd[6] = 0x03;                  // length of the parameter following
+       cmd[7] = 0x01;                  // HCI Transport flow control enable
+       cmd[8] = 0x01;                  // HCI Transport Type = UART
+
+       switch (u->speed) {
+       case 19200:
+               cmd[9] = 0x03;
+               break;
+       case 38400:
+               cmd[9] = 0x02;
+               break;
+       case 57600:
+               cmd[9] = 0x01;
+               break;
+       case 115200:
+               cmd[9] = 0x00;
+               break;
+       default:
+               u->speed = 115200;
+               cmd[9] = 0x00;
+               break;
+       }
+
+       /* Send initialization command */
+       if (write(fd, cmd, 10) != 10) {
+               perror("Failed to write init command");
+               return -1;
+       }
+
+       // We should wait for a "GET Event" to confirm the success of
+       // the baud rate setting. Wait some time before reading. Better:
+       // read with timeout, parse data
+       // until correct answer, else error handling ... todo ...
+
+       nanosleep(&tm, NULL);
+
+       r = read(fd, rsp, sizeof(rsp));
+       if (r > 0) {
+               // guess it's okay, but we should parse the reply. But since
+               // I don't react on an error anyway ... todo
+               // Response packet format:
+               //  04  Event
+               //  FF  Vendor specific
+               //  07  Parameter length
+               //  0B  Subcommand
+               //  01  Setevent
+               //  11  Tag specifying HCI Transport Layer Parameter
+               //  03  length
+               //  01  flow on
+               //  01  Hci Transport type = Uart
+               //  xx  Baud rate set (see above)
+       } else {
+               // ups, got error.
+               return -1;
+       }
+
+       // we probably got the reply. Now we must send the "soft reset"
+       // which is standard HCI RESET.
+
+       cmd[0] = HCI_COMMAND_PKT;       // it's a command packet
+       cmd[1] = 0x03;
+       cmd[2] = 0x0c;
+       cmd[3] = 0x00;
+
+       /* Send reset command */
+       if (write(fd, cmd, 4) != 4) {
+               perror("Can't write Silicon Wave reset cmd.");
+               return -1;
+       }
+
+       nanosleep(&tm, NULL);
+
+       // now the uart baud rate on the silicon wave module is set and effective.
+       // change our own baud rate as well. Then there is a reset event coming in
+       // on the *new* baud rate. This is *undocumented*! The packet looks like this:
+       // 04 FF 01 0B (which would make that a confirmation of 0x0B = "Param
+       // subcommand class". So: change to new baud rate, read with timeout, parse
+       // data, error handling. BTW: all param access in Silicon Wave is done this way.
+       // Maybe this code would belong in a separate file, or at least code reuse...
+
+       return 0;
+}
+
+/*
+ * ST Microelectronics specific initialization
+ * Marcel Holtmann <marcel@holtmann.org>
+ */
+static int st(int fd, struct uart_t *u, struct termios *ti)
+{
+       struct timespec tm = {0, 50000};
+       char cmd[5];
+
+       /* ST Microelectronics set baud rate command */
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x46;                  // OCF = Hci_Cmd_ST_Set_Uart_Baud_Rate
+       cmd[2] = 0xfc;                  // OGF = Vendor specific
+       cmd[3] = 0x01;
+
+       switch (u->speed) {
+       case 9600:
+               cmd[4] = 0x09;
+               break;
+       case 19200:
+               cmd[4] = 0x0b;
+               break;
+       case 38400:
+               cmd[4] = 0x0d;
+               break;
+       case 57600:
+               cmd[4] = 0x0e;
+               break;
+       case 115200:
+               cmd[4] = 0x10;
+               break;
+       case 230400:
+               cmd[4] = 0x12;
+               break;
+       case 460800:
+               cmd[4] = 0x13;
+               break;
+       case 921600:
+               cmd[4] = 0x14;
+               break;
+       default:
+               cmd[4] = 0x10;
+               u->speed = 115200;
+               break;
+       }
+
+       /* Send initialization command */
+       if (write(fd, cmd, 5) != 5) {
+               perror("Failed to write init command");
+               return -1;
+       }
+
+       nanosleep(&tm, NULL);
+       return 0;
+}
+
+static int stlc2500(int fd, struct uart_t *u, struct termios *ti)
+{
+       bdaddr_t bdaddr;
+       unsigned char resp[10];
+       int n;
+       int rvalue;
+
+       /* STLC2500 has an ericsson core */
+       rvalue = ericsson(fd, u, ti);
+       if (rvalue != 0)
+               return rvalue;
+
+#ifdef STLC2500_DEBUG
+       fprintf(stderr, "Setting speed\n");
+#endif
+       if (set_speed(fd, ti, u->speed) < 0) {
+               perror("Can't set baud rate");
+               return -1;
+       }
+
+#ifdef STLC2500_DEBUG
+       fprintf(stderr, "Speed set...\n");
+#endif
+
+       /* Read reply */
+       if ((n = read_hci_event(fd, resp, 10)) < 0) {
+               fprintf(stderr, "Failed to set baud rate on chip\n");
+               return -1;
+       }
+
+#ifdef STLC2500_DEBUG
+       for (i = 0; i < n; i++) {
+               fprintf(stderr, "resp[%d] = %02x\n", i, resp[i]);
+       }
+#endif
+
+       str2ba(u->bdaddr, &bdaddr);
+       return stlc2500_init(fd, &bdaddr);
+}
+
+static int bgb2xx(int fd, struct uart_t *u, struct termios *ti)
+{
+       bdaddr_t bdaddr;
+
+       str2ba(u->bdaddr, &bdaddr);
+
+       return bgb2xx_init(fd, &bdaddr);
+}
+
+/*
+ * Broadcom specific initialization
+ * Extracted from Jungo openrg
+ */
+static int bcm2035(int fd, struct uart_t *u, struct termios *ti)
+{
+       int n;
+       unsigned char cmd[30], resp[30];
+
+       /* Reset the BT Chip */
+       memset(cmd, 0, sizeof(cmd));
+       memset(resp, 0, sizeof(resp));
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x03;
+       cmd[2] = 0x0c;
+       cmd[3] = 0x00;
+
+       /* Send command */
+       if (write(fd, cmd, 4) != 4) {
+               fprintf(stderr, "Failed to write reset command\n");
+               return -1;
+       }
+
+       /* Read reply */
+       if ((n = read_hci_event(fd, resp, 4)) < 0) {
+               fprintf(stderr, "Failed to reset chip\n");
+               return -1;
+       }
+
+       if (u->bdaddr != NULL) {
+               /* Set BD_ADDR */
+               memset(cmd, 0, sizeof(cmd));
+               memset(resp, 0, sizeof(resp));
+               cmd[0] = HCI_COMMAND_PKT;
+               cmd[1] = 0x01;
+               cmd[2] = 0xfc;
+               cmd[3] = 0x06;
+               str2ba(u->bdaddr, (bdaddr_t *) (cmd + 4));
+
+               /* Send command */
+               if (write(fd, cmd, 10) != 10) {
+                       fprintf(stderr, "Failed to write BD_ADDR command\n");
+                       return -1;
+               }
+
+               /* Read reply */
+               if ((n = read_hci_event(fd, resp, 10)) < 0) {
+                       fprintf(stderr, "Failed to set BD_ADDR\n");
+                       return -1;
+               }
+       }
+
+       /* Read the local version info */
+       memset(cmd, 0, sizeof(cmd));
+       memset(resp, 0, sizeof(resp));
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x01;
+       cmd[2] = 0x10;
+       cmd[3] = 0x00;
+
+       /* Send command */
+       if (write(fd, cmd, 4) != 4) {
+               fprintf(stderr, "Failed to write \"read local version\" "
+                       "command\n");
+               return -1;
+       }
+
+       /* Read reply */
+       if ((n = read_hci_event(fd, resp, 4)) < 0) {
+               fprintf(stderr, "Failed to read local version\n");
+               return -1;
+       }
+
+       /* Read the local supported commands info */
+       memset(cmd, 0, sizeof(cmd));
+       memset(resp, 0, sizeof(resp));
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x02;
+       cmd[2] = 0x10;
+       cmd[3] = 0x00;
+
+       /* Send command */
+       if (write(fd, cmd, 4) != 4) {
+               fprintf(stderr, "Failed to write \"read local supported "
+                                               "commands\" command\n");
+               return -1;
+       }
+
+       /* Read reply */
+       if ((n = read_hci_event(fd, resp, 4)) < 0) {
+               fprintf(stderr, "Failed to read local supported commands\n");
+               return -1;
+       }
+
+       /* Set the baud rate */
+       memset(cmd, 0, sizeof(cmd));
+       memset(resp, 0, sizeof(resp));
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x18;
+       cmd[2] = 0xfc;
+       cmd[3] = 0x02;
+       switch (u->speed) {
+       case 57600:
+               cmd[4] = 0x00;
+               cmd[5] = 0xe6;
+               break;
+       case 230400:
+               cmd[4] = 0x22;
+               cmd[5] = 0xfa;
+               break;
+       case 460800:
+               cmd[4] = 0x22;
+               cmd[5] = 0xfd;
+               break;
+       case 921600:
+               cmd[4] = 0x55;
+               cmd[5] = 0xff;
+               break;
+       default:
+               /* Default is 115200 */
+               cmd[4] = 0x00;
+               cmd[5] = 0xf3;
+               break;
+       }
+       fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n",
+               cmd[4], cmd[5]);
+
+       /* Send command */
+       if (write(fd, cmd, 6) != 6) {
+               fprintf(stderr, "Failed to write \"set baud rate\" command\n");
+               return -1;
+       }
+
+       if ((n = read_hci_event(fd, resp, 6)) < 0) {
+               fprintf(stderr, "Failed to set baud rate\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+struct uart_t uart[] = {
+       { "any",        0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+                               FLOW_CTL, DISABLE_PM, NULL, NULL     },
+
+       { "ericsson",   0x0000, 0x0000, HCI_UART_H4,   57600,  115200,
+                               FLOW_CTL, DISABLE_PM, NULL, ericsson },
+
+       { "digi",       0x0000, 0x0000, HCI_UART_H4,   9600,   115200,
+                               FLOW_CTL, DISABLE_PM, NULL, digi     },
+
+       { "bcsp",       0x0000, 0x0000, HCI_UART_BCSP, 115200, 115200,
+                               0, DISABLE_PM, NULL, bcsp     },
+
+       /* Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter */
+       { "xircom",     0x0105, 0x080a, HCI_UART_H4,   115200, 115200,
+                               FLOW_CTL, DISABLE_PM,  NULL, NULL     },
+
+       /* CSR Casira serial adapter or BrainBoxes serial dongle (BL642) */
+       { "csr",        0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+                               FLOW_CTL, DISABLE_PM, NULL, csr      },
+
+       /* BrainBoxes PCMCIA card (BL620) */
+       { "bboxes",     0x0160, 0x0002, HCI_UART_H4,   115200, 460800,
+                               FLOW_CTL, DISABLE_PM, NULL, csr      },
+
+       /* Silicon Wave kits */
+       { "swave",      0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+                               FLOW_CTL, DISABLE_PM, NULL, swave    },
+
+       /* Texas Instruments Bluelink (BRF) modules */
+       { "texas",      0x0000, 0x0000, HCI_UART_LL,   115200, 115200,
+                               FLOW_CTL, DISABLE_PM, NULL, texas,    texas2 },
+
+       { "texasalt",   0x0000, 0x0000, HCI_UART_LL,   115200, 115200,
+                               FLOW_CTL, DISABLE_PM, NULL, texasalt, NULL   },
+
+       /* ST Microelectronics minikits based on STLC2410/STLC2415 */
+       { "st",         0x0000, 0x0000, HCI_UART_H4,    57600, 115200,
+                               FLOW_CTL, DISABLE_PM,  NULL, st       },
+
+       /* ST Microelectronics minikits based on STLC2500 */
+       { "stlc2500",   0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+                       FLOW_CTL, DISABLE_PM, "00:80:E1:00:AB:BA", stlc2500 },
+
+       /* Philips generic Ericsson IP core based */
+       { "philips",    0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+                               FLOW_CTL, DISABLE_PM, NULL, NULL     },
+
+       /* Philips BGB2xx Module */
+       { "bgb2xx",    0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+                       FLOW_CTL, DISABLE_PM, "BD:B2:10:00:AB:BA", bgb2xx },
+
+       /* Sphinx Electronics PICO Card */
+       { "picocard",   0x025e, 0x1000, HCI_UART_H4, 115200, 115200,
+                               FLOW_CTL, DISABLE_PM, NULL, NULL     },
+
+       /* Inventel BlueBird Module */
+       { "inventel",   0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+                               FLOW_CTL, DISABLE_PM, NULL, NULL     },
+
+       /* COM One Platinium Bluetooth PC Card */
+       { "comone",     0xffff, 0x0101, HCI_UART_BCSP, 115200, 115200,
+                               0, DISABLE_PM,  NULL, bcsp     },
+
+       /* TDK Bluetooth PC Card and IBM Bluetooth PC Card II */
+       { "tdk",        0x0105, 0x4254, HCI_UART_BCSP, 115200, 115200,
+                               0, DISABLE_PM, NULL, bcsp     },
+
+       /* Socket Bluetooth CF Card (Rev G) */
+       { "socket",     0x0104, 0x0096, HCI_UART_BCSP, 230400, 230400,
+                               0, DISABLE_PM, NULL, bcsp     },
+
+       /* 3Com Bluetooth Card (Version 3.0) */
+       { "3com",       0x0101, 0x0041, HCI_UART_H4,   115200, 115200,
+                               FLOW_CTL, DISABLE_PM, NULL, csr      },
+
+       /* AmbiCom BT2000C Bluetooth PC/CF Card */
+       { "bt2000c",    0x022d, 0x2000, HCI_UART_H4,    57600, 460800,
+                               FLOW_CTL, DISABLE_PM, NULL, csr      },
+
+       /* Zoom Bluetooth PCMCIA Card */
+       { "zoom",       0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200,
+                               0, DISABLE_PM, NULL, bcsp     },
+
+       /* Sitecom CN-504 PCMCIA Card */
+       { "sitecom",    0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200,
+                               0, DISABLE_PM, NULL, bcsp     },
+
+       /* Billionton PCBTC1 PCMCIA Card */
+       { "billionton", 0x0279, 0x950b, HCI_UART_BCSP, 115200, 115200,
+                               0, DISABLE_PM, NULL, bcsp     },
+
+       /* Broadcom BCM2035 */
+       { "bcm2035",    0x0A5C, 0x2035, HCI_UART_H4,   115200, 460800,
+                               FLOW_CTL, DISABLE_PM, NULL, bcm2035  },
+
+       { "ath3k",    0x0000, 0x0000, HCI_UART_ATH3K, 115200, 115200,
+                       FLOW_CTL, DISABLE_PM, NULL, ath3k_ps, ath3k_pm  },
+
+       /* QUALCOMM BTS */
+       { "qualcomm",   0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+                       FLOW_CTL, DISABLE_PM, NULL, qualcomm, NULL },
+
+       /* Intel Bluetooth Module */
+       { "intel",      0x0000, 0x0000, HCI_UART_H4,   115200, 115200,
+                       FLOW_CTL, DISABLE_PM, NULL, intel, NULL },
+
+       /* Three-wire UART */
+       { "3wire",      0x0000, 0x0000, HCI_UART_3WIRE, 115200, 115200,
+                       0, DISABLE_PM, NULL, NULL, NULL },
+
+       { NULL, 0 }
+};
+
+static struct uart_t * get_by_id(int m_id, int p_id)
+{
+       int i;
+       for (i = 0; uart[i].type; i++) {
+               if (uart[i].m_id == m_id && uart[i].p_id == p_id)
+                       return &uart[i];
+       }
+       return NULL;
+}
+
+static struct uart_t * get_by_type(char *type)
+{
+       int i;
+       for (i = 0; uart[i].type; i++) {
+               if (!strcmp(uart[i].type, type))
+                       return &uart[i];
+       }
+       return NULL;
+}
+
+/* Initialize UART driver */
+static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
+{
+       struct termios ti;
+       int fd, i;
+       unsigned long flags = 0;
+
+       if (raw)
+               flags |= 1 << HCI_UART_RAW_DEVICE;
+
+       fd = open(dev, O_RDWR | O_NOCTTY);
+       if (fd < 0) {
+               perror("Can't open serial port");
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (tcgetattr(fd, &ti) < 0) {
+               perror("Can't get port settings");
+               return -1;
+       }
+
+       cfmakeraw(&ti);
+
+       ti.c_cflag |= CLOCAL;
+       if (u->flags & FLOW_CTL)
+               ti.c_cflag |= CRTSCTS;
+       else
+               ti.c_cflag &= ~CRTSCTS;
+
+       if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+               perror("Can't set port settings");
+               return -1;
+       }
+
+       /* Set initial baudrate */
+       if (set_speed(fd, &ti, u->init_speed) < 0) {
+               perror("Can't set initial baud rate");
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (send_break) {
+               tcsendbreak(fd, 0);
+               usleep(500000);
+       }
+
+       if (u->init && u->init(fd, u, &ti) < 0)
+               return -1;
+
+       tcflush(fd, TCIOFLUSH);
+
+       /* Set actual baudrate */
+       if (set_speed(fd, &ti, u->speed) < 0) {
+               perror("Can't set baud rate");
+               return -1;
+       }
+
+       /* Set TTY to N_HCI line discipline */
+       i = N_HCI;
+       if (ioctl(fd, TIOCSETD, &i) < 0) {
+               perror("Can't set line discipline");
+               return -1;
+       }
+
+       if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
+               perror("Can't set UART flags");
+               return -1;
+       }
+
+       if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
+               perror("Can't set device");
+               return -1;
+       }
+
+       if (u->post && u->post(fd, u, &ti) < 0)
+               return -1;
+
+       return fd;
+}
+
+static void usage(void)
+{
+       printf("hciattach - HCI UART driver initialization utility\n");
+       printf("Usage:\n");
+       printf("\thciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]\n");
+       printf("\thciattach -l\n");
+}
+
+int main(int argc, char *argv[])
+{
+       struct uart_t *u = NULL;
+       int detach, printpid, raw, opt, i, n, ld, err;
+       int to = 10;
+       int init_speed = 0;
+       int send_break = 0;
+       pid_t pid;
+       struct sigaction sa;
+       struct pollfd p;
+       sigset_t sigs;
+       char dev[PATH_MAX];
+
+       detach = 1;
+       printpid = 0;
+       raw = 0;
+
+       while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF) {
+               switch(opt) {
+               case 'b':
+                       send_break = 1;
+                       break;
+
+               case 'n':
+                       detach = 0;
+                       break;
+
+               case 'p':
+                       printpid = 1;
+                       break;
+
+               case 't':
+                       to = atoi(optarg);
+                       break;
+
+               case 's':
+                       init_speed = atoi(optarg);
+                       break;
+
+               case 'l':
+                       for (i = 0; uart[i].type; i++) {
+                               printf("%-10s0x%04x,0x%04x\n", uart[i].type,
+                                                       uart[i].m_id, uart[i].p_id);
+                       }
+                       exit(0);
+
+               case 'r':
+                       raw = 1;
+                       break;
+
+               default:
+                       usage();
+                       exit(1);
+               }
+       }
+
+       n = argc - optind;
+       if (n < 2) {
+               usage();
+               exit(1);
+       }
+
+       for (n = 0; optind < argc; n++, optind++) {
+               char *opt;
+
+               opt = argv[optind];
+
+               switch(n) {
+               case 0:
+                       dev[0] = 0;
+                       if (!strchr(opt, '/'))
+                               strcpy(dev, "/dev/");
+                       strcat(dev, opt);
+                       break;
+
+               case 1:
+                       if (strchr(argv[optind], ',')) {
+                               int m_id, p_id;
+                               sscanf(argv[optind], "%x,%x", &m_id, &p_id);
+                               u = get_by_id(m_id, p_id);
+                       } else {
+                               u = get_by_type(opt);
+                       }
+
+                       if (!u) {
+                               fprintf(stderr, "Unknown device type or id\n");
+                               exit(1);
+                       }
+
+                       break;
+
+               case 2:
+                       u->speed = atoi(argv[optind]);
+                       break;
+
+               case 3:
+                       if (!strcmp("flow", argv[optind]))
+                               u->flags |=  FLOW_CTL;
+                       else
+                               u->flags &= ~FLOW_CTL;
+                       break;
+
+               case 4:
+                       if (!strcmp("sleep", argv[optind]))
+                               u->pm = ENABLE_PM;
+                       else
+                               u->pm = DISABLE_PM;
+                       break;
+
+               case 5:
+                       u->bdaddr = argv[optind];
+                       break;
+               }
+       }
+
+       if (!u) {
+               fprintf(stderr, "Unknown device type or id\n");
+               exit(1);
+       }
+
+       /* If user specified a initial speed, use that instead of
+          the hardware's default */
+       if (init_speed)
+               u->init_speed = init_speed;
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = sig_alarm;
+       sigaction(SIGALRM, &sa, NULL);
+
+       /* 10 seconds should be enough for initialization */
+       alarm(to);
+       bcsp_max_retries = to;
+
+       n = init_uart(dev, u, send_break, raw);
+       if (n < 0) {
+               perror("Can't initialize device");
+               exit(1);
+       }
+
+       printf("Device setup complete\n");
+
+       alarm(0);
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       if (detach) {
+               if ((pid = fork())) {
+                       if (printpid)
+                               printf("%d\n", pid);
+                       return 0;
+               }
+
+               for (i = 0; i < 20; i++)
+                       if (i != n)
+                               close(i);
+       }
+
+       p.fd = n;
+       p.events = POLLERR | POLLHUP;
+
+       sigfillset(&sigs);
+       sigdelset(&sigs, SIGCHLD);
+       sigdelset(&sigs, SIGPIPE);
+       sigdelset(&sigs, SIGTERM);
+       sigdelset(&sigs, SIGINT);
+       sigdelset(&sigs, SIGHUP);
+
+       while (!__io_canceled) {
+               p.revents = 0;
+               err = ppoll(&p, 1, NULL, &sigs);
+               if (err < 0 && errno == EINTR)
+                       continue;
+               if (err)
+                       break;
+       }
+
+       /* Restore TTY line discipline */
+       ld = N_TTY;
+       if (ioctl(n, TIOCSETD, &ld) < 0) {
+               perror("Can't restore line discipline");
+               exit(1);
+       }
+
+       return 0;
+}
diff --git a/tools/hciattach.h b/tools/hciattach.h
new file mode 100644 (file)
index 0000000..a24dbc4
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <termios.h>
+
+#ifndef N_HCI
+#define N_HCI  15
+#endif
+
+#define HCIUARTSETPROTO                _IOW('U', 200, int)
+#define HCIUARTGETPROTO                _IOR('U', 201, int)
+#define HCIUARTGETDEVICE       _IOR('U', 202, int)
+#define HCIUARTSETFLAGS                _IOW('U', 203, int)
+#define HCIUARTGETFLAGS                _IOR('U', 204, int)
+
+#define HCI_UART_H4    0
+#define HCI_UART_BCSP  1
+#define HCI_UART_3WIRE 2
+#define HCI_UART_H4DS  3
+#define HCI_UART_LL    4
+#define HCI_UART_ATH3K  5
+
+#define HCI_UART_RAW_DEVICE    0
+#define HCI_UART_RESET_ON_INIT 1
+#define HCI_UART_CREATE_AMP    2
+
+int read_hci_event(int fd, unsigned char* buf, int size);
+int set_speed(int fd, struct termios *ti, int speed);
+
+int texas_init(int fd, int *speed, struct termios *ti);
+int texas_post(int fd, struct termios *ti);
+int texasalt_init(int fd, int speed, struct termios *ti);
+int stlc2500_init(int fd, bdaddr_t *bdaddr);
+int bgb2xx_init(int dd, bdaddr_t *bdaddr);
+int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
+                                               struct termios *ti);
+int ath3k_post(int fd, int pm);
+int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr);
+int intel_init(int fd, int init_speed, int *speed, struct termios *ti);
diff --git a/tools/hciattach_ath3k.c b/tools/hciattach_ath3k.c
new file mode 100644 (file)
index 0000000..23208c6
--- /dev/null
@@ -0,0 +1,1044 @@
+/*
+ *  Copyright (c) 2009-2010 Atheros Communications Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#define TRUE    1
+#define FALSE   0
+
+#define FW_PATH "/lib/firmware/ar3k/"
+
+struct ps_cfg_entry {
+       uint32_t id;
+       uint32_t len;
+       uint8_t *data;
+};
+
+struct ps_entry_type {
+       unsigned char type;
+       unsigned char array;
+};
+
+#define MAX_TAGS              50
+#define PS_HDR_LEN            4
+#define HCI_VENDOR_CMD_OGF    0x3F
+#define HCI_PS_CMD_OCF        0x0B
+
+struct ps_cfg_entry ps_list[MAX_TAGS];
+
+static void load_hci_ps_hdr(uint8_t *cmd, uint8_t ps_op, int len, int index)
+{
+       hci_command_hdr *ch = (void *)cmd;
+
+       ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+                                               HCI_PS_CMD_OCF));
+       ch->plen = len + PS_HDR_LEN;
+       cmd += HCI_COMMAND_HDR_SIZE;
+
+       cmd[0] = ps_op;
+       cmd[1] = index;
+       cmd[2] = index >> 8;
+       cmd[3] = len;
+}
+
+#define PS_EVENT_LEN 100
+
+/*
+ * Send HCI command and wait for command complete event.
+ * The event buffer has to be freed by the caller.
+ */
+static int send_hci_cmd_sync(int dev, uint8_t *cmd, int len, uint8_t **event)
+{
+       int err;
+       uint8_t *hci_event;
+       uint8_t pkt_type = HCI_COMMAND_PKT;
+
+       if (len == 0)
+               return len;
+
+       if (write(dev, &pkt_type, 1) != 1)
+               return -EILSEQ;
+       if (write(dev, (unsigned char *)cmd, len) != len)
+               return -EILSEQ;
+
+       hci_event = (uint8_t *)malloc(PS_EVENT_LEN);
+       if (!hci_event)
+               return -ENOMEM;
+
+       err = read_hci_event(dev, (unsigned char *)hci_event, PS_EVENT_LEN);
+       if (err > 0) {
+               *event = hci_event;
+       } else {
+               free(hci_event);
+               return -EILSEQ;
+       }
+
+       return len;
+}
+
+#define HCI_EV_SUCCESS        0x00
+
+static int read_ps_event(uint8_t *event, uint16_t ocf)
+{
+       hci_event_hdr *eh;
+       uint16_t opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF, ocf));
+
+       event++;
+
+       eh = (void *)event;
+       event += HCI_EVENT_HDR_SIZE;
+
+       if (eh->evt == EVT_CMD_COMPLETE) {
+               evt_cmd_complete *cc = (void *)event;
+
+               event += EVT_CMD_COMPLETE_SIZE;
+
+               if (cc->opcode == opcode && event[0] == HCI_EV_SUCCESS)
+                       return 0;
+               else
+                       return -EILSEQ;
+       }
+
+       return -EILSEQ;
+}
+
+static int write_cmd(int fd, uint8_t *buffer, int len)
+{
+       uint8_t *event;
+       int err;
+
+       err = send_hci_cmd_sync(fd, buffer, len, &event);
+       if (err < 0)
+               return err;
+
+       err = read_ps_event(event, HCI_PS_CMD_OCF);
+
+       free(event);
+
+       return err;
+}
+
+#define PS_WRITE           1
+#define PS_RESET           2
+#define WRITE_PATCH        8
+#define ENABLE_PATCH       11
+
+#define HCI_PS_CMD_HDR_LEN 7
+
+#define PS_RESET_PARAM_LEN 6
+#define HCI_MAX_CMD_SIZE   260
+#define PS_RESET_CMD_LEN   (HCI_PS_CMD_HDR_LEN + PS_RESET_PARAM_LEN)
+
+#define PS_ID_MASK         0xFF
+
+/* Sends PS commands using vendor specficic HCI commands */
+static int write_ps_cmd(int fd, uint8_t opcode, uint32_t ps_param)
+{
+       uint8_t cmd[HCI_MAX_CMD_SIZE];
+       uint32_t i;
+
+       switch (opcode) {
+       case ENABLE_PATCH:
+               load_hci_ps_hdr(cmd, opcode, 0, 0x00);
+
+               if (write_cmd(fd, cmd, HCI_PS_CMD_HDR_LEN) < 0)
+                       return -EILSEQ;
+               break;
+
+       case PS_RESET:
+               load_hci_ps_hdr(cmd, opcode, PS_RESET_PARAM_LEN, 0x00);
+
+               cmd[7] = 0x00;
+               cmd[PS_RESET_CMD_LEN - 2] = ps_param & PS_ID_MASK;
+               cmd[PS_RESET_CMD_LEN - 1] = (ps_param >> 8) & PS_ID_MASK;
+
+               if (write_cmd(fd, cmd, PS_RESET_CMD_LEN) < 0)
+                       return -EILSEQ;
+               break;
+
+       case PS_WRITE:
+               for (i = 0; i < ps_param; i++) {
+                       load_hci_ps_hdr(cmd, opcode, ps_list[i].len,
+                                                       ps_list[i].id);
+
+                       memcpy(&cmd[HCI_PS_CMD_HDR_LEN], ps_list[i].data,
+                                                       ps_list[i].len);
+
+                       if (write_cmd(fd, cmd, ps_list[i].len +
+                                               HCI_PS_CMD_HDR_LEN) < 0)
+                               return -EILSEQ;
+               }
+               break;
+       }
+
+       return 0;
+}
+
+#define __is_delim(ch) ((ch) == ':')
+#define MAX_PREAMBLE_LEN 4
+
+/* Parse PS entry preamble of format [X:X] for main type and subtype */
+static int get_ps_type(char *ptr, int index, char *type, char *sub_type)
+{
+       int i;
+       int delim = FALSE;
+
+       if (index > MAX_PREAMBLE_LEN)
+               return -EILSEQ;
+
+       for (i = 1; i < index; i++) {
+               if (__is_delim(ptr[i])) {
+                       delim = TRUE;
+                       continue;
+               }
+
+               if (isalpha(ptr[i])) {
+                       if (delim == FALSE)
+                               (*type) = toupper(ptr[i]);
+                       else
+                               (*sub_type) = toupper(ptr[i]);
+               }
+       }
+
+       return 0;
+}
+
+#define ARRAY   'A'
+#define STRING  'S'
+#define DECIMAL 'D'
+#define BINARY  'B'
+
+#define PS_HEX           0
+#define PS_DEC           1
+
+static int get_input_format(char *buf, struct ps_entry_type *format)
+{
+       char *ptr = NULL;
+       char type = '\0';
+       char sub_type = '\0';
+
+       format->type = PS_HEX;
+       format->array = TRUE;
+
+       if (strstr(buf, "[") != buf)
+               return 0;
+
+       ptr = strstr(buf, "]");
+       if (!ptr)
+               return -EILSEQ;
+
+       if (get_ps_type(buf, ptr - buf, &type, &sub_type) < 0)
+               return -EILSEQ;
+
+       /* Check is data type is of array */
+       if (type == ARRAY || sub_type == ARRAY)
+               format->array = TRUE;
+
+       if (type == STRING || sub_type == STRING)
+               format->array = FALSE;
+
+       if (type == DECIMAL || type == BINARY)
+               format->type = PS_DEC;
+       else
+               format->type = PS_HEX;
+
+       return 0;
+}
+
+#define UNDEFINED 0xFFFF
+
+static unsigned int read_data_in_section(char *buf, struct ps_entry_type type)
+{
+       char *ptr = buf;
+
+       if (!buf)
+               return UNDEFINED;
+
+       if (buf == strstr(buf, "[")) {
+               ptr = strstr(buf, "]");
+               if (!ptr)
+                       return UNDEFINED;
+
+               ptr++;
+       }
+
+       if (type.type == PS_HEX && type.array != TRUE)
+               return strtol(ptr, NULL, 16);
+
+       return UNDEFINED;
+}
+
+struct tag_info {
+       unsigned section;
+       unsigned line_count;
+       unsigned char_cnt;
+       unsigned byte_count;
+};
+
+static inline int update_char_count(const char *buf)
+{
+       char *end_ptr;
+
+       if (strstr(buf, "[") == buf) {
+               end_ptr = strstr(buf, "]");
+               if (!end_ptr)
+                       return 0;
+               else
+                       return (end_ptr - buf) + 1;
+       }
+
+       return 0;
+}
+
+/* Read PS entries as string, convert and add to Hex array */
+static void update_tag_data(struct ps_cfg_entry *tag,
+                               struct tag_info *info, const char *ptr)
+{
+       char buf[3];
+
+       buf[2] = '\0';
+
+       strncpy(buf, &ptr[info->char_cnt], 2);
+       tag->data[info->byte_count] = strtol(buf, NULL, 16);
+       info->char_cnt += 3;
+       info->byte_count++;
+
+       strncpy(buf, &ptr[info->char_cnt], 2);
+       tag->data[info->byte_count] = strtol(buf, NULL, 16);
+       info->char_cnt += 3;
+       info->byte_count++;
+}
+
+#define PS_UNDEF   0
+#define PS_ID      1
+#define PS_LEN     2
+#define PS_DATA    3
+
+#define PS_MAX_LEN         500
+#define LINE_SIZE_MAX      (PS_MAX_LEN * 2)
+#define ENTRY_PER_LINE     16
+
+#define __check_comment(buf) (((buf)[0] == '/') && ((buf)[1] == '/'))
+#define __skip_space(str)      while (*(str) == ' ') ((str)++)
+
+static int ath_parse_ps(FILE *stream)
+{
+       char buf[LINE_SIZE_MAX + 1];
+       char *ptr;
+       uint8_t tag_cnt = 0;
+       int16_t byte_count = 0;
+       struct ps_entry_type format;
+       struct tag_info status = { 0, 0, 0, 0 };
+
+       do {
+               int read_count;
+               struct ps_cfg_entry *tag;
+
+               ptr = fgets(buf, LINE_SIZE_MAX, stream);
+               if (!ptr)
+                       break;
+
+               __skip_space(ptr);
+               if (__check_comment(ptr))
+                       continue;
+
+               /* Lines with a '#' will be followed by new PS entry */
+               if (ptr == strstr(ptr, "#")) {
+                       if (status.section != PS_UNDEF) {
+                               return -EILSEQ;
+                       } else {
+                               status.section = PS_ID;
+                               continue;
+                       }
+               }
+
+               tag = &ps_list[tag_cnt];
+
+               switch (status.section) {
+               case PS_ID:
+                       if (get_input_format(ptr, &format) < 0)
+                               return -EILSEQ;
+
+                       tag->id = read_data_in_section(ptr, format);
+                       status.section = PS_LEN;
+                       break;
+
+               case PS_LEN:
+                       if (get_input_format(ptr, &format) < 0)
+                               return -EILSEQ;
+
+                       byte_count = read_data_in_section(ptr, format);
+                       if (byte_count > PS_MAX_LEN)
+                               return -EILSEQ;
+
+                       tag->len = byte_count;
+                       tag->data = (uint8_t *)malloc(byte_count);
+
+                       status.section = PS_DATA;
+                       status.line_count = 0;
+                       break;
+
+               case PS_DATA:
+                       if (status.line_count == 0)
+                               if (get_input_format(ptr, &format) < 0)
+                                       return -EILSEQ;
+
+                       __skip_space(ptr);
+
+                       status.char_cnt = update_char_count(ptr);
+
+                       read_count = (byte_count > ENTRY_PER_LINE) ?
+                                       ENTRY_PER_LINE : byte_count;
+
+                       if (format.type == PS_HEX && format.array == TRUE) {
+                               while (read_count > 0) {
+                                       update_tag_data(tag, &status, ptr);
+                                       read_count -= 2;
+                               }
+
+                               if (byte_count > ENTRY_PER_LINE)
+                                       byte_count -= ENTRY_PER_LINE;
+                               else
+                                       byte_count = 0;
+                       }
+
+                       status.line_count++;
+
+                       if (byte_count == 0)
+                               memset(&status, 0x00, sizeof(struct tag_info));
+
+                       if (status.section == PS_UNDEF)
+                               tag_cnt++;
+
+                       if (tag_cnt == MAX_TAGS)
+                               return -EILSEQ;
+                       break;
+               }
+       } while (ptr);
+
+       return tag_cnt;
+}
+
+#define MAX_PATCH_CMD 244
+struct patch_entry {
+       int16_t len;
+       uint8_t data[MAX_PATCH_CMD];
+};
+
+#define SET_PATCH_RAM_ID       0x0D
+#define SET_PATCH_RAM_CMD_SIZE 11
+#define ADDRESS_LEN            4
+static int set_patch_ram(int dev, char *patch_loc, int len)
+{
+       int err;
+       uint8_t cmd[20];
+       int i, j;
+       char loc_byte[3];
+       uint8_t *event;
+       uint8_t *loc_ptr = &cmd[7];
+
+       if (!patch_loc)
+               return -1;
+
+       loc_byte[2] = '\0';
+
+       load_hci_ps_hdr(cmd, SET_PATCH_RAM_ID, ADDRESS_LEN, 0);
+
+       for (i = 0, j = 3; i < 4; i++, j--) {
+               loc_byte[0] = patch_loc[0];
+               loc_byte[1] = patch_loc[1];
+               loc_ptr[j] = strtol(loc_byte, NULL, 16);
+               patch_loc += 2;
+       }
+
+       err = send_hci_cmd_sync(dev, cmd, SET_PATCH_RAM_CMD_SIZE, &event);
+       if (err < 0)
+               return err;
+
+       err = read_ps_event(event, HCI_PS_CMD_OCF);
+
+       free(event);
+
+       return err;
+}
+
+#define PATCH_LOC_KEY    "DA:"
+#define PATCH_LOC_STRING_LEN    8
+static int ps_patch_download(int fd, FILE *stream)
+{
+       char byte[3];
+       char ptr[MAX_PATCH_CMD + 1];
+       int byte_cnt;
+       int patch_count = 0;
+       char patch_loc[PATCH_LOC_STRING_LEN + 1];
+
+       byte[2] = '\0';
+
+       while (fgets(ptr, MAX_PATCH_CMD, stream)) {
+               if (strlen(ptr) <= 1)
+                       continue;
+               else if (strstr(ptr, PATCH_LOC_KEY) == ptr) {
+                       strncpy(patch_loc, &ptr[sizeof(PATCH_LOC_KEY) - 1],
+                                                       PATCH_LOC_STRING_LEN);
+                       if (set_patch_ram(fd, patch_loc, sizeof(patch_loc)) < 0)
+                               return -1;
+               } else if (isxdigit(ptr[0]))
+                       break;
+               else
+                       return -1;
+       }
+
+       byte_cnt = strtol(ptr, NULL, 16);
+
+       while (byte_cnt > 0) {
+               int i;
+               uint8_t cmd[HCI_MAX_CMD_SIZE];
+               struct patch_entry patch;
+
+               if (byte_cnt > MAX_PATCH_CMD)
+                       patch.len = MAX_PATCH_CMD;
+               else
+                       patch.len = byte_cnt;
+
+               for (i = 0; i < patch.len; i++) {
+                       if (!fgets(byte, 3, stream))
+                               return -1;
+
+                       patch.data[i] = strtoul(byte, NULL, 16);
+               }
+
+               load_hci_ps_hdr(cmd, WRITE_PATCH, patch.len, patch_count);
+               memcpy(&cmd[HCI_PS_CMD_HDR_LEN], patch.data, patch.len);
+
+               if (write_cmd(fd, cmd, patch.len + HCI_PS_CMD_HDR_LEN) < 0)
+                       return -1;
+
+               patch_count++;
+               byte_cnt = byte_cnt - MAX_PATCH_CMD;
+       }
+
+       if (write_ps_cmd(fd, ENABLE_PATCH, 0) < 0)
+               return -1;
+
+       return patch_count;
+}
+
+#define PS_RAM_SIZE 2048
+
+static int ps_config_download(int fd, int tag_count)
+{
+       if (write_ps_cmd(fd, PS_RESET, PS_RAM_SIZE) < 0)
+               return -1;
+
+       if (tag_count > 0)
+               if (write_ps_cmd(fd, PS_WRITE, tag_count) < 0)
+                       return -1;
+       return 0;
+}
+
+#define PS_ASIC_FILE                   "PS_ASIC.pst"
+#define PS_FPGA_FILE                   "PS_FPGA.pst"
+
+static void get_ps_file_name(uint32_t devtype, uint32_t rom_version,
+                                                       char *path)
+{
+       char *filename;
+
+       if (devtype == 0xdeadc0de)
+               filename = PS_ASIC_FILE;
+       else
+               filename = PS_FPGA_FILE;
+
+       snprintf(path, MAXPATHLEN, "%s%x/%s", FW_PATH, rom_version, filename);
+}
+
+#define PATCH_FILE        "RamPatch.txt"
+#define FPGA_ROM_VERSION  0x99999999
+#define ROM_DEV_TYPE      0xdeadc0de
+
+static void get_patch_file_name(uint32_t dev_type, uint32_t rom_version,
+                               uint32_t build_version, char *path)
+{
+       if (rom_version == FPGA_ROM_VERSION && dev_type != ROM_DEV_TYPE &&
+                                       dev_type != 0 && build_version == 1)
+               path[0] = '\0';
+       else
+               snprintf(path, MAXPATHLEN, "%s%x/%s",
+                               FW_PATH, rom_version, PATCH_FILE);
+}
+
+#define VERIFY_CRC   9
+#define PS_REGION    1
+#define PATCH_REGION 2
+
+static int get_ath3k_crc(int dev)
+{
+       uint8_t cmd[7];
+       uint8_t *event;
+       int err;
+
+       load_hci_ps_hdr(cmd, VERIFY_CRC, 0, PS_REGION | PATCH_REGION);
+
+       err = send_hci_cmd_sync(dev, cmd, sizeof(cmd), &event);
+       if (err < 0)
+               return err;
+       /* Send error code if CRC check patched */
+       if (read_ps_event(event, HCI_PS_CMD_OCF) >= 0)
+               err = -EILSEQ;
+
+       free(event);
+
+       return err;
+}
+
+#define DEV_REGISTER      0x4FFC
+#define GET_DEV_TYPE_OCF  0x05
+
+static int get_device_type(int dev, uint32_t *code)
+{
+       uint8_t cmd[8];
+       uint8_t *event;
+       uint32_t reg;
+       int err;
+       uint8_t *ptr = cmd;
+       hci_command_hdr *ch = (void *)cmd;
+
+       ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+                                               GET_DEV_TYPE_OCF));
+       ch->plen = 5;
+       ptr += HCI_COMMAND_HDR_SIZE;
+
+       ptr[0] = (uint8_t)DEV_REGISTER;
+       ptr[1] = (uint8_t)DEV_REGISTER >> 8;
+       ptr[2] = (uint8_t)DEV_REGISTER >> 16;
+       ptr[3] = (uint8_t)DEV_REGISTER >> 24;
+       ptr[4] = 0x04;
+
+       err = send_hci_cmd_sync(dev, cmd, sizeof(cmd), &event);
+       if (err < 0)
+               return err;
+
+       err = read_ps_event(event, GET_DEV_TYPE_OCF);
+       if (err < 0)
+               goto cleanup;
+
+       reg = event[10];
+       reg = (reg << 8) | event[9];
+       reg = (reg << 8) | event[8];
+       reg = (reg << 8) | event[7];
+       *code = reg;
+
+cleanup:
+       free(event);
+
+       return err;
+}
+
+#define GET_VERSION_OCF 0x1E
+
+static int read_ath3k_version(int pConfig, uint32_t *rom_version,
+                                       uint32_t *build_version)
+{
+       uint8_t cmd[3];
+       uint8_t *event;
+       int err;
+       int status;
+       hci_command_hdr *ch = (void *)cmd;
+
+       ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+                                               GET_VERSION_OCF));
+       ch->plen = 0;
+
+       err = send_hci_cmd_sync(pConfig, cmd, sizeof(cmd), &event);
+       if (err < 0)
+               return err;
+
+       err = read_ps_event(event, GET_VERSION_OCF);
+       if (err < 0)
+               goto cleanup;
+
+       status = event[10];
+       status = (status << 8) | event[9];
+       status = (status << 8) | event[8];
+       status = (status << 8) | event[7];
+       *rom_version = status;
+
+       status = event[14];
+       status = (status << 8) | event[13];
+       status = (status << 8) | event[12];
+       status = (status << 8) | event[11];
+       *build_version = status;
+
+cleanup:
+       free(event);
+
+       return err;
+}
+
+static void convert_bdaddr(char *str_bdaddr, char *bdaddr)
+{
+       char bdbyte[3];
+       char *str_byte = str_bdaddr;
+       int i, j;
+       int colon_present = 0;
+
+       if (strstr(str_bdaddr, ":"))
+               colon_present = 1;
+
+       bdbyte[2] = '\0';
+
+       /* Reverse the BDADDR to LSB first */
+       for (i = 0, j = 5; i < 6; i++, j--) {
+               bdbyte[0] = str_byte[0];
+               bdbyte[1] = str_byte[1];
+               bdaddr[j] = strtol(bdbyte, NULL, 16);
+
+               if (colon_present == 1)
+                       str_byte += 3;
+               else
+                       str_byte += 2;
+       }
+}
+
+static int write_bdaddr(int pConfig, char *bdaddr)
+{
+       uint8_t *event;
+       int err;
+       uint8_t cmd[13];
+       uint8_t *ptr = cmd;
+       hci_command_hdr *ch = (void *)cmd;
+
+       memset(cmd, 0, sizeof(cmd));
+
+       ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+                                               HCI_PS_CMD_OCF));
+       ch->plen = 10;
+       ptr += HCI_COMMAND_HDR_SIZE;
+
+       ptr[0] = 0x01;
+       ptr[1] = 0x01;
+       ptr[2] = 0x00;
+       ptr[3] = 0x06;
+
+       convert_bdaddr(bdaddr, (char *)&ptr[4]);
+
+       err = send_hci_cmd_sync(pConfig, cmd, sizeof(cmd), &event);
+       if (err < 0)
+               return err;
+
+       err = read_ps_event(event, HCI_PS_CMD_OCF);
+
+       free(event);
+
+       return err;
+}
+
+#define BDADDR_FILE "ar3kbdaddr.pst"
+
+static void write_bdaddr_from_file(int rom_version, int fd)
+{
+       FILE *stream;
+       char bdaddr[PATH_MAX];
+       char bdaddr_file[PATH_MAX];
+
+       snprintf(bdaddr_file, MAXPATHLEN, "%s%x/%s",
+                       FW_PATH, rom_version, BDADDR_FILE);
+
+       stream = fopen(bdaddr_file, "r");
+       if (!stream)
+               return;
+
+       if (fgets(bdaddr, PATH_MAX - 1, stream))
+               write_bdaddr(fd, bdaddr);
+
+       fclose(stream);
+}
+
+static int ath_ps_download(int fd)
+{
+       int err = 0;
+       int tag_count;
+       int patch_count = 0;
+       uint32_t rom_version = 0;
+       uint32_t build_version = 0;
+       uint32_t dev_type = 0;
+       char patch_file[PATH_MAX];
+       char ps_file[PATH_MAX];
+       FILE *stream;
+
+       /*
+        * Verfiy firmware version. depending on it select the PS
+        * config file to download.
+        */
+       if (get_device_type(fd, &dev_type) < 0) {
+               err = -EILSEQ;
+               goto download_cmplete;
+       }
+
+       if (read_ath3k_version(fd, &rom_version, &build_version) < 0) {
+               err = -EILSEQ;
+               goto download_cmplete;
+       }
+
+       /* Do not download configuration if CRC passes */
+       if (get_ath3k_crc(fd) < 0) {
+               err = 0;
+               goto download_cmplete;
+       }
+
+       get_ps_file_name(dev_type, rom_version, ps_file);
+       get_patch_file_name(dev_type, rom_version, build_version, patch_file);
+
+       stream = fopen(ps_file, "r");
+       if (!stream) {
+               perror("firmware file open error\n");
+               err = -EILSEQ;
+               goto download_cmplete;
+       }
+       tag_count = ath_parse_ps(stream);
+
+       fclose(stream);
+
+       if (tag_count < 0) {
+               err = -EILSEQ;
+               goto download_cmplete;
+       }
+
+       /*
+        * It is not necessary that Patch file be available,
+        * continue with PS Operations if patch file is not available.
+        */
+       if (patch_file[0] == '\0')
+               err = 0;
+
+       stream = fopen(patch_file, "r");
+       if (!stream)
+               err = 0;
+       else {
+               patch_count = ps_patch_download(fd, stream);
+               fclose(stream);
+
+               if (patch_count < 0) {
+                       err = -EILSEQ;
+                       goto download_cmplete;
+               }
+       }
+
+       err = ps_config_download(fd, tag_count);
+
+download_cmplete:
+       if (!err)
+               write_bdaddr_from_file(rom_version, fd);
+
+       return err;
+}
+
+#define HCI_SLEEP_CMD_OCF     0x04
+
+/*
+ * Atheros AR300x specific initialization post callback
+ */
+int ath3k_post(int fd, int pm)
+{
+       int dev_id, dd;
+       struct timespec tm = { 0, 50000 };
+
+       sleep(1);
+
+       dev_id = ioctl(fd, HCIUARTGETDEVICE, 0);
+       if (dev_id < 0) {
+               perror("cannot get device id");
+               return dev_id;
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               return dd;
+       }
+
+       if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) {
+               perror("hci down:Power management Disabled");
+               hci_close_dev(dd);
+               return -1;
+       }
+
+       /* send vendor specific command with Sleep feature Enabled */
+       if (hci_send_cmd(dd, OGF_VENDOR_CMD, HCI_SLEEP_CMD_OCF, 1, &pm) < 0)
+               perror("PM command failed, power management Disabled");
+
+       nanosleep(&tm, NULL);
+       hci_close_dev(dd);
+
+       return 0;
+}
+
+#define HCI_VENDOR_CMD_OGF    0x3F
+#define HCI_PS_CMD_OCF        0x0B
+#define HCI_CHG_BAUD_CMD_OCF  0x0C
+
+#define WRITE_BDADDR_CMD_LEN 14
+#define WRITE_BAUD_CMD_LEN   6
+#define MAX_CMD_LEN          WRITE_BDADDR_CMD_LEN
+
+static int set_cntrlr_baud(int fd, int speed)
+{
+       int baud;
+       struct timespec tm = { 0, 500000 };
+       unsigned char cmd[MAX_CMD_LEN], rsp[HCI_MAX_EVENT_SIZE];
+       unsigned char *ptr = cmd + 1;
+       hci_command_hdr *ch = (void *)ptr;
+
+       cmd[0] = HCI_COMMAND_PKT;
+
+       /* set controller baud rate to user specified value */
+       ptr = cmd + 1;
+       ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+                                               HCI_CHG_BAUD_CMD_OCF));
+       ch->plen = 2;
+       ptr += HCI_COMMAND_HDR_SIZE;
+
+       baud = speed/100;
+       ptr[0] = (char)baud;
+       ptr[1] = (char)(baud >> 8);
+
+       if (write(fd, cmd, WRITE_BAUD_CMD_LEN) != WRITE_BAUD_CMD_LEN) {
+               perror("Failed to write change baud rate command");
+               return -ETIMEDOUT;
+       }
+
+       nanosleep(&tm, NULL);
+
+       if (read_hci_event(fd, rsp, sizeof(rsp)) < 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+/*
+ * Atheros AR300x specific initialization and configuration file
+ * download
+ */
+int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
+                                               struct termios *ti)
+{
+       int r;
+       int err = 0;
+       struct timespec tm = { 0, 500000 };
+       unsigned char cmd[MAX_CMD_LEN], rsp[HCI_MAX_EVENT_SIZE];
+       unsigned char *ptr = cmd + 1;
+       hci_command_hdr *ch = (void *)ptr;
+
+       cmd[0] = HCI_COMMAND_PKT;
+
+       /* set both controller and host baud rate to maximum possible value */
+       err = set_cntrlr_baud(fd, speed);
+       if (err < 0)
+               return err;
+
+       err = set_speed(fd, ti, speed);
+       if (err < 0) {
+               perror("Can't set required baud rate");
+               return err;
+       }
+
+       /* Download PS and patch */
+       r = ath_ps_download(fd);
+       if (r < 0) {
+               perror("Failed to Download configuration");
+               err = -ETIMEDOUT;
+               goto failed;
+       }
+
+       /* Write BDADDR */
+       if (bdaddr) {
+               ch->opcode = htobs(cmd_opcode_pack(HCI_VENDOR_CMD_OGF,
+                                                       HCI_PS_CMD_OCF));
+               ch->plen = 10;
+               ptr += HCI_COMMAND_HDR_SIZE;
+
+               ptr[0] = 0x01;
+               ptr[1] = 0x01;
+               ptr[2] = 0x00;
+               ptr[3] = 0x06;
+               str2ba(bdaddr, (bdaddr_t *)(ptr + 4));
+
+               if (write(fd, cmd, WRITE_BDADDR_CMD_LEN) !=
+                                       WRITE_BDADDR_CMD_LEN) {
+                       perror("Failed to write BD_ADDR command\n");
+                       err = -ETIMEDOUT;
+                       goto failed;
+               }
+
+               if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) {
+                       perror("Failed to set BD_ADDR\n");
+                       err = -ETIMEDOUT;
+                       goto failed;
+               }
+       }
+
+       /* Send HCI Reset */
+       cmd[1] = 0x03;
+       cmd[2] = 0x0C;
+       cmd[3] = 0x00;
+
+       r = write(fd, cmd, 4);
+       if (r != 4) {
+               err = -ETIMEDOUT;
+               goto failed;
+       }
+
+       nanosleep(&tm, NULL);
+       if (read_hci_event(fd, rsp, sizeof(rsp)) < 0) {
+               err = -ETIMEDOUT;
+               goto failed;
+       }
+
+       err = set_cntrlr_baud(fd, speed);
+       if (err < 0)
+               return err;
+
+failed:
+       if (err < 0) {
+               set_cntrlr_baud(fd, init_speed);
+               set_speed(fd, ti, init_speed);
+       }
+
+       return err;
+}
diff --git a/tools/hciattach_intel.c b/tools/hciattach_intel.c
new file mode 100644 (file)
index 0000000..9129993
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <time.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#ifdef INTEL_DEBUG
+#define DBGPRINT(fmt, args...) printf("DBG: " fmt "\n", ## args)
+#define PRINT_PACKET(buf, len, msg)    {       \
+       int i;                                  \
+       printf("%s\n", msg);                    \
+       for (i = 0; i < len; i++)               \
+               printf("%02X ", buf[i]);        \
+       printf("\n");                           \
+       }
+#else
+#define DBGPRINT(fmt, args...)
+#define PRINT_PACKET(buf, len, msg)
+#endif
+
+#define PATCH_SEQ_EXT           ".bseq"
+#define PATCH_FILE_PATH         "/lib/firmware/intel/"
+#define PATCH_MAX_LEN           260
+#define PATCH_TYPE_CMD          1
+#define PATCH_TYPE_EVT          2
+
+#define INTEL_VER_PARAM_LEN     9
+#define INTEL_MFG_PARAM_LEN     2
+
+/**
+ * A data structure for a patch entry.
+ */
+struct patch_entry {
+       int type;
+       int len;
+       unsigned char data[PATCH_MAX_LEN];
+};
+
+/**
+ * A structure for patch context
+ */
+struct patch_ctx {
+       int dev;
+       int fd;
+       int patch_error;
+       int reset_enable_patch;
+};
+
+/**
+ * Send HCI command to the controller
+ */
+static int intel_write_cmd(int dev, unsigned char *buf, int len)
+{
+       int ret;
+
+       PRINT_PACKET(buf, len, "<----- SEND CMD: ");
+
+       ret = write(dev, buf, len);
+       if (ret < 0)
+               return -errno;
+
+       if (ret != len)
+               return -1;
+
+       return ret;
+}
+
+/**
+ * Read the event from the controller
+ */
+static int intel_read_evt(int dev, unsigned char *buf, int len)
+{
+       int ret;
+
+       ret = read_hci_event(dev, buf, len);
+       if (ret < 0)
+               return -1;
+
+       PRINT_PACKET(buf, ret, "-----> READ EVT: ");
+
+       return ret;
+}
+
+/**
+ * Validate HCI events
+ */
+static int validate_events(struct patch_entry *event,
+               struct patch_entry *entry)
+{
+       if (event == NULL || entry == NULL) {
+               DBGPRINT("invalid patch entry parameters");
+               return -1;
+       }
+
+       if (event->len != entry->len) {
+               DBGPRINT("lengths are mismatched:[%d|%d]",
+                               event->len, entry->len);
+               return -1;
+       }
+
+       if (memcmp(event->data, entry->data, event->len)) {
+               DBGPRINT("data is mismatched");
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Read the next patch entry one line at a time
+ */
+static int get_next_patch_entry(int fd, struct patch_entry *entry)
+{
+       int len, size;
+       char rb;
+
+       if (read(fd, &rb, 1) <= 0)
+               return 0;
+
+       entry->type = rb;
+       len = 0;
+
+       switch (entry->type) {
+       case PATCH_TYPE_CMD:
+               entry->data[0] = HCI_COMMAND_PKT;
+
+               if (read(fd, &entry->data[1], 3) < 0)
+                       return -1;
+
+               size = (int)entry->data[3];
+
+               if (read(fd, &entry->data[4], size) < 0)
+                       return -1;
+
+               entry->len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + size;
+
+               break;
+
+       case PATCH_TYPE_EVT:
+               entry->data[0] = HCI_EVENT_PKT;
+
+               if (read(fd, &entry->data[len], 2) < 0)
+                       return -1;
+
+               size = (int)entry->data[2];
+
+               if (read(fd, &entry->data[3], size) < 0)
+                       return -1;
+
+               entry->len = HCI_TYPE_LEN + HCI_EVENT_HDR_SIZE + size;
+
+               break;
+
+       default:
+               fprintf(stderr, "invalid patch entry(%d)\n", entry->type);
+               return -1;
+       }
+
+       return len;
+}
+
+/**
+ * Download the patch set to the controller and verify the event
+ */
+static int intel_download_patch(struct patch_ctx *ctx)
+{
+       int ret;
+       struct patch_entry entry;
+       struct patch_entry event;
+
+       DBGPRINT("start patch downloading");
+
+       do {
+               ret = get_next_patch_entry(ctx->fd, &entry);
+               if (ret <= 0) {
+                       ctx->patch_error = 1;
+                       break;
+               }
+
+               switch (entry.type) {
+               case PATCH_TYPE_CMD:
+                       ret = intel_write_cmd(ctx->dev,
+                                       entry.data,
+                                       entry.len);
+                       if (ret <= 0) {
+                               fprintf(stderr, "failed to send cmd(%d)\n",
+                                               ret);
+                               return ret;
+                       }
+                       break;
+
+               case PATCH_TYPE_EVT:
+                       ret = intel_read_evt(ctx->dev, event.data,
+                                       sizeof(event.data));
+                       if (ret <= 0) {
+                               fprintf(stderr, "failed to read evt(%d)\n",
+                                               ret);
+                               return ret;
+                       }
+                       event.len = ret;
+
+                       if (validate_events(&event, &entry) < 0) {
+                               DBGPRINT("events are mismatched");
+                               ctx->patch_error = 1;
+                               return -1;
+                       }
+                       break;
+
+               default:
+                       fprintf(stderr, "unknown patch type(%d)\n",
+                                       entry.type);
+                       return -1;
+               }
+       } while (1);
+
+       return ret;
+}
+
+static int open_patch_file(struct patch_ctx *ctx, char *fw_ver)
+{
+       char patch_file[PATH_MAX];
+
+       snprintf(patch_file, PATH_MAX, "%s%s%s", PATCH_FILE_PATH,
+                       fw_ver, PATCH_SEQ_EXT);
+       DBGPRINT("PATCH_FILE: %s", patch_file);
+
+       ctx->fd = open(patch_file, O_RDONLY);
+       if (ctx->fd < 0) {
+               DBGPRINT("cannot open patch file. go to post patch");
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Prepare the controller for patching.
+ */
+static int pre_patch(struct patch_ctx *ctx)
+{
+       int ret, i;
+       struct patch_entry entry;
+       char fw_ver[INTEL_VER_PARAM_LEN * 2];
+
+       DBGPRINT("start pre_patch");
+
+       entry.data[0] = HCI_COMMAND_PKT;
+       entry.data[1] = 0x11;
+       entry.data[2] = 0xFC;
+       entry.data[3] = 0x02;
+       entry.data[4] = 0x01;
+       entry.data[5] = 0x00;
+       entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + INTEL_MFG_PARAM_LEN;
+
+       ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+       if (ret < 0) {
+               fprintf(stderr, "failed to send cmd(%d)\n", ret);
+               return ret;
+       }
+
+       ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+       if (ret < 0) {
+               fprintf(stderr, "failed to read evt(%d)\n", ret);
+               return ret;
+       }
+       entry.len = ret;
+
+       if (entry.data[6] != 0x00) {
+               DBGPRINT("command failed. status=%02x", entry.data[6]);
+               ctx->patch_error = 1;
+               return -1;
+       }
+
+       entry.data[0] = HCI_COMMAND_PKT;
+       entry.data[1] = 0x05;
+       entry.data[2] = 0xFC;
+       entry.data[3] = 0x00;
+       entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE;
+
+       ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+       if (ret < 0) {
+               fprintf(stderr, "failed to send cmd(%d)\n", ret);
+               return ret;
+       }
+
+       ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+       if (ret < 0) {
+               fprintf(stderr, "failed to read evt(%d)\n", ret);
+               return ret;
+       }
+       entry.len = ret;
+
+       if (entry.data[6] != 0x00) {
+               DBGPRINT("command failed. status=%02x", entry.data[6]);
+               ctx->patch_error = 1;
+               return -1;
+       }
+
+       for (i = 0; i < INTEL_VER_PARAM_LEN; i++)
+               sprintf(&fw_ver[i*2], "%02x", entry.data[7+i]);
+
+       if (open_patch_file(ctx, fw_ver) < 0) {
+               ctx->patch_error = 1;
+               return -1;
+       }
+
+       return ret;
+}
+
+/*
+ * check the event is startup event
+ */
+static int is_startup_evt(unsigned char *buf)
+{
+       if (buf[1] == 0xFF && buf[2] == 0x01 && buf[3] == 0x00)
+               return 1;
+
+       return 0;
+}
+
+/**
+ * Finalize the patch process and reset the controller
+ */
+static int post_patch(struct patch_ctx *ctx)
+{
+       int ret;
+       struct patch_entry entry;
+
+       DBGPRINT("start post_patch");
+
+       entry.data[0] = HCI_COMMAND_PKT;
+       entry.data[1] = 0x11;
+       entry.data[2] = 0xFC;
+       entry.data[3] = 0x02;
+       entry.data[4] = 0x00;
+       if (ctx->reset_enable_patch)
+               entry.data[5] = 0x02;
+       else
+               entry.data[5] = 0x01;
+
+       entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + INTEL_MFG_PARAM_LEN;
+
+       ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+       if (ret < 0) {
+               fprintf(stderr, "failed to send cmd(%d)\n", ret);
+               return ret;
+       }
+
+       ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+       if (ret < 0) {
+               fprintf(stderr, "failed to read evt(%d)\n", ret);
+               return ret;
+       }
+       entry.len = ret;
+
+       if (entry.data[6] != 0x00) {
+               fprintf(stderr, "cmd failed. st=%02x\n", entry.data[6]);
+               return -1;
+       }
+
+       do {
+               ret = intel_read_evt(ctx->dev, entry.data,
+                                       sizeof(entry.data));
+               if (ret < 0) {
+                       fprintf(stderr, "failed to read cmd(%d)\n", ret);
+                       return ret;
+               }
+               entry.len = ret;
+       } while (!is_startup_evt(entry.data));
+
+       return ret;
+}
+
+/**
+ * Main routine that handles the device patching process.
+ */
+static int intel_patch_device(struct patch_ctx *ctx)
+{
+       int ret;
+
+       ret = pre_patch(ctx);
+       if (ret < 0) {
+               if (!ctx->patch_error) {
+                       fprintf(stderr, "I/O error: pre_patch failed\n");
+                       return ret;
+               }
+
+               DBGPRINT("patch failed. proceed to post patch");
+               goto post_patch;
+       }
+
+       ret = intel_download_patch(ctx);
+       if (ret < 0) {
+               if (!ctx->patch_error) {
+                       fprintf(stderr, "I/O error: download_patch failed\n");
+                       close(ctx->fd);
+                       return ret;
+               }
+       } else {
+               DBGPRINT("patch done");
+               ctx->reset_enable_patch = 1;
+       }
+
+       close(ctx->fd);
+
+post_patch:
+       ret = post_patch(ctx);
+       if (ret < 0) {
+               fprintf(stderr, "post_patch failed(%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int set_rts(int dev, int rtsval)
+{
+       int arg;
+
+       if (ioctl(dev, TIOCMGET, &arg) < 0) {
+               perror("cannot get TIOCMGET");
+               return -errno;
+       }
+       if (rtsval)
+               arg |= TIOCM_RTS;
+       else
+               arg &= ~TIOCM_RTS;
+
+       if (ioctl(dev, TIOCMSET, &arg) == -1) {
+               perror("cannot set TIOCMGET");
+               return -errno;
+       }
+
+       return 0;
+}
+
+static unsigned char get_intel_speed(int speed)
+{
+       switch (speed) {
+       case 9600:
+               return 0x00;
+       case 19200:
+               return 0x01;
+       case 38400:
+               return 0x02;
+       case 57600:
+               return 0x03;
+       case 115200:
+               return 0x04;
+       case 230400:
+               return 0x05;
+       case 460800:
+               return 0x06;
+       case 921600:
+               return 0x07;
+       case 1843200:
+               return 0x08;
+       case 3250000:
+               return 0x09;
+       case 2000000:
+               return 0x0A;
+       case 3000000:
+               return 0x0B;
+       default:
+               return 0xFF;
+       }
+}
+
+/**
+ * if it failed to change to new baudrate, it will rollback
+ * to initial baudrate
+ */
+static int change_baudrate(int dev, int init_speed, int *speed,
+                               struct termios *ti)
+{
+       int ret;
+       unsigned char br;
+       unsigned char cmd[5];
+       unsigned char evt[7];
+
+       DBGPRINT("start baudrate change");
+
+       ret = set_rts(dev, 0);
+       if (ret < 0) {
+               fprintf(stderr, "failed to clear RTS\n");
+               return ret;
+       }
+
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x06;
+       cmd[2] = 0xFC;
+       cmd[3] = 0x01;
+
+       br = get_intel_speed(*speed);
+       if (br == 0xFF) {
+               fprintf(stderr, "speed %d is not supported\n", *speed);
+               return -1;
+       }
+       cmd[4] = br;
+
+       ret = intel_write_cmd(dev, cmd, sizeof(cmd));
+       if (ret < 0) {
+               fprintf(stderr, "failed to send cmd(%d)\n", ret);
+               return ret;
+       }
+
+       /*
+        *  wait for buffer to be consumed by the controller
+        */
+       usleep(300000);
+
+       if (set_speed(dev, ti, *speed) < 0) {
+               fprintf(stderr, "can't set to new baud rate\n");
+               return -1;
+       }
+
+       ret = set_rts(dev, 1);
+       if (ret < 0) {
+               fprintf(stderr, "failed to set RTS\n");
+               return ret;
+       }
+
+       ret = intel_read_evt(dev, evt, sizeof(evt));
+       if (ret < 0) {
+               fprintf(stderr, "failed to read evt(%d)\n", ret);
+               return ret;
+       }
+
+       if (evt[4] != 0x00) {
+               fprintf(stderr,
+                       "failed to change speed. use default speed %d\n",
+                       init_speed);
+               *speed = init_speed;
+       }
+
+       return 0;
+}
+
+/**
+ * An entry point for Intel specific initialization
+ */
+int intel_init(int dev, int init_speed, int *speed, struct termios *ti)
+{
+       int ret = 0;
+       struct patch_ctx ctx;
+
+       if (change_baudrate(dev, init_speed, speed, ti) < 0)
+               return -1;
+
+       ctx.dev = dev;
+       ctx.patch_error = 0;
+       ctx.reset_enable_patch = 0;
+
+       ret = intel_patch_device(&ctx);
+       if (ret < 0)
+               fprintf(stderr, "failed to initialize the device");
+
+       return ret;
+}
diff --git a/tools/hciattach_qualcomm.c b/tools/hciattach_qualcomm.c
new file mode 100644 (file)
index 0000000..0e02e1e
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#define FAILIF(x, args...) do { \
+       if (x) { \
+               fprintf(stderr, ##args); \
+               return -1; \
+       } \
+} while (0)
+
+typedef struct {
+       uint8_t uart_prefix;
+       hci_event_hdr hci_hdr;
+       evt_cmd_complete cmd_complete;
+       uint8_t status;
+       uint8_t data[16];
+} __attribute__((packed)) command_complete_t;
+
+static int read_command_complete(int fd,
+                                       unsigned short opcode,
+                                       unsigned char len)
+{
+       command_complete_t resp;
+       unsigned char vsevent[512];
+       int n;
+
+       /* Read reply. */
+       n = read_hci_event(fd, vsevent, sizeof(vsevent));
+       FAILIF(n < 0, "Failed to read response");
+
+       FAILIF(vsevent[1] != 0xFF, "Failed to read response");
+
+       n = read_hci_event(fd, (unsigned char *)&resp, sizeof(resp));
+       FAILIF(n < 0, "Failed to read response");
+
+       /* event must be event-complete */
+       FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE,
+               "Error in response: not a cmd-complete event, "
+               "but 0x%02x!\n", resp.hci_hdr.evt);
+
+       FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */
+               "Error in response: plen is not >= 4, but 0x%02x!\n",
+               resp.hci_hdr.plen);
+
+       /* cmd-complete event: opcode */
+       FAILIF(resp.cmd_complete.opcode != 0,
+               "Error in response: opcode is 0x%04x, not 0!",
+               resp.cmd_complete.opcode);
+
+       return resp.status == 0 ? 0 : -1;
+}
+
+static int qualcomm_load_firmware(int fd, const char *firmware, const char *bdaddr_s)
+{
+
+       int fw = open(firmware, O_RDONLY);
+
+       fprintf(stdout, "Opening firmware file: %s\n", firmware);
+
+       FAILIF(fw < 0,
+               "Could not open firmware file %s: %s (%d).\n",
+               firmware, strerror(errno), errno);
+
+       fprintf(stdout, "Uploading firmware...\n");
+       do {
+               /* Read each command and wait for a response. */
+               unsigned char data[1024];
+               unsigned char cmdp[1 + sizeof(hci_command_hdr)];
+               hci_command_hdr *cmd = (hci_command_hdr *) (cmdp + 1);
+               int nr;
+
+               nr = read(fw, cmdp, sizeof(cmdp));
+               if (!nr)
+                       break;
+
+               FAILIF(nr != sizeof(cmdp),
+                       "Could not read H4 + HCI header!\n");
+               FAILIF(*cmdp != HCI_COMMAND_PKT,
+                       "Command is not an H4 command packet!\n");
+
+               FAILIF(read(fw, data, cmd->plen) != cmd->plen,
+                               "Could not read %d bytes of data \
+                               for command with opcode %04x!\n",
+                               cmd->plen, cmd->opcode);
+
+               if ((data[0] == 1) && (data[1] == 2) && (data[2] == 6)) {
+                       bdaddr_t bdaddr;
+                       if (bdaddr_s != NULL) {
+                               str2ba(bdaddr_s, &bdaddr);
+                               memcpy(&data[3], &bdaddr, sizeof(bdaddr_t));
+                       }
+               }
+
+               {
+                       int nw;
+                       struct iovec iov_cmd[2];
+                       iov_cmd[0].iov_base = cmdp;
+                       iov_cmd[0].iov_len = sizeof(cmdp);
+                       iov_cmd[1].iov_base = data;
+                       iov_cmd[1].iov_len = cmd->plen;
+                       nw = writev(fd, iov_cmd, 2);
+                       FAILIF(nw != (int) sizeof(cmdp) + cmd->plen,
+                               "Could not send entire command \
+                               (sent only %d bytes)!\n",
+                               nw);
+               }
+
+               /* Wait for response */
+               if (read_command_complete(fd, cmd->opcode, cmd->plen) < 0)
+                       return -1;
+       } while (1);
+       fprintf(stdout, "Firmware upload successful.\n");
+
+       close(fw);
+
+       return 0;
+}
+
+int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr)
+{
+       struct timespec tm = {0, 50000};
+       char cmd[5];
+       unsigned char resp[100];                /* Response */
+       char fw[100];
+       int n;
+
+       memset(resp, 0, 100);
+
+       /* Get Manufacturer and LMP version */
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x01;
+       cmd[2] = 0x10;
+       cmd[3] = 0x00;
+
+       do {
+               n = write(fd, cmd, 4);
+               if (n < 4) {
+                       perror("Failed to write init command");
+                       return -1;
+               }
+
+               /* Read reply. */
+               if (read_hci_event(fd, resp, 100) < 0) {
+                       perror("Failed to read init response");
+                       return -1;
+               }
+
+               /* Wait for command complete event for our Opcode */
+       } while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+       /* Verify manufacturer */
+       if ((resp[11] & 0xFF) != 0x1d)
+               fprintf(stderr,
+                       "WARNING : module's manufacturer is not Qualcomm\n");
+
+       /* Print LMP version */
+       fprintf(stderr,
+               "Qualcomm module LMP version : 0x%02x\n", resp[10] & 0xFF);
+
+       /* Print LMP subversion */
+       {
+               unsigned short lmp_subv = resp[13] | (resp[14] << 8);
+
+               fprintf(stderr, "Qualcomm module LMP sub-version : 0x%04x\n",
+                                                               lmp_subv);
+       }
+
+       /* Get SoC type */
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x00;
+       cmd[2] = 0xFC;
+       cmd[3] = 0x01;
+       cmd[4] = 0x06;
+
+       do {
+               n = write(fd, cmd, 5);
+               if (n < 5) {
+                       perror("Failed to write vendor init command");
+                       return -1;
+               }
+
+               /* Read reply. */
+               if ((n = read_hci_event(fd, resp, 100)) < 0) {
+                       perror("Failed to read vendor init response");
+                       return -1;
+               }
+
+       } while (resp[3] != 0 && resp[4] != 2);
+
+       snprintf(fw, sizeof(fw), "/etc/firmware/%c%c%c%c%c%c_%c%c%c%c.bin",
+                               resp[18], resp[19], resp[20], resp[21],
+                               resp[22], resp[23],
+                               resp[32], resp[33], resp[34], resp[35]);
+
+       /* Wait for command complete event for our Opcode */
+       if (read_hci_event(fd, resp, 100) < 0) {
+               perror("Failed to read init response");
+               return -1;
+       }
+
+       qualcomm_load_firmware(fd, fw, bdaddr);
+
+       /* Reset */
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x03;
+       cmd[2] = 0x0C;
+       cmd[3] = 0x00;
+
+       do {
+               n = write(fd, cmd, 4);
+               if (n < 4) {
+                       perror("Failed to write reset command");
+                       return -1;
+               }
+
+               /* Read reply. */
+               if ((n = read_hci_event(fd, resp, 100)) < 0) {
+                       perror("Failed to read reset response");
+                       return -1;
+               }
+
+       } while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+       nanosleep(&tm, NULL);
+
+       return 0;
+}
diff --git a/tools/hciattach_st.c b/tools/hciattach_st.c
new file mode 100644 (file)
index 0000000..dbb7c47
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/param.h>
+
+#include <bluetooth/bluetooth.h>
+
+#include "hciattach.h"
+
+static int debug = 0;
+
+static int do_command(int fd, uint8_t ogf, uint16_t ocf,
+                       uint8_t *cparam, int clen, uint8_t *rparam, int rlen)
+{
+       //uint16_t opcode = (uint16_t) ((ocf & 0x03ff) | (ogf << 10));
+       unsigned char cp[260], rp[260];
+       int len, size, offset = 3;
+
+       cp[0] = 0x01;
+       cp[1] = ocf & 0xff;
+       cp[2] = ogf << 2 | ocf >> 8;
+       cp[3] = clen;
+
+       if (clen > 0)
+               memcpy(cp + 4, cparam, clen);
+
+       if (debug) {
+               int i;
+               printf("[<");
+               for (i = 0; i < clen + 4; i++)
+                       printf(" %02x", cp[i]);
+               printf("]\n");
+       }
+
+       if (write(fd, cp, clen + 4) < 0)
+               return -1;
+
+       do {
+               if (read(fd, rp, 1) < 1)
+                       return -1;
+       } while (rp[0] != 0x04);
+
+       if (read(fd, rp + 1, 2) < 2)
+               return -1;
+
+       do {
+               len = read(fd, rp + offset, sizeof(rp) - offset);
+               offset += len;
+       } while (offset < rp[2] + 3);
+
+       if (debug) {
+               int i;
+               printf("[>");
+               for (i = 0; i < offset; i++)
+                       printf(" %02x", rp[i]);
+               printf("]\n");
+       }
+
+       if (rp[0] != 0x04) {
+               errno = EIO;
+               return -1;
+       }
+
+       switch (rp[1]) {
+       case 0x0e:      /* command complete */
+               if (rp[6] != 0x00)
+                       return -ENXIO;
+               offset = 3 + 4;
+               size = rp[2] - 4;
+               break;
+       case 0x0f:      /* command status */
+               /* fall through */
+       default:
+               offset = 3;
+               size = rp[2];
+               break;
+       }
+
+       if (!rparam || rlen < size)
+               return -ENXIO;
+
+       memcpy(rparam, rp + offset, size);
+
+       return size;
+}
+
+static int load_file(int dd, uint16_t version, const char *suffix)
+{
+       DIR *dir;
+       struct dirent *d;
+       char pathname[PATH_MAX], filename[NAME_MAX], prefix[20];
+       unsigned char cmd[256];
+       unsigned char buf[256];
+       uint8_t seqnum = 0;
+       int fd, size, len, found_fw_file;
+
+       memset(filename, 0, sizeof(filename));
+
+       snprintf(prefix, sizeof(prefix), "STLC2500_R%d_%02d_",
+                                               version >> 8, version & 0xff);
+
+       strcpy(pathname, "/lib/firmware");
+       dir = opendir(pathname);
+       if (!dir) {
+               strcpy(pathname, ".");
+               dir = opendir(pathname);
+               if (!dir)
+                       return -errno;
+       }
+
+       found_fw_file = 0;
+       while (1) {
+               d = readdir(dir);
+               if (!d)
+                       break;
+
+               if (strncmp(d->d_name + strlen(d->d_name) - strlen(suffix),
+                                               suffix, strlen(suffix)))
+                       continue;
+
+               if (strncmp(d->d_name, prefix, strlen(prefix)))
+                       continue;
+
+               snprintf(filename, sizeof(filename), "%s/%s",
+                                                       pathname, d->d_name);
+               found_fw_file = 1;
+       }
+
+       closedir(dir);
+
+       if (!found_fw_file)
+               return -ENOENT;
+
+       printf("Loading file %s\n", filename);
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               perror("Can't open firmware file");
+               return -errno;
+       }
+
+       while (1) {
+               size = read(fd, cmd + 1, 254);
+               if (size <= 0)
+                       break;
+
+               cmd[0] = seqnum;
+
+               len = do_command(dd, 0xff, 0x002e, cmd, size + 1, buf, sizeof(buf));
+               if (len < 1)
+                       break;
+
+               if (buf[0] != seqnum) {
+                       fprintf(stderr, "Sequence number mismatch\n");
+                       break;
+               }
+
+               seqnum++;
+       }
+
+       close(fd);
+
+       return 0;
+}
+
+int stlc2500_init(int dd, bdaddr_t *bdaddr)
+{
+       unsigned char cmd[16];
+       unsigned char buf[254];
+       uint16_t version;
+       int len;
+       int err;
+
+       /* Hci_Cmd_Ericsson_Read_Revision_Information */
+       len = do_command(dd, 0xff, 0x000f, NULL, 0, buf, sizeof(buf));
+       if (len < 0)
+               return -1;
+
+       printf("%s\n", buf);
+
+       /* HCI_Read_Local_Version_Information */
+       len = do_command(dd, 0x04, 0x0001, NULL, 0, buf, sizeof(buf));
+       if (len < 0)
+               return -1;
+
+       version = buf[2] << 8 | buf[1];
+
+       err = load_file(dd, version, ".ptc");
+       if (err < 0) {
+               if (err == -ENOENT)
+                       fprintf(stderr, "No ROM patch file loaded.\n");
+               else
+                       return -1;
+       }
+
+       err = load_file(dd, buf[2] << 8 | buf[1], ".ssf");
+       if (err < 0) {
+               if (err == -ENOENT)
+                       fprintf(stderr, "No static settings file loaded.\n");
+               else
+                       return -1;
+       }
+
+       cmd[0] = 0xfe;
+       cmd[1] = 0x06;
+       bacpy((bdaddr_t *) (cmd + 2), bdaddr);
+
+       /* Hci_Cmd_ST_Store_In_NVDS */
+       len = do_command(dd, 0xff, 0x0022, cmd, 8, buf, sizeof(buf));
+       if (len < 0)
+               return -1;
+
+       /* HCI_Reset : applies parameters*/
+       len = do_command(dd, 0x03, 0x0003, NULL, 0, buf, sizeof(buf));
+       if (len < 0)
+               return -1;
+
+       return 0;
+}
+
+int bgb2xx_init(int dd, bdaddr_t *bdaddr)
+{
+       unsigned char cmd[16];
+       unsigned char buf[254];
+       int len;
+
+       len = do_command(dd, 0xff, 0x000f, NULL, 0, buf, sizeof(buf));
+       if (len < 0)
+               return -1;
+
+       printf("%s\n", buf);
+
+       cmd[0] = 0xfe;
+       cmd[1] = 0x06;
+       bacpy((bdaddr_t *) (cmd + 2), bdaddr);
+
+       len = do_command(dd, 0xff, 0x0022, cmd, 8, buf, sizeof(buf));
+       if (len < 0)
+               return -1;
+
+       len = do_command(dd, 0x03, 0x0003, NULL, 0, buf, sizeof(buf));
+       if (len < 0)
+               return -1;
+
+       return 0;
+}
diff --git a/tools/hciattach_ti.c b/tools/hciattach_ti.c
new file mode 100644 (file)
index 0000000..158e568
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2007-2008  Texas Instruments, Inc.
+ *  Copyright (C) 2005-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#ifdef HCIATTACH_DEBUG
+#define DPRINTF(x...)  printf(x)
+#else
+#define DPRINTF(x...)
+#endif
+
+#define HCIUARTGETDEVICE       _IOR('U', 202, int)
+
+#define MAKEWORD(a, b)  ((uint16_t)(((uint8_t)(a)) | ((uint16_t)((uint8_t)(b))) << 8))
+
+#define TI_MANUFACTURER_ID     13
+
+#define FIRMWARE_DIRECTORY     "/lib/firmware/"
+
+#define ACTION_SEND_COMMAND    1
+#define ACTION_WAIT_EVENT      2
+#define ACTION_SERIAL          3
+#define ACTION_DELAY           4
+#define ACTION_RUN_SCRIPT      5
+#define ACTION_REMARKS         6
+
+#define BRF_DEEP_SLEEP_OPCODE_BYTE_1   0x0c
+#define BRF_DEEP_SLEEP_OPCODE_BYTE_2   0xfd
+#define BRF_DEEP_SLEEP_OPCODE          \
+       (BRF_DEEP_SLEEP_OPCODE_BYTE_1 | (BRF_DEEP_SLEEP_OPCODE_BYTE_2 << 8))
+
+#define FILE_HEADER_MAGIC      0x42535442
+
+/*
+ * BRF Firmware header
+ */
+struct bts_header {
+       uint32_t        magic;
+       uint32_t        version;
+       uint8_t future[24];
+       uint8_t actions[0];
+}__attribute__ ((packed));
+
+/*
+ * BRF Actions structure
+ */
+struct bts_action {
+       uint16_t        type;
+       uint16_t        size;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+struct bts_action_send {
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+struct bts_action_wait {
+       uint32_t msec;
+       uint32_t size;
+       uint8_t data[0];
+}__attribute__ ((packed));
+
+struct bts_action_delay {
+       uint32_t msec;
+}__attribute__ ((packed));
+
+struct bts_action_serial {
+       uint32_t baud;
+       uint32_t flow_control;
+}__attribute__ ((packed));
+
+static FILE *bts_load_script(const char* file_name, uint32_t* version)
+{
+       struct bts_header header;
+       FILE* fp;
+
+       fp = fopen(file_name, "rb");
+       if (!fp) {
+               perror("can't open firmware file");
+               return NULL;
+       }
+
+       if (1 != fread(&header, sizeof(struct bts_header), 1, fp)) {
+               perror("can't read firmware file");
+               goto errclose;
+       }
+
+       if (header.magic != FILE_HEADER_MAGIC) {
+               fprintf(stderr, "%s not a legal TI firmware file\n", file_name);
+               goto errclose;
+       }
+
+       if (NULL != version)
+               *version = header.version;
+
+       return fp;
+
+errclose:
+       fclose(fp);
+
+       return NULL;
+}
+
+static unsigned long bts_fetch_action(FILE* fp, unsigned char* action_buf,
+                               unsigned long buf_size, uint16_t* action_type)
+{
+       struct bts_action action_hdr;
+       unsigned long nread;
+
+       if (!fp)
+               return 0;
+
+       if (1 != fread(&action_hdr, sizeof(struct bts_action), 1, fp))
+               return 0;
+
+       if (action_hdr.size > buf_size) {
+               fprintf(stderr, "bts_next_action: not enough space to read next action\n");
+               return 0;
+       }
+
+       nread = fread(action_buf, sizeof(uint8_t), action_hdr.size, fp);
+       if (nread != (action_hdr.size)) {
+               fprintf(stderr, "bts_next_action: fread failed to read next action\n");
+               return 0;
+       }
+
+       *action_type = action_hdr.type;
+
+       return nread * sizeof(uint8_t);
+}
+
+static void bts_unload_script(FILE* fp)
+{
+       if (fp)
+               fclose(fp);
+}
+
+static int is_it_texas(const uint8_t *respond)
+{
+       uint16_t manufacturer_id;
+
+       manufacturer_id = MAKEWORD(respond[11], respond[12]);
+
+       return TI_MANUFACTURER_ID == manufacturer_id ? 1 : 0;
+}
+
+static const char *get_firmware_name(const uint8_t *respond)
+{
+       static char firmware_file_name[PATH_MAX] = {0};
+       uint16_t version = 0, chip = 0, min_ver = 0, maj_ver = 0;
+
+       version = MAKEWORD(respond[13], respond[14]);
+       chip =  (version & 0x7C00) >> 10;
+       min_ver = (version & 0x007F);
+       maj_ver = (version & 0x0380) >> 7;
+
+       if (version & 0x8000)
+               maj_ver |= 0x0008;
+
+       sprintf(firmware_file_name, FIRMWARE_DIRECTORY "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
+
+       return firmware_file_name;
+}
+
+static void brf_delay(struct bts_action_delay *delay)
+{
+       usleep(1000 * delay->msec);
+}
+
+static int brf_set_serial_params(struct bts_action_serial *serial_action,
+                                               int fd, int *speed, struct termios *ti)
+{
+       fprintf(stderr, "texas: changing baud rate to %u, flow control to %u\n",
+                               serial_action->baud, serial_action->flow_control );
+       tcflush(fd, TCIOFLUSH);
+
+       if (serial_action->flow_control)
+               ti->c_cflag |= CRTSCTS;
+       else
+               ti->c_cflag &= ~CRTSCTS;
+
+       if (tcsetattr(fd, TCSANOW, ti) < 0) {
+               perror("Can't set port settings");
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (set_speed(fd, ti, serial_action->baud) < 0) {
+               perror("Can't set baud rate");
+               return -1;
+       }
+
+       if (speed)
+               *speed = serial_action->baud;
+
+       return 0;
+}
+
+static int brf_send_command_socket(int fd, struct bts_action_send* send_action)
+{
+       char response[1024] = {0};
+       hci_command_hdr *cmd = (hci_command_hdr *) send_action->data;
+       uint16_t opcode = cmd->opcode;
+
+       struct hci_request rq;
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = cmd_opcode_ogf(opcode);
+       rq.ocf    = cmd_opcode_ocf(opcode);
+       rq.event  = EVT_CMD_COMPLETE;
+       rq.cparam = &send_action->data[3];
+       rq.clen   = send_action->data[2];
+       rq.rparam = response;
+       rq.rlen   = sizeof(response);
+
+       if (hci_send_req(fd, &rq, 15) < 0) {
+               perror("Cannot send hci command to socket");
+               return -1;
+       }
+
+       /* verify success */
+       if (response[0]) {
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+static int brf_send_command_file(int fd, struct bts_action_send* send_action, long size)
+{
+       unsigned char response[1024] = {0};
+       long ret = 0;
+
+       /* send command */
+       if (size != write(fd, send_action, size)) {
+               perror("Texas: Failed to write action command");
+               return -1;
+       }
+
+       /* read response */
+       ret = read_hci_event(fd, response, sizeof(response));
+       if (ret < 0) {
+               perror("texas: failed to read command response");
+               return -1;
+       }
+
+       /* verify success */
+       if (ret < 7 || 0 != response[6]) {
+               fprintf( stderr, "TI init command failed.\n" );
+               errno = EIO;
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int brf_send_command(int fd, struct bts_action_send* send_action, long size, int hcill_installed)
+{
+       int ret = 0;
+       char *fixed_action;
+
+       /* remove packet type when giving to socket API */
+       if (hcill_installed) {
+               fixed_action = ((char *) send_action) + 1;
+               ret = brf_send_command_socket(fd, (struct bts_action_send *) fixed_action);
+       } else {
+               ret = brf_send_command_file(fd, send_action, size);
+       }
+
+       return ret;
+}
+
+static int brf_do_action(uint16_t brf_type, uint8_t *brf_action, long brf_size,
+                               int fd, int *speed, struct termios *ti, int hcill_installed)
+{
+       int ret = 0;
+
+       switch (brf_type) {
+       case ACTION_SEND_COMMAND:
+               DPRINTF("W");
+               ret = brf_send_command(fd, (struct bts_action_send*) brf_action, brf_size, hcill_installed);
+               break;
+       case ACTION_WAIT_EVENT:
+               DPRINTF("R");
+               break;
+       case ACTION_SERIAL:
+               DPRINTF("S");
+               ret = brf_set_serial_params((struct bts_action_serial *) brf_action, fd, speed, ti);
+               break;
+       case ACTION_DELAY:
+               DPRINTF("D");
+               brf_delay((struct bts_action_delay *) brf_action);
+               break;
+       case ACTION_REMARKS:
+               DPRINTF("C");
+               break;
+       default:
+               fprintf(stderr, "brf_init: unknown firmware action type (%d)\n", brf_type);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * tests whether a given brf action is a HCI_VS_Sleep_Mode_Configurations cmd
+ */
+static int brf_action_is_deep_sleep(uint8_t *brf_action, long brf_size,
+                                                       uint16_t brf_type)
+{
+       uint16_t opcode;
+
+       if (brf_type != ACTION_SEND_COMMAND)
+               return 0;
+
+       if (brf_size < 3)
+               return 0;
+
+       if (brf_action[0] != HCI_COMMAND_PKT)
+               return 0;
+
+       /* HCI data is little endian */
+       opcode = brf_action[1] | (brf_action[2] << 8);
+
+       if (opcode != BRF_DEEP_SLEEP_OPCODE)
+               return 0;
+
+       /* action is deep sleep configuration command ! */
+       return 1;
+}
+
+/*
+ * This function is called twice.
+ * The first time it is called, it loads the brf script, and executes its
+ * commands until it reaches a deep sleep command (or its end).
+ * The second time it is called, it assumes HCILL protocol is set up,
+ * and sends rest of brf script via the supplied socket.
+ */
+static int brf_do_script(int fd, int *speed, struct termios *ti, const char *bts_file)
+{
+       int ret = 0,  hcill_installed = bts_file ? 0 : 1;
+       uint32_t vers;
+       static FILE *brf_script_file = NULL;
+       static uint8_t brf_action[512];
+       static long brf_size;
+       static uint16_t brf_type;
+
+       /* is it the first time we are called ? */
+       if (0 == hcill_installed) {
+               DPRINTF("Sending script to serial device\n");
+               brf_script_file = bts_load_script(bts_file, &vers );
+               if (!brf_script_file) {
+                       fprintf(stderr, "Warning: cannot find BTS file: %s\n",
+                                       bts_file);
+                       return 0;
+               }
+
+               fprintf( stderr, "Loaded BTS script version %u\n", vers );
+
+               brf_size = bts_fetch_action(brf_script_file, brf_action,
+                                               sizeof(brf_action), &brf_type);
+               if (brf_size == 0) {
+                       fprintf(stderr, "Warning: BTS file is empty !");
+                       return 0;
+               }
+       }
+       else {
+               DPRINTF("Sending script to bluetooth socket\n");
+       }
+
+       /* execute current action and continue to parse brf script file */
+       while (brf_size != 0) {
+               ret = brf_do_action(brf_type, brf_action, brf_size,
+                                               fd, speed, ti, hcill_installed);
+               if (ret == -1)
+                       break;
+
+               brf_size = bts_fetch_action(brf_script_file, brf_action,
+                                               sizeof(brf_action), &brf_type);
+
+               /* if this is the first time we run (no HCILL yet) */
+               /* and a deep sleep command is encountered */
+               /* we exit */
+               if (!hcill_installed &&
+                               brf_action_is_deep_sleep(brf_action,
+                                                       brf_size, brf_type))
+                       return 0;
+       }
+
+       bts_unload_script(brf_script_file);
+       brf_script_file = NULL;
+       DPRINTF("\n");
+
+       return ret;
+}
+
+int texas_init(int fd, int *speed, struct termios *ti)
+{
+       struct timespec tm = {0, 50000};
+       char cmd[4];
+       unsigned char resp[100];                /* Response */
+       const char *bts_file;
+       int n;
+
+       memset(resp,'\0', 100);
+
+       /* It is possible to get software version with manufacturer specific
+          HCI command HCI_VS_TI_Version_Number. But the only thing you get more
+          is if this is point-to-point or point-to-multipoint module */
+
+       /* Get Manufacturer and LMP version */
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x01;
+       cmd[2] = 0x10;
+       cmd[3] = 0x00;
+
+       do {
+               n = write(fd, cmd, 4);
+               if (n < 0) {
+                       perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)");
+                       return -1;
+               }
+               if (n < 4) {
+                       fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n);
+                       return -1;
+               }
+
+               /* Read reply. */
+               if (read_hci_event(fd, resp, 100) < 0) {
+                       perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)");
+                       return -1;
+               }
+
+               /* Wait for command complete event for our Opcode */
+       } while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+       /* Verify manufacturer */
+       if (! is_it_texas(resp)) {
+               fprintf(stderr,"ERROR: module's manufacturer is not Texas Instruments\n");
+               return -1;
+       }
+
+       fprintf(stderr, "Found a Texas Instruments' chip!\n");
+
+       bts_file = get_firmware_name(resp);
+       fprintf(stderr, "Firmware file : %s\n", bts_file);
+
+       n = brf_do_script(fd, speed, ti, bts_file);
+
+       nanosleep(&tm, NULL);
+
+       return n;
+}
+
+int texas_post(int fd, struct termios *ti)
+{
+       int dev_id, dd, ret = 0;
+
+       sleep(1);
+
+       dev_id = ioctl(fd, HCIUARTGETDEVICE, 0);
+       if (dev_id < 0) {
+               perror("cannot get device id");
+               return -1;
+       }
+
+       DPRINTF("\nAdded device hci%d\n", dev_id);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               return -1;
+       }
+
+       if (ioctl(dd, HCIDEVUP, dev_id) < 0 && errno != EALREADY) {
+               fprintf(stderr, "Can't init device hci%d: %s (%d)", dev_id,
+                                                       strerror(errno), errno);
+               hci_close_dev(dd);
+               return -1;
+       }
+
+       ret = brf_do_script(dd, NULL, ti, NULL);
+
+       hci_close_dev(dd);
+
+       return ret;
+}
diff --git a/tools/hciattach_tialt.c b/tools/hciattach_tialt.c
new file mode 100644 (file)
index 0000000..c3caa49
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2005-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#define FAILIF(x, args...) do {   \
+       if (x) {                                          \
+               fprintf(stderr, ##args);  \
+               return -1;                                \
+       }                                                         \
+} while(0)
+
+typedef struct {
+       uint8_t uart_prefix;
+       hci_event_hdr hci_hdr;
+       evt_cmd_complete cmd_complete;
+       uint8_t status;
+       uint8_t data[16];
+} __attribute__((packed)) command_complete_t;
+
+static int read_command_complete(int fd, unsigned short opcode, unsigned char len) {
+       command_complete_t resp;
+       /* Read reply. */
+       FAILIF(read_hci_event(fd, (unsigned char *)&resp, sizeof(resp)) < 0,
+                  "Failed to read response");
+
+       /* Parse speed-change reply */
+       FAILIF(resp.uart_prefix != HCI_EVENT_PKT,
+                  "Error in response: not an event packet, but 0x%02x!\n",
+                  resp.uart_prefix);
+
+       FAILIF(resp.hci_hdr.evt != EVT_CMD_COMPLETE, /* event must be event-complete */
+                  "Error in response: not a cmd-complete event, "
+                  "but 0x%02x!\n", resp.hci_hdr.evt);
+
+       FAILIF(resp.hci_hdr.plen < 4, /* plen >= 4 for EVT_CMD_COMPLETE */
+                  "Error in response: plen is not >= 4, but 0x%02x!\n",
+                  resp.hci_hdr.plen);
+
+       /* cmd-complete event: opcode */
+       FAILIF(resp.cmd_complete.opcode != (uint16_t)opcode,
+                  "Error in response: opcode is 0x%04x, not 0x%04x!",
+                  resp.cmd_complete.opcode, opcode);
+
+       return resp.status == 0 ? 0 : -1;
+}
+
+typedef struct {
+       uint8_t uart_prefix;
+       hci_command_hdr hci_hdr;
+       uint32_t speed;
+} __attribute__((packed)) texas_speed_change_cmd_t;
+
+static int texas_change_speed(int fd, uint32_t speed)
+{
+       return 0;
+}
+
+static int texas_load_firmware(int fd, const char *firmware) {
+
+       int fw = open(firmware, O_RDONLY);
+
+       fprintf(stdout, "Opening firmware file: %s\n", firmware);
+
+       FAILIF(fw < 0,
+                  "Could not open firmware file %s: %s (%d).\n",
+                  firmware, strerror(errno), errno);
+
+       fprintf(stdout, "Uploading firmware...\n");
+       do {
+               /* Read each command and wait for a response. */
+               unsigned char data[1024];
+               unsigned char cmdp[1 + sizeof(hci_command_hdr)];
+               hci_command_hdr *cmd = (hci_command_hdr *)(cmdp + 1);
+               int nr;
+               nr = read(fw, cmdp, sizeof(cmdp));
+               if (!nr)
+                       break;
+               FAILIF(nr != sizeof(cmdp), "Could not read H4 + HCI header!\n");
+               FAILIF(*cmdp != HCI_COMMAND_PKT, "Command is not an H4 command packet!\n");
+
+               FAILIF(read(fw, data, cmd->plen) != cmd->plen,
+                          "Could not read %d bytes of data for command with opcode %04x!\n",
+                          cmd->plen,
+                          cmd->opcode);
+
+               {
+                       int nw;
+#if 0
+                       fprintf(stdout, "\topcode 0x%04x (%d bytes of data).\n",
+                                       cmd->opcode,
+                                       cmd->plen);
+#endif
+                       struct iovec iov_cmd[2];
+                       iov_cmd[0].iov_base = cmdp;
+                       iov_cmd[0].iov_len      = sizeof(cmdp);
+                       iov_cmd[1].iov_base = data;
+                       iov_cmd[1].iov_len      = cmd->plen;
+                       nw = writev(fd, iov_cmd, 2);
+                       FAILIF(nw != (int) sizeof(cmd) +        cmd->plen,
+                                  "Could not send entire command (sent only %d bytes)!\n",
+                                  nw);
+               }
+
+               /* Wait for response */
+               if (read_command_complete(fd,
+                                                                 cmd->opcode,
+                                                                 cmd->plen) < 0) {
+                       return -1;
+               }
+
+       } while(1);
+       fprintf(stdout, "Firmware upload successful.\n");
+
+       close(fw);
+       return 0;
+}
+
+int texasalt_init(int fd, int speed, struct termios *ti)
+{
+       struct timespec tm = {0, 50000};
+       char cmd[4];
+       unsigned char resp[100];                /* Response */
+       int n;
+
+       memset(resp,'\0', 100);
+
+       /* It is possible to get software version with manufacturer specific
+          HCI command HCI_VS_TI_Version_Number. But the only thing you get more
+          is if this is point-to-point or point-to-multipoint module */
+
+       /* Get Manufacturer and LMP version */
+       cmd[0] = HCI_COMMAND_PKT;
+       cmd[1] = 0x01;
+       cmd[2] = 0x10;
+       cmd[3] = 0x00;
+
+       do {
+               n = write(fd, cmd, 4);
+               if (n < 0) {
+                       perror("Failed to write init command (READ_LOCAL_VERSION_INFORMATION)");
+                       return -1;
+               }
+               if (n < 4) {
+                       fprintf(stderr, "Wanted to write 4 bytes, could only write %d. Stop\n", n);
+                       return -1;
+               }
+
+               /* Read reply. */
+               if (read_hci_event(fd, resp, 100) < 0) {
+                       perror("Failed to read init response (READ_LOCAL_VERSION_INFORMATION)");
+                       return -1;
+               }
+
+               /* Wait for command complete event for our Opcode */
+       } while (resp[4] != cmd[1] && resp[5] != cmd[2]);
+
+       /* Verify manufacturer */
+       if ((resp[11] & 0xFF) != 0x0d)
+               fprintf(stderr,"WARNING : module's manufacturer is not Texas Instrument\n");
+
+       /* Print LMP version */
+       fprintf(stderr, "Texas module LMP version : 0x%02x\n", resp[10] & 0xFF);
+
+       /* Print LMP subversion */
+       {
+               unsigned short lmp_subv = resp[13] | (resp[14] << 8);
+               unsigned short brf_chip = (lmp_subv & 0x7c00) >> 10;
+               static const char *c_brf_chip[8] = {
+                       "unknown",
+                       "unknown",
+                       "brf6100",
+                       "brf6150",
+                       "brf6300",
+                       "brf6350",
+                       "unknown",
+                       "wl1271"
+               };
+               char fw[100];
+
+               fprintf(stderr, "Texas module LMP sub-version : 0x%04x\n", lmp_subv);
+
+               fprintf(stderr,
+                               "\tinternal version freeze: %d\n"
+                               "\tsoftware version: %d\n"
+                               "\tchip: %s (%d)\n",
+                               lmp_subv & 0x7f,
+                               ((lmp_subv & 0x8000) >> (15-3)) | ((lmp_subv & 0x380) >> 7),
+                               ((brf_chip > 7) ? "unknown" : c_brf_chip[brf_chip]),
+                               brf_chip);
+
+               sprintf(fw, "/etc/firmware/%s.bin", c_brf_chip[brf_chip]);
+               texas_load_firmware(fd, fw);
+
+               texas_change_speed(fd, speed);
+       }
+       nanosleep(&tm, NULL);
+       return 0;
+}
diff --git a/tools/hciconfig.8 b/tools/hciconfig.8
new file mode 100644 (file)
index 0000000..35956c4
--- /dev/null
@@ -0,0 +1,277 @@
+.TH HCICONFIG 8 "Nov 11 2002" BlueZ "Linux System Administration"
+.SH NAME
+hciconfig \- configure Bluetooth devices
+.SH SYNOPSIS
+.B hciconfig
+.B \-h
+.br
+.B hciconfig
+.RB [\| \-a \|]
+.br
+.B hciconfig
+.RB [\| \-a \|]
+.B hciX
+.RI [\| command
+.RI [\| "command parameters" \|]\|]
+
+.SH DESCRIPTION
+.LP
+.B hciconfig
+is used to configure Bluetooth devices.
+.I hciX
+is the name of a Bluetooth device installed in the system. If
+.I hciX
+is not given,
+.B hciconfig
+prints name and basic information about all the Bluetooth devices installed in
+the system. If
+.I hciX
+is given but no command is given, it prints basic information on device
+.I hciX
+only. Basic information is
+interface type, BD address, ACL MTU, SCO MTU, flags (up, init, running, raw,
+page scan enabled, inquiry scan enabled, inquiry, authentication enabled,
+encryption enabled).
+.SH OPTIONS
+.TP
+.B \-h, \-\-help
+Gives a list of possible commands.
+.TP
+.B \-a, \-\-all
+Other than the basic info, print features, packet type, link policy, link mode,
+name, class, version.
+.SH COMMANDS
+.TP
+.B up
+Open and initialize HCI device.
+.TP
+.B down
+Close HCI device.
+.TP
+.B reset
+Reset HCI device.
+.TP
+.B rstat
+Reset statistic counters.
+.TP
+.B auth
+Enable authentication (sets device to security mode 3).
+.TP
+.B noauth
+Disable authentication.
+.TP
+.B encrypt
+Enable encryption (sets device to security mode 3).
+.TP
+.B noencrypt
+Disable encryption.
+.TP
+.B secmgr
+Enable security manager (current kernel support is limited).
+.TP
+.B nosecmgr
+Disable security manager.
+.TP
+.B piscan
+Enable page and inquiry scan.
+.TP
+.B noscan
+Disable page and inquiry scan.
+.TP
+.B iscan
+Enable inquiry scan, disable page scan.
+.TP
+.B pscan
+Enable page scan, disable inquiry scan.
+.TP
+\fBptype\fP [\fItype\fP]
+With no
+.I type
+, displays the current packet types. Otherwise, all the packet types specified
+by
+.I type
+are set.
+.I type
+is a comma-separated list of packet types, where the possible packet types are
+.BR DM1 ,
+.BR DM3 ,
+.BR DM5 ,
+.BR DH1 ,
+.BR DH3 ,
+.BR DH5 ,
+.BR HV1 ,
+.BR HV2 ,
+.BR HV3 .
+.TP
+.BI name " [name]"
+With no
+.IR name ,
+prints local name. Otherwise, sets local name to
+.IR name .
+.TP
+.BI class " [class]"
+With no
+.IR class ,
+prints class of device. Otherwise, sets class of device to
+.IR class .
+.I
+class
+is a 24-bit hex number describing the class of device, as specified in section
+1.2 of the Bluetooth Assigned Numers document.
+.TP
+.BI voice " [voice]"
+With no
+.IR voice ,
+prints voice setting. Otherwise, sets voice setting to
+.IR voice .
+.I voice
+is a 16-bit hex number describing the voice setting.
+.TP
+.BI iac " [iac]"
+With no
+.IR iac ,
+prints the current IAC setting. Otherwise, sets the IAC to
+.IR iac .
+.TP
+.BI inqtpl " [level]"
+With no
+.IR level ,
+prints out the current inquiry transmit power level. Otherwise, sets
+inquiry transmit power level to
+.IR level .
+.TP
+.BI inqmode " [mode]"
+With no
+.IR mode ,
+prints out the current inquiry mode. Otherwise, sets inquiry mode to
+.IR mode .
+.TP
+.BI inqdata " [data]"
+With no
+.IR name ,
+prints out the current inquiry data. Otherwise, sets inquiry data to
+.IR data .
+.TP
+.BI inqtype " [type]"
+With no
+.IR type ,
+prints out the current inquiry scan type. Otherwise, sets inquiry scan type to
+.IR type .
+.TP
+\fBinqparams\fP [\fIwin\fP:\fIint\fP]
+With no
+.IR win : int ,
+prints inquiry scan window and interval. Otherwise, sets inquiry scan window
+to
+.I win
+slots and inquiry scan interval to
+.I int
+slots.
+.TP
+\fBpageparms\fP [\fIwin\fP:\fIint\fP]
+With no
+.IR win : int ,
+prints page scan window and interval. Otherwise, sets page scan window to
+.I win
+slots and page scan interval to
+.I int
+slots.
+.TP
+.BI pageto " [to]"
+With no
+.IR to ,
+prints page timeout. Otherwise, sets page timeout
+to .I
+to
+slots.
+.TP
+.BI afhmode " [mode]"
+With no
+.IR mode ,
+prints out the current AFH mode. Otherwise, sets AFH mode to
+.IR mode .
+.TP
+.BI sspmode " [mode]"
+With no
+.IR mode ,
+prints out the current Simple Pairing mode. Otherwise, sets Simple Pairing mode to
+.IR mode .
+.TP
+\fBaclmtu\fP \fImtu\fP:\fIpkt\fP
+Sets ACL MTU to
+to
+.I mtu
+bytes and ACL buffer size to
+.I pkt
+packets.
+.TP
+\fBscomtu\fP \fImtu\fP:\fIpkt\fP
+Sets SCO MTU to
+.I mtu
+bytes and SCO buffer size to
+.I pkt
+packets.
+.TP
+.BI putkey " <bdaddr>"
+This command stores the link key for
+.I bdaddr
+on the device.
+.TP
+.BI delkey " <bdaddr>"
+This command deletes the stored link key for
+.I bdaddr
+from the device.
+.TP
+.BI oobdata
+Display local OOB data.
+.TP
+.BI commands
+Display supported commands.
+.TP
+.BI features
+Display device features.
+.TP
+.BI version
+Display version information.
+.TP
+.BI revision
+Display revision information.
+.TP
+.BI lm " [mode]"
+With no
+.I mode
+, prints link mode.
+.B MASTER
+or
+.B SLAVE
+mean, respectively, to ask to become master or to remain slave when a
+connection request comes in. The additional keyword
+.B ACCEPT
+means that baseband  connections will be accepted even if there are no
+listening
+.I AF_BLUETOOTH
+sockets.
+.I mode
+is
+.B NONE
+or a comma-separated list of keywords, where possible keywords are
+.B MASTER
+and
+.B "ACCEPT" .
+.B NONE
+sets link policy to the default behaviour of remaining slave and not accepting
+baseband connections when there are no listening
+.I AF_BLUETOOTH
+sockets. If
+.B MASTER
+is present, the device will ask to become master if a connection request comes
+in. If
+.B ACCEPT
+is present, the device will accept baseband connections even when there are no
+listening
+.I AF_BLUETOOTH
+sockets.
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Fabrizio Gennari <fabrizio.gennari@philips.com>
diff --git a/tools/hciconfig.c b/tools/hciconfig.c
new file mode 100644 (file)
index 0000000..f1458b9
--- /dev/null
@@ -0,0 +1,2036 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "textfile.h"
+#include "csr.h"
+
+static struct hci_dev_info di;
+static int all;
+
+static void print_dev_hdr(struct hci_dev_info *di);
+static void print_dev_info(int ctl, struct hci_dev_info *di);
+
+static void print_dev_list(int ctl, int flags)
+{
+       struct hci_dev_list_req *dl;
+       struct hci_dev_req *dr;
+       int i;
+
+       if (!(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) +
+               sizeof(uint16_t)))) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+       dl->dev_num = HCI_MAX_DEV;
+       dr = dl->dev_req;
+
+       if (ioctl(ctl, HCIGETDEVLIST, (void *) dl) < 0) {
+               perror("Can't get device list");
+               exit(1);
+       }
+
+       for (i = 0; i< dl->dev_num; i++) {
+               di.dev_id = (dr+i)->dev_id;
+               if (ioctl(ctl, HCIGETDEVINFO, (void *) &di) < 0)
+                       continue;
+               if (hci_test_bit(HCI_RAW, &di.flags) &&
+                               !bacmp(&di.bdaddr, BDADDR_ANY)) {
+                       int dd = hci_open_dev(di.dev_id);
+                       hci_read_bd_addr(dd, &di.bdaddr, 1000);
+                       hci_close_dev(dd);
+               }
+               print_dev_info(ctl, &di);
+       }
+}
+
+static void print_pkt_type(struct hci_dev_info *di)
+{
+       char *str;
+       str = hci_ptypetostr(di->pkt_type);
+       printf("\tPacket type: %s\n", str);
+       bt_free(str);
+}
+
+static void print_link_policy(struct hci_dev_info *di)
+{
+       printf("\tLink policy: %s\n", hci_lptostr(di->link_policy));
+}
+
+static void print_link_mode(struct hci_dev_info *di)
+{
+       char *str;
+       str =  hci_lmtostr(di->link_mode);
+       printf("\tLink mode: %s\n", str);
+       bt_free(str);
+}
+
+static void print_dev_features(struct hci_dev_info *di, int format)
+{
+       printf("\tFeatures: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                               "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+               di->features[0], di->features[1], di->features[2],
+               di->features[3], di->features[4], di->features[5],
+               di->features[6], di->features[7]);
+
+       if (format) {
+               char *tmp = lmp_featurestostr(di->features, "\t\t", 63);
+               printf("%s\n", tmp);
+               bt_free(tmp);
+       }
+}
+
+static void print_le_states(uint64_t states)
+{
+       int i;
+       const char *le_states[] = {
+               "Non-connectable Advertising State" ,
+               "Scannable Advertising State",
+               "Connectable Advertising State",
+               "Directed Advertising State",
+               "Passive Scanning State",
+               "Active Scanning State",
+               "Initiating State/Connection State in Master Role",
+               "Connection State in the Slave Role",
+               "Non-connectable Advertising State and Passive Scanning State combination",
+               "Scannable Advertising State and Passive Scanning State combination",
+               "Connectable Advertising State and Passive Scanning State combination",
+               "Directed Advertising State and Passive Scanning State combination",
+               "Non-connectable Advertising State and Active Scanning State combination",
+               "Scannable Advertising State and Active Scanning State combination",
+               "Connectable Advertising State and Active Scanning State combination",
+               "Directed Advertising State and Active Scanning State combination",
+               "Non-connectable Advertising State and Initiating State combination",
+               "Scannable Advertising State and Initiating State combination",
+               "Non-connectable Advertising State and Master Role combination",
+               "Scannable Advertising State and Master Role combination",
+               "Non-connectable Advertising State and Slave Role combination",
+               "Scannable Advertising State and Slave Role combination",
+               "Passive Scanning State and Initiating State combination",
+               "Active Scanning State and Initiating State combination",
+               "Passive Scanning State and Master Role combination",
+               "Active Scanning State and Master Role combination",
+               "Passive Scanning State and Slave Role combination",
+               "Active Scanning State and Slave Role combination",
+               "Initiating State and Master Role combination/Master Role and Master Role combination",
+               NULL
+       };
+
+       printf("Supported link layer states:\n");
+       for (i = 0; le_states[i]; i++) {
+               const char *status;
+
+               status = states & (1 << i) ? "YES" : "NO ";
+               printf("\t%s %s\n", status, le_states[i]);
+       }
+}
+
+static void cmd_rstat(int ctl, int hdev, char *opt)
+{
+       /* Reset HCI device stat counters */
+       if (ioctl(ctl, HCIDEVRESTAT, hdev) < 0) {
+               fprintf(stderr, "Can't reset stats counters hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+}
+
+static void cmd_scan(int ctl, int hdev, char *opt)
+{
+       struct hci_dev_req dr;
+
+       dr.dev_id  = hdev;
+       dr.dev_opt = SCAN_DISABLED;
+       if (!strcmp(opt, "iscan"))
+               dr.dev_opt = SCAN_INQUIRY;
+       else if (!strcmp(opt, "pscan"))
+               dr.dev_opt = SCAN_PAGE;
+       else if (!strcmp(opt, "piscan"))
+               dr.dev_opt = SCAN_PAGE | SCAN_INQUIRY;
+
+       if (ioctl(ctl, HCISETSCAN, (unsigned long) &dr) < 0) {
+               fprintf(stderr, "Can't set scan mode on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+}
+
+static void cmd_le_addr(int ctl, int hdev, char *opt)
+{
+       struct hci_request rq;
+       le_set_random_address_cp cp;
+       uint8_t status;
+       int dd, err, ret;
+
+       if (!opt)
+               return;
+
+       if (hdev < 0)
+               hdev = hci_get_route(NULL);
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               err = -errno;
+               fprintf(stderr, "Could not open device: %s(%d)\n",
+                                                       strerror(-err), -err);
+               exit(1);
+       }
+
+       memset(&cp, 0, sizeof(cp));
+
+       str2ba(opt, &cp.bdaddr);
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_SET_RANDOM_ADDRESS;
+       rq.cparam = &cp;
+       rq.clen = LE_SET_RANDOM_ADDRESS_CP_SIZE;
+       rq.rparam = &status;
+       rq.rlen = 1;
+
+       ret = hci_send_req(dd, &rq, 1000);
+       if (status || ret < 0) {
+               err = -errno;
+               fprintf(stderr, "Can't set random address for hci%d: "
+                               "%s (%d)\n", hdev, strerror(-err), -err);
+       }
+
+       hci_close_dev(dd);
+}
+
+static void cmd_le_adv(int ctl, int hdev, char *opt)
+{
+       struct hci_request rq;
+       le_set_advertise_enable_cp advertise_cp;
+       uint8_t status;
+       int dd, ret;
+
+       if (hdev < 0)
+               hdev = hci_get_route(NULL);
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               perror("Could not open device");
+               exit(1);
+       }
+
+       memset(&advertise_cp, 0, sizeof(advertise_cp));
+       if (strcmp(opt, "noleadv") == 0)
+               advertise_cp.enable = 0x00;
+       else
+               advertise_cp.enable = 0x01;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf = OGF_LE_CTL;
+       rq.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
+       rq.cparam = &advertise_cp;
+       rq.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
+       rq.rparam = &status;
+       rq.rlen = 1;
+
+       ret = hci_send_req(dd, &rq, 1000);
+
+       hci_close_dev(dd);
+
+       if (ret < 0) {
+               fprintf(stderr, "Can't set advertise mode on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (status) {
+               fprintf(stderr, "LE set advertise enable on hci%d returned status %d\n",
+                                               hdev, status);
+               exit(1);
+       }
+}
+
+static void cmd_le_states(int ctl, int hdev, char *opt)
+{
+       le_read_supported_states_rp rp;
+       struct hci_request rq;
+       int err, dd;
+
+       if (hdev < 0)
+               hdev = hci_get_route(NULL);
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       memset(&rp, 0, sizeof(rp));
+       memset(&rq, 0, sizeof(rq));
+
+       rq.ogf    = OGF_LE_CTL;
+       rq.ocf    = OCF_LE_READ_SUPPORTED_STATES;
+       rq.rparam = &rp;
+       rq.rlen   = LE_READ_SUPPORTED_STATES_RP_SIZE;
+
+       err = hci_send_req(dd, &rq, 1000);
+
+       hci_close_dev(dd);
+
+       if (err < 0) {
+               fprintf(stderr, "Can't read LE supported states on hci%d:"
+                               " %s(%d)\n", hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (rp.status) {
+               fprintf(stderr, "Read LE supported states on hci%d"
+                               " returned status %d\n", hdev, rp.status);
+               exit(1);
+       }
+
+       print_le_states(rp.states);
+}
+
+static void cmd_iac(int ctl, int hdev, char *opt)
+{
+       int s = hci_open_dev(hdev);
+
+       if (s < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+       if (opt) {
+               int l = strtoul(opt, 0, 16);
+               uint8_t lap[3];
+               if (!strcasecmp(opt, "giac")) {
+                       l = 0x9e8b33;
+               } else if (!strcasecmp(opt, "liac")) {
+                       l = 0x9e8b00;
+               } else if (l < 0x9e8b00 || l > 0x9e8b3f) {
+                       printf("Invalid access code 0x%x\n", l);
+                       exit(1);
+               }
+               lap[0] = (l & 0xff);
+               lap[1] = (l >> 8) & 0xff;
+               lap[2] = (l >> 16) & 0xff;
+               if (hci_write_current_iac_lap(s, 1, lap, 1000) < 0) {
+                       printf("Failed to set IAC on hci%d: %s\n", hdev, strerror(errno));
+                       exit(1);
+               }
+       } else {
+               uint8_t lap[3 * MAX_IAC_LAP];
+               int i, j;
+               uint8_t n;
+               if (hci_read_current_iac_lap(s, &n, lap, 1000) < 0) {
+                       printf("Failed to read IAC from hci%d: %s\n", hdev, strerror(errno));
+                       exit(1);
+               }
+               print_dev_hdr(&di);
+               printf("\tIAC: ");
+               for (i = 0; i < n; i++) {
+                       printf("0x");
+                       for (j = 3; j--; )
+                               printf("%02x", lap[j + 3 * i]);
+                       if (i < n - 1)
+                               printf(", ");
+               }
+               printf("\n");
+       }
+       close(s);
+}
+
+static void cmd_auth(int ctl, int hdev, char *opt)
+{
+       struct hci_dev_req dr;
+
+       dr.dev_id = hdev;
+       if (!strcmp(opt, "auth"))
+               dr.dev_opt = AUTH_ENABLED;
+       else
+               dr.dev_opt = AUTH_DISABLED;
+
+       if (ioctl(ctl, HCISETAUTH, (unsigned long) &dr) < 0) {
+               fprintf(stderr, "Can't set auth on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+}
+
+static void cmd_encrypt(int ctl, int hdev, char *opt)
+{
+       struct hci_dev_req dr;
+
+       dr.dev_id = hdev;
+       if (!strcmp(opt, "encrypt"))
+               dr.dev_opt = ENCRYPT_P2P;
+       else
+               dr.dev_opt = ENCRYPT_DISABLED;
+
+       if (ioctl(ctl, HCISETENCRYPT, (unsigned long) &dr) < 0) {
+               fprintf(stderr, "Can't set encrypt on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+}
+
+static void cmd_up(int ctl, int hdev, char *opt)
+{
+       /* Start HCI device */
+       if (ioctl(ctl, HCIDEVUP, hdev) < 0) {
+               if (errno == EALREADY)
+                       return;
+               fprintf(stderr, "Can't init device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+}
+
+static void cmd_down(int ctl, int hdev, char *opt)
+{
+       /* Stop HCI device */
+       if (ioctl(ctl, HCIDEVDOWN, hdev) < 0) {
+               fprintf(stderr, "Can't down device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+}
+
+static void cmd_reset(int ctl, int hdev, char *opt)
+{
+       /* Reset HCI device */
+#if 0
+       if (ioctl(ctl, HCIDEVRESET, hdev) < 0 ){
+               fprintf(stderr, "Reset failed for device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+#endif
+       cmd_down(ctl, hdev, "down");
+       cmd_up(ctl, hdev, "up");
+}
+
+static void cmd_ptype(int ctl, int hdev, char *opt)
+{
+       struct hci_dev_req dr;
+
+       dr.dev_id = hdev;
+
+       if (hci_strtoptype(opt, &dr.dev_opt)) {
+               if (ioctl(ctl, HCISETPTYPE, (unsigned long) &dr) < 0) {
+                       fprintf(stderr, "Can't set pkttype on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               print_dev_hdr(&di);
+               print_pkt_type(&di);
+       }
+}
+
+static void cmd_lp(int ctl, int hdev, char *opt)
+{
+       struct hci_dev_req dr;
+
+       dr.dev_id = hdev;
+
+       if (hci_strtolp(opt, &dr.dev_opt)) {
+               if (ioctl(ctl, HCISETLINKPOL, (unsigned long) &dr) < 0) {
+                       fprintf(stderr, "Can't set link policy on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               print_dev_hdr(&di);
+               print_link_policy(&di);
+       }
+}
+
+static void cmd_lm(int ctl, int hdev, char *opt)
+{
+       struct hci_dev_req dr;
+
+       dr.dev_id = hdev;
+
+       if (hci_strtolm(opt, &dr.dev_opt)) {
+               if (ioctl(ctl, HCISETLINKMODE, (unsigned long) &dr) < 0) {
+                       fprintf(stderr, "Can't set default link mode on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               print_dev_hdr(&di);
+               print_link_mode(&di);
+       }
+}
+
+static void cmd_aclmtu(int ctl, int hdev, char *opt)
+{
+       struct hci_dev_req dr = { .dev_id = hdev };
+       uint16_t mtu, mpkt;
+
+       if (!opt)
+               return;
+
+       if (sscanf(opt, "%4hu:%4hu", &mtu, &mpkt) != 2)
+               return;
+
+       dr.dev_opt = htobl(htobs(mpkt) | (htobs(mtu) << 16));
+
+       if (ioctl(ctl, HCISETACLMTU, (unsigned long) &dr) < 0) {
+               fprintf(stderr, "Can't set ACL mtu on hci%d: %s(%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+}
+
+static void cmd_scomtu(int ctl, int hdev, char *opt)
+{
+       struct hci_dev_req dr = { .dev_id = hdev };
+       uint16_t mtu, mpkt;
+
+       if (!opt)
+               return;
+
+       if (sscanf(opt, "%4hu:%4hu", &mtu, &mpkt) != 2)
+               return;
+
+       dr.dev_opt = htobl(htobs(mpkt) | (htobs(mtu) << 16));
+
+       if (ioctl(ctl, HCISETSCOMTU, (unsigned long) &dr) < 0) {
+               fprintf(stderr, "Can't set SCO mtu on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+}
+
+static void cmd_features(int ctl, int hdev, char *opt)
+{
+       uint8_t features[8], max_page = 0;
+       char *tmp;
+       int i, dd;
+
+       if (!(di.features[7] & LMP_EXT_FEAT)) {
+               print_dev_hdr(&di);
+               print_dev_features(&di, 1);
+               return;
+       }
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (hci_read_local_ext_features(dd, 0, &max_page, features, 1000) < 0) {
+               fprintf(stderr, "Can't read extended features hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       print_dev_hdr(&di);
+       printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                               "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+               (max_page > 0) ? " page 0" : "",
+               features[0], features[1], features[2], features[3],
+               features[4], features[5], features[6], features[7]);
+
+       tmp = lmp_featurestostr(di.features, "\t\t", 63);
+       printf("%s\n", tmp);
+       bt_free(tmp);
+
+       for (i = 1; i <= max_page; i++) {
+               if (hci_read_local_ext_features(dd, i, NULL,
+                                                       features, 1000) < 0)
+                       continue;
+
+               printf("\tFeatures page %d: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                                       "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", i,
+                       features[0], features[1], features[2], features[3],
+                       features[4], features[5], features[6], features[7]);
+       }
+
+       hci_close_dev(dd);
+}
+
+static void cmd_name(int ctl, int hdev, char *opt)
+{
+       int dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (opt) {
+               if (hci_write_local_name(dd, opt, 2000) < 0) {
+                       fprintf(stderr, "Can't change local name on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               char name[249];
+               int i;
+
+               if (hci_read_local_name(dd, sizeof(name), name, 1000) < 0) {
+                       fprintf(stderr, "Can't read local name on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+
+               for (i = 0; i < 248 && name[i]; i++) {
+                       if ((unsigned char) name[i] < 32 || name[i] == 127)
+                               name[i] = '.';
+               }
+
+               name[248] = '\0';
+
+               print_dev_hdr(&di);
+               printf("\tName: '%s'\n", name);
+       }
+
+       hci_close_dev(dd);
+}
+
+/*
+ * see http://www.bluetooth.org/assigned-numbers/baseband.htm --- all
+ * strings are reproduced verbatim
+ */
+static char *get_minor_device_name(int major, int minor)
+{
+       switch (major) {
+       case 0: /* misc */
+               return "";
+       case 1: /* computer */
+               switch (minor) {
+               case 0:
+                       return "Uncategorized";
+               case 1:
+                       return "Desktop workstation";
+               case 2:
+                       return "Server";
+               case 3:
+                       return "Laptop";
+               case 4:
+                       return "Handheld";
+               case 5:
+                       return "Palm";
+               case 6:
+                       return "Wearable";
+               }
+               break;
+       case 2: /* phone */
+               switch (minor) {
+               case 0:
+                       return "Uncategorized";
+               case 1:
+                       return "Cellular";
+               case 2:
+                       return "Cordless";
+               case 3:
+                       return "Smart phone";
+               case 4:
+                       return "Wired modem or voice gateway";
+               case 5:
+                       return "Common ISDN Access";
+               case 6:
+                       return "Sim Card Reader";
+               }
+               break;
+       case 3: /* lan access */
+               if (minor == 0)
+                       return "Uncategorized";
+               switch (minor / 8) {
+               case 0:
+                       return "Fully available";
+               case 1:
+                       return "1-17% utilized";
+               case 2:
+                       return "17-33% utilized";
+               case 3:
+                       return "33-50% utilized";
+               case 4:
+                       return "50-67% utilized";
+               case 5:
+                       return "67-83% utilized";
+               case 6:
+                       return "83-99% utilized";
+               case 7:
+                       return "No service available";
+               }
+               break;
+       case 4: /* audio/video */
+               switch (minor) {
+               case 0:
+                       return "Uncategorized";
+               case 1:
+                       return "Device conforms to the Headset profile";
+               case 2:
+                       return "Hands-free";
+                       /* 3 is reserved */
+               case 4:
+                       return "Microphone";
+               case 5:
+                       return "Loudspeaker";
+               case 6:
+                       return "Headphones";
+               case 7:
+                       return "Portable Audio";
+               case 8:
+                       return "Car Audio";
+               case 9:
+                       return "Set-top box";
+               case 10:
+                       return "HiFi Audio Device";
+               case 11:
+                       return "VCR";
+               case 12:
+                       return "Video Camera";
+               case 13:
+                       return "Camcorder";
+               case 14:
+                       return "Video Monitor";
+               case 15:
+                       return "Video Display and Loudspeaker";
+               case 16:
+                       return "Video Conferencing";
+                       /* 17 is reserved */
+               case 18:
+                       return "Gaming/Toy";
+               }
+               break;
+       case 5: /* peripheral */ {
+               static char cls_str[48];
+
+               cls_str[0] = '\0';
+
+               switch (minor & 48) {
+               case 16:
+                       strncpy(cls_str, "Keyboard", sizeof(cls_str));
+                       break;
+               case 32:
+                       strncpy(cls_str, "Pointing device", sizeof(cls_str));
+                       break;
+               case 48:
+                       strncpy(cls_str, "Combo keyboard/pointing device", sizeof(cls_str));
+                       break;
+               }
+               if ((minor & 15) && (strlen(cls_str) > 0))
+                       strcat(cls_str, "/");
+
+               switch (minor & 15) {
+               case 0:
+                       break;
+               case 1:
+                       strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               case 2:
+                       strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               case 3:
+                       strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               case 4:
+                       strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               case 5:
+                       strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               case 6:
+                       strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               default:
+                       strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               }
+               if (strlen(cls_str) > 0)
+                       return cls_str;
+       }
+       case 6: /* imaging */
+               if (minor & 4)
+                       return "Display";
+               if (minor & 8)
+                       return "Camera";
+               if (minor & 16)
+                       return "Scanner";
+               if (minor & 32)
+                       return "Printer";
+               break;
+       case 7: /* wearable */
+               switch (minor) {
+               case 1:
+                       return "Wrist Watch";
+               case 2:
+                       return "Pager";
+               case 3:
+                       return "Jacket";
+               case 4:
+                       return "Helmet";
+               case 5:
+                       return "Glasses";
+               }
+               break;
+       case 8: /* toy */
+               switch (minor) {
+               case 1:
+                       return "Robot";
+               case 2:
+                       return "Vehicle";
+               case 3:
+                       return "Doll / Action Figure";
+               case 4:
+                       return "Controller";
+               case 5:
+                       return "Game";
+               }
+               break;
+       case 63:        /* uncategorised */
+               return "";
+       }
+       return "Unknown (reserved) minor device class";
+}
+
+static void cmd_class(int ctl, int hdev, char *opt)
+{
+       static const char *services[] = { "Positioning",
+                                       "Networking",
+                                       "Rendering",
+                                       "Capturing",
+                                       "Object Transfer",
+                                       "Audio",
+                                       "Telephony",
+                                       "Information" };
+       static const char *major_devices[] = { "Miscellaneous",
+                                       "Computer",
+                                       "Phone",
+                                       "LAN Access",
+                                       "Audio/Video",
+                                       "Peripheral",
+                                       "Imaging",
+                                       "Uncategorized" };
+       int s = hci_open_dev(hdev);
+
+       if (s < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+       if (opt) {
+               uint32_t cod = strtoul(opt, NULL, 16);
+               if (hci_write_class_of_dev(s, cod, 2000) < 0) {
+                       fprintf(stderr, "Can't write local class of device on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               uint8_t cls[3];
+               if (hci_read_class_of_dev(s, cls, 1000) < 0) {
+                       fprintf(stderr, "Can't read class of device on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+               print_dev_hdr(&di);
+               printf("\tClass: 0x%02x%02x%02x\n", cls[2], cls[1], cls[0]);
+               printf("\tService Classes: ");
+               if (cls[2]) {
+                       unsigned int i;
+                       int first = 1;
+                       for (i = 0; i < (sizeof(services) / sizeof(*services)); i++)
+                               if (cls[2] & (1 << i)) {
+                                       if (!first)
+                                               printf(", ");
+                                       printf("%s", services[i]);
+                                       first = 0;
+                               }
+               } else
+                       printf("Unspecified");
+               printf("\n\tDevice Class: ");
+               if ((cls[1] & 0x1f) >= sizeof(major_devices) / sizeof(*major_devices))
+                       printf("Invalid Device Class!\n");
+               else
+                       printf("%s, %s\n", major_devices[cls[1] & 0x1f],
+                               get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2));
+       }
+}
+
+static void cmd_voice(int ctl, int hdev, char *opt)
+{
+       static char *icf[] = {  "Linear",
+                               "u-Law",
+                               "A-Law",
+                               "Reserved" };
+
+       static char *idf[] = {  "1's complement",
+                               "2's complement",
+                               "Sign-Magnitude",
+                               "Reserved" };
+
+       static char *iss[] = {  "8 bit",
+                               "16 bit" };
+
+       static char *acf[] = {  "CVSD",
+                               "u-Law",
+                               "A-Law",
+                               "Reserved" };
+
+       int s = hci_open_dev(hdev);
+
+       if (s < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+       if (opt) {
+               uint16_t vs = htobs(strtoul(opt, NULL, 16));
+               if (hci_write_voice_setting(s, vs, 2000) < 0) {
+                       fprintf(stderr, "Can't write voice setting on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               uint16_t vs;
+               uint8_t ic;
+               if (hci_read_voice_setting(s, &vs, 1000) < 0) {
+                       fprintf(stderr, "Can't read voice setting on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+               vs = htobs(vs);
+               ic = (vs & 0x0300) >> 8;
+               print_dev_hdr(&di);
+               printf("\tVoice setting: 0x%04x%s\n", vs,
+                       ((vs & 0x03fc) == 0x0060) ? " (Default Condition)" : "");
+               printf("\tInput Coding: %s\n", icf[ic]);
+               printf("\tInput Data Format: %s\n", idf[(vs & 0xc0) >> 6]);
+
+               if (!ic) {
+                       printf("\tInput Sample Size: %s\n",
+                               iss[(vs & 0x20) >> 5]);
+                       printf("\t# of bits padding at MSB: %d\n",
+                               (vs & 0x1c) >> 2);
+               }
+               printf("\tAir Coding Format: %s\n", acf[vs & 0x03]);
+       }
+}
+
+static int get_link_key(const bdaddr_t *local, const bdaddr_t *peer,
+                       uint8_t *key)
+{
+       char filename[PATH_MAX + 1], addr[18], tmp[3], *str;
+       int i;
+
+       ba2str(local, addr);
+       create_name(filename, PATH_MAX, STORAGEDIR, addr, "linkkeys");
+
+       ba2str(peer, addr);
+       str = textfile_get(filename, addr);
+       if (!str)
+               return -EIO;
+
+       memset(tmp, 0, sizeof(tmp));
+       for (i = 0; i < 16; i++) {
+               memcpy(tmp, str + (i * 2), 2);
+               key[i] = (uint8_t) strtol(tmp, NULL, 16);
+       }
+
+       free(str);
+
+       return 0;
+}
+
+static void cmd_putkey(int ctl, int hdev, char *opt)
+{
+       struct hci_dev_info di;
+       bdaddr_t bdaddr;
+       uint8_t key[16];
+       int dd;
+
+       if (!opt)
+               return;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (hci_devinfo(hdev, &di) < 0) {
+               fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       str2ba(opt, &bdaddr);
+       if (get_link_key(&di.bdaddr, &bdaddr, key) < 0) {
+               fprintf(stderr, "Can't find link key for %s on hci%d\n", opt, hdev);
+               exit(1);
+       }
+
+       if (hci_write_stored_link_key(dd, &bdaddr, key, 1000) < 0) {
+               fprintf(stderr, "Can't write stored link key on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       hci_close_dev(dd);
+}
+
+static void cmd_delkey(int ctl, int hdev, char *opt)
+{
+       bdaddr_t bdaddr;
+       uint8_t all;
+       int dd;
+
+       if (!opt)
+               return;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (!strcasecmp(opt, "all")) {
+               bacpy(&bdaddr, BDADDR_ANY);
+               all = 1;
+       } else {
+               str2ba(opt, &bdaddr);
+               all = 0;
+       }
+
+       if (hci_delete_stored_link_key(dd, &bdaddr, all, 1000) < 0) {
+               fprintf(stderr, "Can't delete stored link key on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       hci_close_dev(dd);
+}
+
+static void cmd_oob_data(int ctl, int hdev, char *opt)
+{
+       uint8_t hash[16], randomizer[16];
+       int i, dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (hci_read_local_oob_data(dd, hash, randomizer, 1000) < 0) {
+               fprintf(stderr, "Can't read local OOB data on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       print_dev_hdr(&di);
+       printf("\tOOB Hash:  ");
+       for (i = 0; i < 16; i++)
+               printf(" %02x", hash[i]);
+       printf("\n\tRandomizer:");
+       for (i = 0; i < 16; i++)
+               printf(" %02x", randomizer[i]);
+       printf("\n");
+
+       hci_close_dev(dd);
+}
+
+static void cmd_commands(int ctl, int hdev, char *opt)
+{
+       uint8_t cmds[64];
+       char *str;
+       int i, n, dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (hci_read_local_commands(dd, cmds, 1000) < 0) {
+               fprintf(stderr, "Can't read support commands on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       print_dev_hdr(&di);
+       for (i = 0; i < 64; i++) {
+               if (!cmds[i])
+                       continue;
+
+               printf("%s Octet %-2d = 0x%02x (Bit",
+                       i ? "\t\t ": "\tCommands:", i, cmds[i]);
+               for (n = 0; n < 8; n++)
+                       if (cmds[i] & (1 << n))
+                               printf(" %d", n);
+               printf(")\n");
+       }
+
+       str = hci_commandstostr(cmds, "\t", 71);
+       printf("%s\n", str);
+       bt_free(str);
+
+       hci_close_dev(dd);
+}
+
+static void cmd_version(int ctl, int hdev, char *opt)
+{
+       struct hci_version ver;
+       char *hciver, *lmpver;
+       int dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (hci_read_local_version(dd, &ver, 1000) < 0) {
+               fprintf(stderr, "Can't read version info hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       hciver = hci_vertostr(ver.hci_ver);
+       lmpver = lmp_vertostr(ver.lmp_ver);
+
+       print_dev_hdr(&di);
+       printf("\tHCI Version: %s (0x%x)  Revision: 0x%x\n"
+               "\tLMP Version: %s (0x%x)  Subversion: 0x%x\n"
+               "\tManufacturer: %s (%d)\n",
+               hciver ? hciver : "n/a", ver.hci_ver, ver.hci_rev,
+               lmpver ? lmpver : "n/a", ver.lmp_ver, ver.lmp_subver,
+               bt_compidtostr(ver.manufacturer), ver.manufacturer);
+
+       if (hciver)
+               bt_free(hciver);
+       if (lmpver)
+               bt_free(lmpver);
+
+       hci_close_dev(dd);
+}
+
+static void cmd_inq_tpl(int ctl, int hdev, char *opt)
+{
+       int dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (opt) {
+               int8_t level = atoi(opt);
+
+               if (hci_write_inquiry_transmit_power_level(dd, level, 2000) < 0) {
+                       fprintf(stderr, "Can't set inquiry transmit power level on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               int8_t level;
+
+               if (hci_read_inq_response_tx_power_level(dd, &level, 1000) < 0) {
+                       fprintf(stderr, "Can't read inquiry transmit power level on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+
+               print_dev_hdr(&di);
+               printf("\tInquiry transmit power level: %d\n", level);
+       }
+
+       hci_close_dev(dd);
+}
+
+static void cmd_inq_mode(int ctl, int hdev, char *opt)
+{
+       int dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (opt) {
+               uint8_t mode = atoi(opt);
+
+               if (hci_write_inquiry_mode(dd, mode, 2000) < 0) {
+                       fprintf(stderr, "Can't set inquiry mode on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               uint8_t mode;
+
+               if (hci_read_inquiry_mode(dd, &mode, 1000) < 0) {
+                       fprintf(stderr, "Can't read inquiry mode on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+
+               print_dev_hdr(&di);
+               printf("\tInquiry mode: ");
+               switch (mode) {
+               case 0:
+                       printf("Standard Inquiry\n");
+                       break;
+               case 1:
+                       printf("Inquiry with RSSI\n");
+                       break;
+               case 2:
+                       printf("Inquiry with RSSI or Extended Inquiry\n");
+                       break;
+               default:
+                       printf("Unknown (0x%02x)\n", mode);
+                       break;
+               }
+       }
+
+       hci_close_dev(dd);
+}
+
+static void cmd_inq_data(int ctl, int hdev, char *opt)
+{
+       int i, dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (opt) {
+               uint8_t fec = 0, data[HCI_MAX_EIR_LENGTH];
+               char tmp[3];
+               int i, size;
+
+               memset(data, 0, sizeof(data));
+
+               memset(tmp, 0, sizeof(tmp));
+               size = (strlen(opt) + 1) / 2;
+               if (size > HCI_MAX_EIR_LENGTH)
+                       size = HCI_MAX_EIR_LENGTH;
+
+               for (i = 0; i < size; i++) {
+                       memcpy(tmp, opt + (i * 2), 2);
+                       data[i] = strtol(tmp, NULL, 16);
+               }
+
+               if (hci_write_ext_inquiry_response(dd, fec, data, 2000) < 0) {
+                       fprintf(stderr, "Can't set extended inquiry response on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               uint8_t fec, data[HCI_MAX_EIR_LENGTH], len, type, *ptr;
+               char *str;
+
+               if (hci_read_ext_inquiry_response(dd, &fec, data, 1000) < 0) {
+                       fprintf(stderr, "Can't read extended inquiry response on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+
+               print_dev_hdr(&di);
+               printf("\tFEC %s\n\t\t", fec ? "enabled" : "disabled");
+               for (i = 0; i < HCI_MAX_EIR_LENGTH; i++)
+                       printf("%02x%s%s", data[i], (i + 1) % 8 ? "" : " ",
+                               (i + 1) % 16 ? " " : (i < 239 ? "\n\t\t" : "\n"));
+
+               ptr = data;
+               while (*ptr) {
+                       len = *ptr++;
+                       type = *ptr++;
+                       switch (type) {
+                       case 0x01:
+                               printf("\tFlags:");
+                               for (i = 0; i < len - 1; i++)
+                                       printf(" 0x%2.2x", *((uint8_t *) (ptr + i)));
+                               printf("\n");
+                               break;
+                       case 0x02:
+                       case 0x03:
+                               printf("\t%s service classes:",
+                                       type == 0x02 ? "Shortened" : "Complete");
+                               for (i = 0; i < (len - 1) / 2; i++) {
+                                       uint16_t val = bt_get_le16((ptr + (i * 2)));
+                                       printf(" 0x%4.4x", val);
+                               }
+                               printf("\n");
+                               break;
+                       case 0x08:
+                       case 0x09:
+                               str = malloc(len);
+                               if (str) {
+                                       snprintf(str, len, "%s", ptr);
+                                       for (i = 0; i < len - 1; i++) {
+                                               if ((unsigned char) str[i] < 32 || str[i] == 127)
+                                                       str[i] = '.';
+                                       }
+                                       printf("\t%s local name: \'%s\'\n",
+                                               type == 0x08 ? "Shortened" : "Complete", str);
+                                       free(str);
+                               }
+                               break;
+                       case 0x0a:
+                               printf("\tTX power level: %d\n", *((int8_t *) ptr));
+                               break;
+                       case 0x10:
+                               printf("\tDevice ID with %d bytes data\n",
+                                                               len - 1);
+                               break;
+                       default:
+                               printf("\tUnknown type 0x%02x with %d bytes data\n",
+                                                               type, len - 1);
+                               break;
+                       }
+
+                       ptr += (len - 1);
+               }
+
+               printf("\n");
+       }
+
+       hci_close_dev(dd);
+}
+
+static void cmd_inq_type(int ctl, int hdev, char *opt)
+{
+       int dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (opt) {
+               uint8_t type = atoi(opt);
+
+               if (hci_write_inquiry_scan_type(dd, type, 2000) < 0) {
+                       fprintf(stderr, "Can't set inquiry scan type on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               uint8_t type;
+
+               if (hci_read_inquiry_scan_type(dd, &type, 1000) < 0) {
+                       fprintf(stderr, "Can't read inquiry scan type on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+
+               print_dev_hdr(&di);
+               printf("\tInquiry scan type: %s\n",
+                       type == 1 ? "Interlaced Inquiry Scan" : "Standard Inquiry Scan");
+       }
+
+       hci_close_dev(dd);
+}
+
+static void cmd_inq_parms(int ctl, int hdev, char *opt)
+{
+       struct hci_request rq;
+       int s;
+
+       if ((s = hci_open_dev(hdev)) < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       memset(&rq, 0, sizeof(rq));
+
+       if (opt) {
+               unsigned int window, interval;
+               write_inq_activity_cp cp;
+
+               if (sscanf(opt,"%4u:%4u", &window, &interval) != 2) {
+                       printf("Invalid argument format\n");
+                       exit(1);
+               }
+
+               rq.ogf = OGF_HOST_CTL;
+               rq.ocf = OCF_WRITE_INQ_ACTIVITY;
+               rq.cparam = &cp;
+               rq.clen = WRITE_INQ_ACTIVITY_CP_SIZE;
+
+               cp.window = htobs((uint16_t) window);
+               cp.interval = htobs((uint16_t) interval);
+
+               if (window < 0x12 || window > 0x1000)
+                       printf("Warning: inquiry window out of range!\n");
+
+               if (interval < 0x12 || interval > 0x1000)
+                       printf("Warning: inquiry interval out of range!\n");
+
+               if (hci_send_req(s, &rq, 2000) < 0) {
+                       fprintf(stderr, "Can't set inquiry parameters name on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               uint16_t window, interval;
+               read_inq_activity_rp rp;
+
+               rq.ogf = OGF_HOST_CTL;
+               rq.ocf = OCF_READ_INQ_ACTIVITY;
+               rq.rparam = &rp;
+               rq.rlen = READ_INQ_ACTIVITY_RP_SIZE;
+
+               if (hci_send_req(s, &rq, 1000) < 0) {
+                       fprintf(stderr, "Can't read inquiry parameters on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+               if (rp.status) {
+                       printf("Read inquiry parameters on hci%d returned status %d\n",
+                                                       hdev, rp.status);
+                       exit(1);
+               }
+               print_dev_hdr(&di);
+
+               window   = btohs(rp.window);
+               interval = btohs(rp.interval);
+               printf("\tInquiry interval: %u slots (%.2f ms), window: %u slots (%.2f ms)\n",
+                               interval, (float)interval * 0.625, window, (float)window * 0.625);
+       }
+}
+
+static void cmd_page_parms(int ctl, int hdev, char *opt)
+{
+       struct hci_request rq;
+       int s;
+
+       if ((s = hci_open_dev(hdev)) < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       memset(&rq, 0, sizeof(rq));
+
+       if (opt) {
+               unsigned int window, interval;
+               write_page_activity_cp cp;
+
+               if (sscanf(opt,"%4u:%4u", &window, &interval) != 2) {
+                       printf("Invalid argument format\n");
+                       exit(1);
+               }
+
+               rq.ogf = OGF_HOST_CTL;
+               rq.ocf = OCF_WRITE_PAGE_ACTIVITY;
+               rq.cparam = &cp;
+               rq.clen = WRITE_PAGE_ACTIVITY_CP_SIZE;
+
+               cp.window = htobs((uint16_t) window);
+               cp.interval = htobs((uint16_t) interval);
+
+               if (window < 0x12 || window > 0x1000)
+                       printf("Warning: page window out of range!\n");
+
+               if (interval < 0x12 || interval > 0x1000)
+                       printf("Warning: page interval out of range!\n");
+
+               if (hci_send_req(s, &rq, 2000) < 0) {
+                       fprintf(stderr, "Can't set page parameters name on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               uint16_t window, interval;
+               read_page_activity_rp rp;
+
+               rq.ogf = OGF_HOST_CTL;
+               rq.ocf = OCF_READ_PAGE_ACTIVITY;
+               rq.rparam = &rp;
+               rq.rlen = READ_PAGE_ACTIVITY_RP_SIZE;
+
+               if (hci_send_req(s, &rq, 1000) < 0) {
+                       fprintf(stderr, "Can't read page parameters on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+               if (rp.status) {
+                       printf("Read page parameters on hci%d returned status %d\n",
+                                                       hdev, rp.status);
+                       exit(1);
+               }
+               print_dev_hdr(&di);
+
+               window   = btohs(rp.window);
+               interval = btohs(rp.interval);
+               printf("\tPage interval: %u slots (%.2f ms), "
+                       "window: %u slots (%.2f ms)\n",
+                       interval, (float)interval * 0.625,
+                       window, (float)window * 0.625);
+       }
+}
+
+static void cmd_page_to(int ctl, int hdev, char *opt)
+{
+       struct hci_request rq;
+       int s;
+
+       if ((s = hci_open_dev(hdev)) < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       memset(&rq, 0, sizeof(rq));
+
+       if (opt) {
+               unsigned int timeout;
+               write_page_timeout_cp cp;
+
+               if (sscanf(opt,"%5u", &timeout) != 1) {
+                       printf("Invalid argument format\n");
+                       exit(1);
+               }
+
+               rq.ogf = OGF_HOST_CTL;
+               rq.ocf = OCF_WRITE_PAGE_TIMEOUT;
+               rq.cparam = &cp;
+               rq.clen = WRITE_PAGE_TIMEOUT_CP_SIZE;
+
+               cp.timeout = htobs((uint16_t) timeout);
+
+               if (timeout < 0x01 || timeout > 0xFFFF)
+                       printf("Warning: page timeout out of range!\n");
+
+               if (hci_send_req(s, &rq, 2000) < 0) {
+                       fprintf(stderr, "Can't set page timeout on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               uint16_t timeout;
+               read_page_timeout_rp rp;
+
+               rq.ogf = OGF_HOST_CTL;
+               rq.ocf = OCF_READ_PAGE_TIMEOUT;
+               rq.rparam = &rp;
+               rq.rlen = READ_PAGE_TIMEOUT_RP_SIZE;
+
+               if (hci_send_req(s, &rq, 1000) < 0) {
+                       fprintf(stderr, "Can't read page timeout on hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+                       exit(1);
+               }
+               if (rp.status) {
+                       printf("Read page timeout on hci%d returned status %d\n",
+                                                       hdev, rp.status);
+                       exit(1);
+               }
+               print_dev_hdr(&di);
+
+               timeout = btohs(rp.timeout);
+               printf("\tPage timeout: %u slots (%.2f ms)\n",
+                               timeout, (float)timeout * 0.625);
+       }
+}
+
+static void cmd_afh_mode(int ctl, int hdev, char *opt)
+{
+       int dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (opt) {
+               uint8_t mode = atoi(opt);
+
+               if (hci_write_afh_mode(dd, mode, 2000) < 0) {
+                       fprintf(stderr, "Can't set AFH mode on hci%d: %s (%d)\n",
+                                       hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               uint8_t mode;
+
+               if (hci_read_afh_mode(dd, &mode, 1000) < 0) {
+                       fprintf(stderr, "Can't read AFH mode on hci%d: %s (%d)\n",
+                                       hdev, strerror(errno), errno);
+                       exit(1);
+               }
+
+               print_dev_hdr(&di);
+               printf("\tAFH mode: %s\n", mode == 1 ? "Enabled" : "Disabled");
+       }
+}
+
+static void cmd_ssp_mode(int ctl, int hdev, char *opt)
+{
+       int dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (opt) {
+               uint8_t mode = atoi(opt);
+
+               if (hci_write_simple_pairing_mode(dd, mode, 2000) < 0) {
+                       fprintf(stderr, "Can't set Simple Pairing mode on hci%d: %s (%d)\n",
+                                       hdev, strerror(errno), errno);
+                       exit(1);
+               }
+       } else {
+               uint8_t mode;
+
+               if (hci_read_simple_pairing_mode(dd, &mode, 1000) < 0) {
+                       fprintf(stderr, "Can't read Simple Pairing mode on hci%d: %s (%d)\n",
+                                       hdev, strerror(errno), errno);
+                       exit(1);
+               }
+
+               print_dev_hdr(&di);
+               printf("\tSimple Pairing mode: %s\n",
+                       mode == 1 ? "Enabled" : "Disabled");
+       }
+}
+
+static void print_rev_ericsson(int dd)
+{
+       struct hci_request rq;
+       unsigned char buf[102];
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x000f;
+       rq.cparam = NULL;
+       rq.clen   = 0;
+       rq.rparam = &buf;
+       rq.rlen   = sizeof(buf);
+
+       if (hci_send_req(dd, &rq, 1000) < 0) {
+               printf("\nCan't read revision info: %s (%d)\n",
+                       strerror(errno), errno);
+               return;
+       }
+
+       printf("\t%s\n", buf + 1);
+}
+
+static void print_rev_csr(int dd, uint16_t rev)
+{
+       uint16_t buildid, chipver, chiprev, maxkeylen, mapsco;
+
+       if (csr_read_varid_uint16(dd, 0, CSR_VARID_BUILDID, &buildid) < 0) {
+               printf("\t%s\n", csr_buildidtostr(rev));
+               return;
+       }
+
+       printf("\t%s\n", csr_buildidtostr(buildid));
+
+       if (!csr_read_varid_uint16(dd, 1, CSR_VARID_CHIPVER, &chipver)) {
+               if (csr_read_varid_uint16(dd, 2, CSR_VARID_CHIPREV, &chiprev) < 0)
+                       chiprev = 0;
+               printf("\tChip version: %s\n", csr_chipvertostr(chipver, chiprev));
+       }
+
+       if (!csr_read_varid_uint16(dd, 3, CSR_VARID_MAX_CRYPT_KEY_LENGTH, &maxkeylen))
+               printf("\tMax key size: %d bit\n", maxkeylen * 8);
+
+       if (!csr_read_pskey_uint16(dd, 4, CSR_PSKEY_HOSTIO_MAP_SCO_PCM, 0x0000, &mapsco))
+               printf("\tSCO mapping:  %s\n", mapsco ? "PCM" : "HCI");
+}
+
+static void print_rev_digianswer(int dd)
+{
+       struct hci_request rq;
+       unsigned char req[] = { 0x07 };
+       unsigned char buf[102];
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_VENDOR_CMD;
+       rq.ocf    = 0x000e;
+       rq.cparam = req;
+       rq.clen   = sizeof(req);
+       rq.rparam = &buf;
+       rq.rlen   = sizeof(buf);
+
+       if (hci_send_req(dd, &rq, 1000) < 0) {
+               printf("\nCan't read revision info: %s (%d)\n",
+                       strerror(errno), errno);
+               return;
+       }
+
+       printf("\t%s\n", buf + 1);
+}
+
+static void print_rev_broadcom(uint16_t hci_rev, uint16_t lmp_subver)
+{
+       printf("\tFirmware %d.%d / %d\n",
+               hci_rev & 0xff, lmp_subver >> 8, lmp_subver & 0xff);
+}
+
+static void print_rev_avm(uint16_t hci_rev, uint16_t lmp_subver)
+{
+       if (lmp_subver == 0x01)
+               printf("\tFirmware 03.%d.%d\n", hci_rev >> 8, hci_rev & 0xff);
+       else
+               printf("\tUnknown type\n");
+}
+
+static void cmd_revision(int ctl, int hdev, char *opt)
+{
+       struct hci_version ver;
+       int dd;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               return;
+       }
+
+       if (hci_read_local_version(dd, &ver, 1000) < 0) {
+               fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               return;
+       }
+
+       print_dev_hdr(&di);
+       switch (ver.manufacturer) {
+       case 0:
+       case 37:
+       case 48:
+               print_rev_ericsson(dd);
+               break;
+       case 10:
+               print_rev_csr(dd, ver.hci_rev);
+               break;
+       case 12:
+               print_rev_digianswer(dd);
+               break;
+       case 15:
+               print_rev_broadcom(ver.hci_rev, ver.lmp_subver);
+               break;
+       case 31:
+               print_rev_avm(ver.hci_rev, ver.lmp_subver);
+               break;
+       default:
+               printf("\tUnsupported manufacturer\n");
+               break;
+       }
+       return;
+}
+
+static void cmd_block(int ctl, int hdev, char *opt)
+{
+       bdaddr_t bdaddr;
+       int dd;
+
+       if (!opt)
+               return;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       str2ba(opt, &bdaddr);
+
+       if (ioctl(dd, HCIBLOCKADDR, &bdaddr) < 0) {
+               perror("ioctl(HCIBLOCKADDR)");
+               exit(1);
+       }
+
+       hci_close_dev(dd);
+}
+
+static void cmd_unblock(int ctl, int hdev, char *opt)
+{
+       bdaddr_t bdaddr;
+       int dd;
+
+       if (!opt)
+               return;
+
+       dd = hci_open_dev(hdev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               hdev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (!strcasecmp(opt, "all"))
+               bacpy(&bdaddr, BDADDR_ANY);
+       else
+               str2ba(opt, &bdaddr);
+
+       if (ioctl(dd, HCIUNBLOCKADDR, &bdaddr) < 0) {
+               perror("ioctl(HCIUNBLOCKADDR)");
+               exit(1);
+       }
+
+       hci_close_dev(dd);
+}
+
+static void print_dev_hdr(struct hci_dev_info *di)
+{
+       static int hdr = -1;
+       char addr[18];
+
+       if (hdr == di->dev_id)
+               return;
+       hdr = di->dev_id;
+
+       ba2str(&di->bdaddr, addr);
+
+       printf("%s:\tType: %s  Bus: %s\n", di->name,
+                                       hci_typetostr(di->type >> 4),
+                                       hci_bustostr(di->type & 0x0f));
+       printf("\tBD Address: %s  ACL MTU: %d:%d  SCO MTU: %d:%d\n",
+                                       addr, di->acl_mtu, di->acl_pkts,
+                                               di->sco_mtu, di->sco_pkts);
+}
+
+static void print_dev_info(int ctl, struct hci_dev_info *di)
+{
+       struct hci_dev_stats *st = &di->stat;
+       char *str;
+
+       print_dev_hdr(di);
+
+       str = hci_dflagstostr(di->flags);
+       printf("\t%s\n", str);
+       bt_free(str);
+
+       printf("\tRX bytes:%d acl:%d sco:%d events:%d errors:%d\n",
+               st->byte_rx, st->acl_rx, st->sco_rx, st->evt_rx, st->err_rx);
+
+       printf("\tTX bytes:%d acl:%d sco:%d commands:%d errors:%d\n",
+               st->byte_tx, st->acl_tx, st->sco_tx, st->cmd_tx, st->err_tx);
+
+       if (all && !hci_test_bit(HCI_RAW, &di->flags) &&
+                       (bacmp(&di->bdaddr, BDADDR_ANY) || (di->type >> 4))) {
+               print_dev_features(di, 0);
+               print_pkt_type(di);
+               print_link_policy(di);
+               print_link_mode(di);
+
+               if (hci_test_bit(HCI_UP, &di->flags)) {
+                       cmd_name(ctl, di->dev_id, NULL);
+                       cmd_class(ctl, di->dev_id, NULL);
+                       cmd_version(ctl, di->dev_id, NULL);
+               }
+       }
+
+       printf("\n");
+}
+
+static struct {
+       char *cmd;
+       void (*func)(int ctl, int hdev, char *opt);
+       char *opt;
+       char *doc;
+} command[] = {
+       { "up",         cmd_up,         0,              "Open and initialize HCI device" },
+       { "down",       cmd_down,       0,              "Close HCI device" },
+       { "reset",      cmd_reset,      0,              "Reset HCI device" },
+       { "rstat",      cmd_rstat,      0,              "Reset statistic counters" },
+       { "auth",       cmd_auth,       0,              "Enable Authentication" },
+       { "noauth",     cmd_auth,       0,              "Disable Authentication" },
+       { "encrypt",    cmd_encrypt,    0,              "Enable Encryption" },
+       { "noencrypt",  cmd_encrypt,    0,              "Disable Encryption" },
+       { "piscan",     cmd_scan,       0,              "Enable Page and Inquiry scan" },
+       { "noscan",     cmd_scan,       0,              "Disable scan" },
+       { "iscan",      cmd_scan,       0,              "Enable Inquiry scan" },
+       { "pscan",      cmd_scan,       0,              "Enable Page scan" },
+       { "ptype",      cmd_ptype,      "[type]",       "Get/Set default packet type" },
+       { "lm",         cmd_lm,         "[mode]",       "Get/Set default link mode"   },
+       { "lp",         cmd_lp,         "[policy]",     "Get/Set default link policy" },
+       { "name",       cmd_name,       "[name]",       "Get/Set local name" },
+       { "class",      cmd_class,      "[class]",      "Get/Set class of device" },
+       { "voice",      cmd_voice,      "[voice]",      "Get/Set voice setting" },
+       { "iac",        cmd_iac,        "[iac]",        "Get/Set inquiry access code" },
+       { "inqtpl",     cmd_inq_tpl,    "[level]",      "Get/Set inquiry transmit power level" },
+       { "inqmode",    cmd_inq_mode,   "[mode]",       "Get/Set inquiry mode" },
+       { "inqdata",    cmd_inq_data,   "[data]",       "Get/Set inquiry data" },
+       { "inqtype",    cmd_inq_type,   "[type]",       "Get/Set inquiry scan type" },
+       { "inqparms",   cmd_inq_parms,  "[win:int]",    "Get/Set inquiry scan window and interval" },
+       { "pageparms",  cmd_page_parms, "[win:int]",    "Get/Set page scan window and interval" },
+       { "pageto",     cmd_page_to,    "[to]",         "Get/Set page timeout" },
+       { "afhmode",    cmd_afh_mode,   "[mode]",       "Get/Set AFH mode" },
+       { "sspmode",    cmd_ssp_mode,   "[mode]",       "Get/Set Simple Pairing Mode" },
+       { "aclmtu",     cmd_aclmtu,     "<mtu:pkt>",    "Set ACL MTU and number of packets" },
+       { "scomtu",     cmd_scomtu,     "<mtu:pkt>",    "Set SCO MTU and number of packets" },
+       { "putkey",     cmd_putkey,     "<bdaddr>",     "Store link key on the device" },
+       { "delkey",     cmd_delkey,     "<bdaddr>",     "Delete link key from the device" },
+       { "oobdata",    cmd_oob_data,   0,              "Display local OOB data" },
+       { "commands",   cmd_commands,   0,              "Display supported commands" },
+       { "features",   cmd_features,   0,              "Display device features" },
+       { "version",    cmd_version,    0,              "Display version information" },
+       { "revision",   cmd_revision,   0,              "Display revision information" },
+       { "block",      cmd_block,      "<bdaddr>",     "Add a device to the blacklist" },
+       { "unblock",    cmd_unblock,    "<bdaddr>",     "Remove a device from the blacklist" },
+       { "lerandaddr", cmd_le_addr,    "<bdaddr>",     "Set LE Random Address" },
+       { "leadv",      cmd_le_adv,     0,              "Enable LE advertising" },
+       { "noleadv",    cmd_le_adv,     0,              "Disable LE advertising" },
+       { "lestates",   cmd_le_states,  0,              "Display the supported LE states" },
+       { NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+       int i;
+
+       printf("hciconfig - HCI device configuration utility\n");
+       printf("Usage:\n"
+               "\thciconfig\n"
+               "\thciconfig [-a] hciX [command ...]\n");
+       printf("Commands:\n");
+       for (i = 0; command[i].cmd; i++)
+               printf("\t%-10s %-8s\t%s\n", command[i].cmd,
+               command[i].opt ? command[i].opt : " ",
+               command[i].doc);
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "all",        0, 0, 'a' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       int opt, ctl, i, cmd = 0;
+
+       while ((opt = getopt_long(argc, argv, "ah", main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'a':
+                       all = 1;
+                       break;
+
+               case 'h':
+               default:
+                       usage();
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       /* Open HCI socket  */
+       if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
+               perror("Can't open HCI socket.");
+               exit(1);
+       }
+
+       if (argc < 1) {
+               print_dev_list(ctl, 0);
+               exit(0);
+       }
+
+       di.dev_id = atoi(argv[0] + 3);
+       argc--; argv++;
+
+       if (ioctl(ctl, HCIGETDEVINFO, (void *) &di)) {
+               perror("Can't get device info");
+               exit(1);
+       }
+
+       if (hci_test_bit(HCI_RAW, &di.flags) &&
+                       !bacmp(&di.bdaddr, BDADDR_ANY)) {
+               int dd = hci_open_dev(di.dev_id);
+               hci_read_bd_addr(dd, &di.bdaddr, 1000);
+               hci_close_dev(dd);
+       }
+
+       while (argc > 0) {
+               for (i = 0; command[i].cmd; i++) {
+                       if (strncmp(command[i].cmd,
+                                       *argv, strlen(command[i].cmd)))
+                               continue;
+
+                       if (command[i].opt) {
+                               argc--; argv++;
+                       }
+
+                       command[i].func(ctl, di.dev_id, *argv);
+                       cmd = 1;
+                       break;
+               }
+
+               if (command[i].cmd == 0)
+                       fprintf(stderr, "Warning: unknown command - \"%s\"\n",
+                                       *argv);
+
+               argc--; argv++;
+       }
+
+       if (!cmd)
+               print_dev_info(ctl, &di);
+
+       close(ctl);
+       return 0;
+}
diff --git a/tools/hcieventmask.c b/tools/hcieventmask.c
new file mode 100644 (file)
index 0000000..87beac9
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+static struct option main_options[] = {
+       { "device",     1, 0, 'i' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       uint8_t events[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00 };
+       struct hci_dev_info di;
+       struct hci_version ver;
+       int dd, opt, dev = 0;
+
+       while ((opt=getopt_long(argc, argv, "+i:", main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'i':
+                       dev = hci_devid(optarg);
+                       if (dev < 0) {
+                               perror("Invalid device");
+                               exit(1);
+                       }
+                       break;
+               }
+       }
+
+       dd = hci_open_dev(dev);
+       if (dd < 0) {
+               fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               exit(1);
+       }
+
+       if (hci_devinfo(dev, &di) < 0) {
+               fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               hci_close_dev(dd);
+               exit(1);
+       }
+
+       if (hci_read_local_version(dd, &ver, 1000) < 0) {
+               fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
+                                               dev, strerror(errno), errno);
+               hci_close_dev(dd);
+               exit(1);
+       }
+
+       hci_close_dev(dd);
+
+       if (ver.hci_ver > 1) {
+               if (di.features[5] & LMP_SNIFF_SUBR)
+                       events[5] |= 0x20;
+
+               if (di.features[5] & LMP_PAUSE_ENC)
+                       events[5] |= 0x80;
+
+               if (di.features[6] & LMP_EXT_INQ)
+                       events[5] |= 0x40;
+
+               if (di.features[6] & LMP_NFLUSH_PKTS)
+                       events[7] |= 0x01;
+
+               if (di.features[7] & LMP_LSTO)
+                       events[6] |= 0x80;
+
+               if (di.features[6] & LMP_SIMPLE_PAIR) {
+                       events[6] |= 0x01;      /* IO Capability Request */
+                       events[6] |= 0x02;      /* IO Capability Response */
+                       events[6] |= 0x04;      /* User Confirmation Request */
+                       events[6] |= 0x08;      /* User Passkey Request */
+                       events[6] |= 0x10;      /* Remote OOB Data Request */
+                       events[6] |= 0x20;      /* Simple Pairing Complete */
+                       events[7] |= 0x04;      /* User Passkey Notification */
+                       events[7] |= 0x08;      /* Keypress Notification */
+                       events[7] |= 0x10;      /* Remote Host Supported
+                                                * Features Notification */
+               }
+
+               if (di.features[4] & LMP_LE)
+                       events[7] |= 0x20;
+
+               if (di.features[6] & LMP_LE_BREDR)
+                       events[7] |= 0x20;
+       }
+
+       printf("Setting event mask:\n");
+       printf("\thcitool cmd 0x%02x 0x%04x  "
+                                       "0x%02x 0x%02x 0x%02x 0x%02x "
+                                       "0x%02x 0x%02x 0x%02x 0x%02x\n",
+                               OGF_HOST_CTL, OCF_SET_EVENT_MASK,
+                               events[0], events[1], events[2], events[3],
+                               events[4], events[5], events[6], events[7]);
+
+       return 0;
+}
diff --git a/tools/hcisecfilter.c b/tools/hcisecfilter.c
new file mode 100644 (file)
index 0000000..9ad4ce0
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+int main(void)
+{
+       uint32_t type_mask;
+       uint32_t event_mask[2];
+       uint32_t ocf_mask[4];
+
+       /* Packet types */
+       memset(&type_mask, 0, sizeof(type_mask));
+       hci_set_bit(HCI_EVENT_PKT, &type_mask);
+
+       printf("Type mask:        { 0x%02x }\n", type_mask);
+
+       /* Events */
+       memset(event_mask, 0, sizeof(event_mask));
+       hci_set_bit(EVT_INQUIRY_COMPLETE,                       event_mask);
+       hci_set_bit(EVT_INQUIRY_RESULT,                         event_mask);
+       hci_set_bit(EVT_CONN_COMPLETE,                          event_mask);
+       hci_set_bit(EVT_CONN_REQUEST,                           event_mask);
+       hci_set_bit(EVT_DISCONN_COMPLETE,                       event_mask);
+       hci_set_bit(EVT_AUTH_COMPLETE,                          event_mask);
+       hci_set_bit(EVT_REMOTE_NAME_REQ_COMPLETE,               event_mask);
+       hci_set_bit(EVT_ENCRYPT_CHANGE,                         event_mask);
+       hci_set_bit(EVT_READ_REMOTE_FEATURES_COMPLETE,          event_mask);
+       hci_set_bit(EVT_READ_REMOTE_VERSION_COMPLETE,           event_mask);
+       hci_set_bit(EVT_CMD_COMPLETE,                           event_mask);
+       hci_set_bit(EVT_CMD_STATUS,                             event_mask);
+       hci_set_bit(EVT_READ_CLOCK_OFFSET_COMPLETE,             event_mask);
+       hci_set_bit(EVT_INQUIRY_RESULT_WITH_RSSI,               event_mask);
+       hci_set_bit(EVT_READ_REMOTE_EXT_FEATURES_COMPLETE,      event_mask);
+       hci_set_bit(EVT_SYNC_CONN_COMPLETE,                     event_mask);
+       hci_set_bit(EVT_SYNC_CONN_CHANGED,                      event_mask);
+       hci_set_bit(EVT_EXTENDED_INQUIRY_RESULT,                event_mask);
+
+       printf("Event mask:       { 0x%08x, 0x%08x }\n",
+                                       event_mask[0], event_mask[1]);
+
+       /* OGF_LINK_CTL */
+       memset(ocf_mask, 0, sizeof(ocf_mask));
+       hci_set_bit(OCF_INQUIRY,                        ocf_mask);
+       hci_set_bit(OCF_INQUIRY_CANCEL,                 ocf_mask);
+       hci_set_bit(OCF_REMOTE_NAME_REQ,                ocf_mask);
+       hci_set_bit(OCF_REMOTE_NAME_REQ_CANCEL,         ocf_mask);
+       hci_set_bit(OCF_READ_REMOTE_FEATURES,           ocf_mask);
+       hci_set_bit(OCF_READ_REMOTE_EXT_FEATURES,       ocf_mask);
+       hci_set_bit(OCF_READ_REMOTE_VERSION,            ocf_mask);
+       hci_set_bit(OCF_READ_CLOCK_OFFSET,              ocf_mask);
+       hci_set_bit(OCF_READ_LMP_HANDLE,                ocf_mask);
+
+       printf("OGF_LINK_CTL:     { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+                       ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+       /* OGF_LINK_POLICY */
+       memset(ocf_mask, 0, sizeof(ocf_mask));
+       hci_set_bit(OCF_ROLE_DISCOVERY,                 ocf_mask);
+       hci_set_bit(OCF_READ_LINK_POLICY,               ocf_mask);
+       hci_set_bit(OCF_READ_DEFAULT_LINK_POLICY,       ocf_mask);
+
+       printf("OGF_LINK_POLICY:  { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+                       ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+       /* OGF_HOST_CTL */
+       memset(ocf_mask, 0, sizeof(ocf_mask));
+       hci_set_bit(OCF_READ_PIN_TYPE,                  ocf_mask);
+       hci_set_bit(OCF_READ_LOCAL_NAME,                ocf_mask);
+       hci_set_bit(OCF_READ_CONN_ACCEPT_TIMEOUT,       ocf_mask);
+       hci_set_bit(OCF_READ_PAGE_TIMEOUT,              ocf_mask);
+       hci_set_bit(OCF_READ_SCAN_ENABLE,               ocf_mask);
+       hci_set_bit(OCF_READ_PAGE_ACTIVITY,             ocf_mask);
+       hci_set_bit(OCF_READ_INQ_ACTIVITY,              ocf_mask);
+       hci_set_bit(OCF_READ_AUTH_ENABLE,               ocf_mask);
+       hci_set_bit(OCF_READ_ENCRYPT_MODE,              ocf_mask);
+       hci_set_bit(OCF_READ_CLASS_OF_DEV,              ocf_mask);
+       hci_set_bit(OCF_READ_VOICE_SETTING,             ocf_mask);
+       hci_set_bit(OCF_READ_AUTOMATIC_FLUSH_TIMEOUT,   ocf_mask);
+       hci_set_bit(OCF_READ_NUM_BROADCAST_RETRANS,     ocf_mask);
+       hci_set_bit(OCF_READ_HOLD_MODE_ACTIVITY,        ocf_mask);
+       hci_set_bit(OCF_READ_TRANSMIT_POWER_LEVEL,      ocf_mask);
+       hci_set_bit(OCF_READ_LINK_SUPERVISION_TIMEOUT,  ocf_mask);
+       hci_set_bit(OCF_READ_NUM_SUPPORTED_IAC,         ocf_mask);
+       hci_set_bit(OCF_READ_CURRENT_IAC_LAP,           ocf_mask);
+       hci_set_bit(OCF_READ_PAGE_SCAN_PERIOD_MODE,     ocf_mask);
+       hci_set_bit(OCF_READ_PAGE_SCAN_MODE,            ocf_mask);
+       hci_set_bit(OCF_READ_INQUIRY_SCAN_TYPE,         ocf_mask);
+       hci_set_bit(OCF_READ_INQUIRY_MODE,              ocf_mask);
+       hci_set_bit(OCF_READ_PAGE_SCAN_TYPE,            ocf_mask);
+       hci_set_bit(OCF_READ_AFH_MODE,                  ocf_mask);
+       hci_set_bit(OCF_READ_EXT_INQUIRY_RESPONSE,      ocf_mask);
+       hci_set_bit(OCF_READ_SIMPLE_PAIRING_MODE,       ocf_mask);
+       hci_set_bit(OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL,       ocf_mask);
+       hci_set_bit(OCF_READ_DEFAULT_ERROR_DATA_REPORTING,      ocf_mask);
+
+       printf("OGF_HOST_CTL:     { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+                       ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+       /* OGF_INFO_PARAM */
+       memset(ocf_mask, 0, sizeof(ocf_mask));
+       hci_set_bit(OCF_READ_LOCAL_VERSION,             ocf_mask);
+       hci_set_bit(OCF_READ_LOCAL_COMMANDS,            ocf_mask);
+       hci_set_bit(OCF_READ_LOCAL_FEATURES,            ocf_mask);
+       hci_set_bit(OCF_READ_LOCAL_EXT_FEATURES,        ocf_mask);
+       hci_set_bit(OCF_READ_BUFFER_SIZE,               ocf_mask);
+       hci_set_bit(OCF_READ_COUNTRY_CODE,              ocf_mask);
+       hci_set_bit(OCF_READ_BD_ADDR,                   ocf_mask);
+
+       printf("OGF_INFO_PARAM:   { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+                       ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+       /* OGF_STATUS_PARAM */
+       memset(ocf_mask, 0, sizeof(ocf_mask));
+       hci_set_bit(OCF_READ_FAILED_CONTACT_COUNTER,    ocf_mask);
+       hci_set_bit(OCF_READ_LINK_QUALITY,              ocf_mask);
+       hci_set_bit(OCF_READ_RSSI,                      ocf_mask);
+       hci_set_bit(OCF_READ_AFH_MAP,                   ocf_mask);
+       hci_set_bit(OCF_READ_CLOCK,                     ocf_mask);
+
+       printf("OGF_STATUS_PARAM: { 0x%08x, 0x%08x, 0x%08x, 0x%02x }\n",
+                       ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+       return 0;
+}
diff --git a/tools/hcitool.1 b/tools/hcitool.1
new file mode 100644 (file)
index 0000000..85498dc
--- /dev/null
@@ -0,0 +1,209 @@
+.TH HCITOOL 1 "Nov 12 2002" BlueZ "Linux System Administration"
+.SH NAME
+hcitool \- configure Bluetooth connections
+.SH SYNOPSIS
+.B hcitool [-h]
+.br
+.B hcitool [-i <hciX>] [command [command parameters]]
+
+.SH DESCRIPTION
+.LP
+.B
+hcitool
+is used to configure Bluetooth connections and send some special command to
+Bluetooth devices. If no
+.B
+command
+is given, or if the option
+.B
+-h
+is used,
+.B
+hcitool
+prints some usage information and exits.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands
+.TP
+.BI -i " <hciX>"
+The command is applied to device
+.I
+hciX
+, which must be the name of an installed Bluetooth device. If not specified,
+the command will be sent to the first available Bluetooth device.
+.SH COMMANDS
+.TP
+.BI dev
+Display local devices
+.TP
+.BI inq
+Inquire remote devices. For each discovered device, Bluetooth device address,
+clock offset and class are printed.
+.TP
+.BI scan
+Inquire remote devices. For each discovered device, device name are printed.
+.TP
+.BI name " <bdaddr>"
+Print device name of remote device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI info " <bdaddr>"
+Print device name, version and supported features of remote device with
+Bluetooth address
+.IR bdaddr .
+.TP
+.BI spinq
+Start periodic inquiry process. No inquiry results are printed.
+.TP
+.BI epinq
+Exit periodic inquiry process.
+.TP
+.BI cmd " <ogf> <ocf> [parameters]"
+Submit an arbitrary HCI command to local device.
+.IR ogf ,
+.IR ocf
+and
+.IR parameters
+are hexadecimal bytes.
+.TP
+.BI con
+Display active baseband connections
+.TP
+.BI cc " [--role=m|s] [--pkt-type=<ptype>] <bdaddr>"
+Create baseband connection to remote device with Bluetooth address
+.IR bdaddr .
+Option
+.I
+--pkt-type
+specifies a list of allowed packet types.
+.I
+<ptype>
+is a comma-separated list of packet types, where the possible packet types are
+.BR DM1 ,
+.BR DM3 ,
+.BR DM5 ,
+.BR DH1 ,
+.BR DH3 ,
+.BR DH5 ,
+.BR HV1 ,
+.BR HV2 ,
+.BR HV3 .
+Default is to allow all packet types. Option
+.I
+--role
+can have value
+.I
+m
+(do not allow role switch, stay master) or
+.I
+s
+(allow role switch, become slave if the peer asks to become master). Default is
+.IR m .
+.TP
+.BI dc " <bdaddr> [reason]"
+Delete baseband connection from remote device with Bluetooth address
+.IR bdaddr .
+The reason can be one of the Bluetooth HCI error codes. Default is
+.IR 19
+for user ended connections. The value must be given in decimal.
+.TP
+.BI sr " <bdaddr> <role>"
+Switch role for the baseband connection from the remote device to
+.BR master
+or
+.BR slave .
+.TP
+.BI cpt " <bdaddr> <packet types>"
+Change packet types for baseband connection to device with Bluetooth address
+.IR bdaddr .
+.I
+packet types
+is a comma-separated list of packet types, where the possible packet types are
+.BR DM1 ,
+.BR DM3 ,
+.BR DM5 ,
+.BR DH1 ,
+.BR DH3 ,
+.BR DH5 ,
+.BR HV1 ,
+.BR HV2 ,
+.BR HV3 .
+.TP
+.BI rssi " <bdaddr>"
+Display received signal strength information for the connection to the device
+with Bluetooth address
+.IR bdaddr .
+.TP
+.BI lq " <bdaddr>"
+Display link quality for the connection to the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI tpl " <bdaddr> [type]"
+Display transmit power level for the connection to the device with Bluetooth address
+.IR bdaddr .
+The type can be
+.BR 0
+for the current transmit power level (which is default) or
+.BR 1
+for the maximum transmit power level.
+.TP
+.BI afh " <bdaddr>"
+Display AFH channel map for the connection to the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI lp " <bdaddr> [value]"
+With no
+.IR value ,
+displays link policy settings for the connection to the device with Bluetooth address
+.IR bdaddr .
+If
+.IR value
+is given, sets the link policy settings for that connection to
+.IR value .
+Possible values are RSWITCH, HOLD, SNIFF and PARK.
+.TP
+.BI lst " <bdaddr> [value]"
+With no
+.IR value ,
+displays link supervision timeout for the connection to the device with Bluetooth address
+.IR bdaddr .
+If
+.I
+value
+is given, sets the link supervision timeout for that connection to
+.I
+value
+slots, or to infinite if
+.I
+value
+is 0.
+.TP
+.BI auth " <bdaddr>"
+Request authentication for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI enc " <bdaddr> [encrypt enable]"
+Enable or disable the encryption for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI key " <bdaddr>"
+Change the connection link key for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI clkoff " <bdaddr>"
+Read the clock offset for the device with Bluetooth address
+.IR bdaddr .
+.TP
+.BI clock " [bdaddr] [which clock]"
+Read the clock for the device with Bluetooth address
+.IR bdaddr .
+The clock can be
+.BR 0
+for the local clock or
+.BR 1
+for the piconet clock (which is default).
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Fabrizio Gennari <fabrizio.gennari@philips.com>
diff --git a/tools/hcitool.c b/tools/hcitool.c
new file mode 100644 (file)
index 0000000..66e5c20
--- /dev/null
@@ -0,0 +1,3088 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <signal.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "textfile.h"
+#include "oui.h"
+
+/* Unofficial value, might still change */
+#define LE_LINK                0x03
+
+#define FLAGS_AD_TYPE 0x01
+#define FLAGS_LIMITED_MODE_BIT 0x01
+#define FLAGS_GENERAL_MODE_BIT 0x02
+
+#define EIR_FLAGS                   0x01  /* flags */
+#define EIR_UUID16_SOME             0x02  /* 16-bit UUID, more available */
+#define EIR_UUID16_ALL              0x03  /* 16-bit UUID, all listed */
+#define EIR_UUID32_SOME             0x04  /* 32-bit UUID, more available */
+#define EIR_UUID32_ALL              0x05  /* 32-bit UUID, all listed */
+#define EIR_UUID128_SOME            0x06  /* 128-bit UUID, more available */
+#define EIR_UUID128_ALL             0x07  /* 128-bit UUID, all listed */
+#define EIR_NAME_SHORT              0x08  /* shortened local name */
+#define EIR_NAME_COMPLETE           0x09  /* complete local name */
+#define EIR_TX_POWER                0x0A  /* transmit power level */
+#define EIR_DEVICE_ID               0x10  /* device ID */
+
+#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)
+
+static volatile int signal_received = 0;
+
+static void usage(void);
+
+static int dev_info(int s, int dev_id, long arg)
+{
+       struct hci_dev_info di = { .dev_id = dev_id };
+       char addr[18];
+
+       if (ioctl(s, HCIGETDEVINFO, (void *) &di))
+               return 0;
+
+       ba2str(&di.bdaddr, addr);
+       printf("\t%s\t%s\n", di.name, addr);
+       return 0;
+}
+
+static void helper_arg(int min_num_arg, int max_num_arg, int *argc,
+                       char ***argv, const char *usage)
+{
+       *argc -= optind;
+       /* too many arguments, but when "max_num_arg < min_num_arg" then no
+                limiting (prefer "max_num_arg=-1" to gen infinity)
+       */
+       if ( (*argc > max_num_arg) && (max_num_arg >= min_num_arg ) ) {
+               fprintf(stderr, "%s: too many arguments (maximal: %i)\n",
+                               *argv[0], max_num_arg);
+               printf("%s", usage);
+               exit(1);
+       }
+
+       /* print usage */
+       if (*argc < min_num_arg) {
+               fprintf(stderr, "%s: too few arguments (minimal: %i)\n",
+                               *argv[0], min_num_arg);
+               printf("%s", usage);
+               exit(0);
+       }
+
+       *argv += optind;
+}
+
+static char *type2str(uint8_t type)
+{
+       switch (type) {
+       case SCO_LINK:
+               return "SCO";
+       case ACL_LINK:
+               return "ACL";
+       case ESCO_LINK:
+               return "eSCO";
+       case LE_LINK:
+               return "LE";
+       default:
+               return "Unknown";
+       }
+}
+
+static int conn_list(int s, int dev_id, long arg)
+{
+       struct hci_conn_list_req *cl;
+       struct hci_conn_info *ci;
+       int id = arg;
+       int i;
+
+       if (id != -1 && dev_id != id)
+               return 0;
+
+       if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+       cl->dev_id = dev_id;
+       cl->conn_num = 10;
+       ci = cl->conn_info;
+
+       if (ioctl(s, HCIGETCONNLIST, (void *) cl)) {
+               perror("Can't get connection list");
+               exit(1);
+       }
+
+       for (i = 0; i < cl->conn_num; i++, ci++) {
+               char addr[18];
+               char *str;
+               ba2str(&ci->bdaddr, addr);
+               str = hci_lmtostr(ci->link_mode);
+               printf("\t%s %s %s handle %d state %d lm %s\n",
+                       ci->out ? "<" : ">", type2str(ci->type),
+                       addr, ci->handle, ci->state, str);
+               bt_free(str);
+       }
+
+       free(cl);
+       return 0;
+}
+
+static int find_conn(int s, int dev_id, long arg)
+{
+       struct hci_conn_list_req *cl;
+       struct hci_conn_info *ci;
+       int i;
+
+       if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+       cl->dev_id = dev_id;
+       cl->conn_num = 10;
+       ci = cl->conn_info;
+
+       if (ioctl(s, HCIGETCONNLIST, (void *) cl)) {
+               perror("Can't get connection list");
+               exit(1);
+       }
+
+       for (i = 0; i < cl->conn_num; i++, ci++)
+               if (!bacmp((bdaddr_t *) arg, &ci->bdaddr)) {
+                       free(cl);
+                       return 1;
+               }
+
+       free(cl);
+       return 0;
+}
+
+static void hex_dump(char *pref, int width, unsigned char *buf, int len)
+{
+       register int i,n;
+
+       for (i = 0, n = 1; i < len; i++, n++) {
+               if (n == 1)
+                       printf("%s", pref);
+               printf("%2.2X ", buf[i]);
+               if (n == width) {
+                       printf("\n");
+                       n = 0;
+               }
+       }
+       if (i && n!=1)
+               printf("\n");
+}
+
+static char *get_minor_device_name(int major, int minor)
+{
+       switch (major) {
+       case 0: /* misc */
+               return "";
+       case 1: /* computer */
+               switch (minor) {
+               case 0:
+                       return "Uncategorized";
+               case 1:
+                       return "Desktop workstation";
+               case 2:
+                       return "Server";
+               case 3:
+                       return "Laptop";
+               case 4:
+                       return "Handheld";
+               case 5:
+                       return "Palm";
+               case 6:
+                       return "Wearable";
+               }
+               break;
+       case 2: /* phone */
+               switch (minor) {
+               case 0:
+                       return "Uncategorized";
+               case 1:
+                       return "Cellular";
+               case 2:
+                       return "Cordless";
+               case 3:
+                       return "Smart phone";
+               case 4:
+                       return "Wired modem or voice gateway";
+               case 5:
+                       return "Common ISDN Access";
+               case 6:
+                       return "Sim Card Reader";
+               }
+               break;
+       case 3: /* lan access */
+               if (minor == 0)
+                       return "Uncategorized";
+               switch (minor / 8) {
+               case 0:
+                       return "Fully available";
+               case 1:
+                       return "1-17% utilized";
+               case 2:
+                       return "17-33% utilized";
+               case 3:
+                       return "33-50% utilized";
+               case 4:
+                       return "50-67% utilized";
+               case 5:
+                       return "67-83% utilized";
+               case 6:
+                       return "83-99% utilized";
+               case 7:
+                       return "No service available";
+               }
+               break;
+       case 4: /* audio/video */
+               switch (minor) {
+               case 0:
+                       return "Uncategorized";
+               case 1:
+                       return "Device conforms to the Headset profile";
+               case 2:
+                       return "Hands-free";
+                       /* 3 is reserved */
+               case 4:
+                       return "Microphone";
+               case 5:
+                       return "Loudspeaker";
+               case 6:
+                       return "Headphones";
+               case 7:
+                       return "Portable Audio";
+               case 8:
+                       return "Car Audio";
+               case 9:
+                       return "Set-top box";
+               case 10:
+                       return "HiFi Audio Device";
+               case 11:
+                       return "VCR";
+               case 12:
+                       return "Video Camera";
+               case 13:
+                       return "Camcorder";
+               case 14:
+                       return "Video Monitor";
+               case 15:
+                       return "Video Display and Loudspeaker";
+               case 16:
+                       return "Video Conferencing";
+                       /* 17 is reserved */
+               case 18:
+                       return "Gaming/Toy";
+               }
+               break;
+       case 5: /* peripheral */ {
+               static char cls_str[48]; cls_str[0] = 0;
+
+               switch (minor & 48) {
+               case 16:
+                       strncpy(cls_str, "Keyboard", sizeof(cls_str));
+                       break;
+               case 32:
+                       strncpy(cls_str, "Pointing device", sizeof(cls_str));
+                       break;
+               case 48:
+                       strncpy(cls_str, "Combo keyboard/pointing device", sizeof(cls_str));
+                       break;
+               }
+               if ((minor & 15) && (strlen(cls_str) > 0))
+                       strcat(cls_str, "/");
+
+               switch (minor & 15) {
+               case 0:
+                       break;
+               case 1:
+                       strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               case 2:
+                       strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               case 3:
+                       strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               case 4:
+                       strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               case 5:
+                       strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str));
+               break;
+               case 6:
+                       strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               default:
+                       strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str));
+                       break;
+               }
+               if (strlen(cls_str) > 0)
+                       return cls_str;
+       }
+       case 6: /* imaging */
+               if (minor & 4)
+                       return "Display";
+               if (minor & 8)
+                       return "Camera";
+               if (minor & 16)
+                       return "Scanner";
+               if (minor & 32)
+                       return "Printer";
+               break;
+       case 7: /* wearable */
+               switch (minor) {
+               case 1:
+                       return "Wrist Watch";
+               case 2:
+                       return "Pager";
+               case 3:
+                       return "Jacket";
+               case 4:
+                       return "Helmet";
+               case 5:
+                       return "Glasses";
+               }
+               break;
+       case 8: /* toy */
+               switch (minor) {
+               case 1:
+                       return "Robot";
+               case 2:
+                       return "Vehicle";
+               case 3:
+                       return "Doll / Action Figure";
+               case 4:
+                       return "Controller";
+               case 5:
+                       return "Game";
+               }
+               break;
+       case 63:        /* uncategorised */
+               return "";
+       }
+       return "Unknown (reserved) minor device class";
+}
+
+static char *major_classes[] = {
+       "Miscellaneous", "Computer", "Phone", "LAN Access",
+       "Audio/Video", "Peripheral", "Imaging", "Uncategorized"
+};
+
+static char *get_device_name(const bdaddr_t *local, const bdaddr_t *peer)
+{
+       char filename[PATH_MAX + 1], addr[18];
+
+       ba2str(local, addr);
+       create_name(filename, PATH_MAX, STORAGEDIR, addr, "names");
+
+       ba2str(peer, addr);
+       return textfile_get(filename, addr);
+}
+
+/* Display local devices */
+
+static struct option dev_options[] = {
+       { "help",       0, 0, 'h' },
+       {0, 0, 0, 0 }
+};
+
+static const char *dev_help =
+       "Usage:\n"
+       "\tdev\n";
+
+static void cmd_dev(int dev_id, int argc, char **argv)
+{
+       int opt;
+
+       for_each_opt(opt, dev_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", dev_help);
+                       return;
+               }
+       }
+       helper_arg(0, 0, &argc, &argv, dev_help);
+
+       printf("Devices:\n");
+
+       hci_for_each_dev(HCI_UP, dev_info, 0);
+}
+
+/* Inquiry */
+
+static struct option inq_options[] = {
+       { "help",       0, 0, 'h' },
+       { "length",     1, 0, 'l' },
+       { "numrsp",     1, 0, 'n' },
+       { "iac",        1, 0, 'i' },
+       { "flush",      0, 0, 'f' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *inq_help =
+       "Usage:\n"
+       "\tinq [--length=N] maximum inquiry duration in 1.28 s units\n"
+       "\t    [--numrsp=N] specify maximum number of inquiry responses\n"
+       "\t    [--iac=lap]  specify the inquiry access code\n"
+       "\t    [--flush]    flush the inquiry cache\n";
+
+static void cmd_inq(int dev_id, int argc, char **argv)
+{
+       inquiry_info *info = NULL;
+       uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+       int num_rsp, length, flags;
+       char addr[18];
+       int i, l, opt;
+
+       length  = 8;    /* ~10 seconds */
+       num_rsp = 0;
+       flags   = 0;
+
+       for_each_opt(opt, inq_options, NULL) {
+               switch (opt) {
+               case 'l':
+                       length = atoi(optarg);
+                       break;
+
+               case 'n':
+                       num_rsp = atoi(optarg);
+                       break;
+
+               case 'i':
+                       l = strtoul(optarg, 0, 16);
+                       if (!strcasecmp(optarg, "giac")) {
+                               l = 0x9e8b33;
+                       } else if (!strcasecmp(optarg, "liac")) {
+                               l = 0x9e8b00;
+                       } if (l < 0x9e8b00 || l > 0x9e8b3f) {
+                               printf("Invalid access code 0x%x\n", l);
+                               exit(1);
+                       }
+                       lap[0] = (l & 0xff);
+                       lap[1] = (l >> 8) & 0xff;
+                       lap[2] = (l >> 16) & 0xff;
+                       break;
+
+               case 'f':
+                       flags |= IREQ_CACHE_FLUSH;
+                       break;
+
+               default:
+                       printf("%s", inq_help);
+                       return;
+               }
+       }
+       helper_arg(0, 0, &argc, &argv, inq_help);
+
+       printf("Inquiring ...\n");
+
+       num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &info, flags);
+       if (num_rsp < 0) {
+               perror("Inquiry failed.");
+               exit(1);
+       }
+
+       for (i = 0; i < num_rsp; i++) {
+               ba2str(&(info+i)->bdaddr, addr);
+               printf("\t%s\tclock offset: 0x%4.4x\tclass: 0x%2.2x%2.2x%2.2x\n",
+                       addr, btohs((info+i)->clock_offset),
+                       (info+i)->dev_class[2],
+                       (info+i)->dev_class[1],
+                       (info+i)->dev_class[0]);
+       }
+
+       bt_free(info);
+}
+
+/* Device scanning */
+
+static struct option scan_options[] = {
+       { "help",       0, 0, 'h' },
+       { "length",     1, 0, 'l' },
+       { "numrsp",     1, 0, 'n' },
+       { "iac",        1, 0, 'i' },
+       { "flush",      0, 0, 'f' },
+       { "refresh",    0, 0, 'r' },
+       { "class",      0, 0, 'C' },
+       { "info",       0, 0, 'I' },
+       { "oui",        0, 0, 'O' },
+       { "all",        0, 0, 'A' },
+       { "ext",        0, 0, 'A' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *scan_help =
+       "Usage:\n"
+       "\tscan [--length=N] [--numrsp=N] [--iac=lap] [--flush] [--class] [--info] [--oui] [--refresh]\n";
+
+static void cmd_scan(int dev_id, int argc, char **argv)
+{
+       inquiry_info *info = NULL;
+       uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+       int num_rsp, length, flags;
+       uint8_t cls[3], features[8];
+       char addr[18], name[249], oui[9], *comp, *tmp;
+       struct hci_version version;
+       struct hci_dev_info di;
+       struct hci_conn_info_req *cr;
+       int refresh = 0, extcls = 0, extinf = 0, extoui = 0;
+       int i, n, l, opt, dd, cc, nc;
+
+       length  = 8;    /* ~10 seconds */
+       num_rsp = 0;
+       flags   = 0;
+
+       for_each_opt(opt, scan_options, NULL) {
+               switch (opt) {
+               case 'l':
+                       length = atoi(optarg);
+                       break;
+
+               case 'n':
+                       num_rsp = atoi(optarg);
+                       break;
+
+               case 'i':
+                       l = strtoul(optarg, 0, 16);
+                       if (!strcasecmp(optarg, "giac")) {
+                               l = 0x9e8b33;
+                       } else if (!strcasecmp(optarg, "liac")) {
+                               l = 0x9e8b00;
+                       } else if (l < 0x9e8b00 || l > 0x9e8b3f) {
+                               printf("Invalid access code 0x%x\n", l);
+                               exit(1);
+                       }
+                       lap[0] = (l & 0xff);
+                       lap[1] = (l >> 8) & 0xff;
+                       lap[2] = (l >> 16) & 0xff;
+                       break;
+
+               case 'f':
+                       flags |= IREQ_CACHE_FLUSH;
+                       break;
+
+               case 'r':
+                       refresh = 1;
+                       break;
+
+               case 'C':
+                       extcls = 1;
+                       break;
+
+               case 'I':
+                       extinf = 1;
+                       break;
+
+               case 'O':
+                       extoui = 1;
+                       break;
+
+               case 'A':
+                       extcls = 1;
+                       extinf = 1;
+                       extoui = 1;
+                       break;
+
+               default:
+                       printf("%s", scan_help);
+                       return;
+               }
+       }
+       helper_arg(0, 0, &argc, &argv, scan_help);
+
+       if (dev_id < 0) {
+               dev_id = hci_get_route(NULL);
+               if (dev_id < 0) {
+                       perror("Device is not available");
+                       exit(1);
+               }
+       }
+
+       if (hci_devinfo(dev_id, &di) < 0) {
+               perror("Can't get device info");
+               exit(1);
+       }
+
+       printf("Scanning ...\n");
+       num_rsp = hci_inquiry(dev_id, length, num_rsp, lap, &info, flags);
+       if (num_rsp < 0) {
+               perror("Inquiry failed");
+               exit(1);
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               free(info);
+               exit(1);
+       }
+
+       if (extcls || extinf || extoui)
+               printf("\n");
+
+       for (i = 0; i < num_rsp; i++) {
+               uint16_t handle = 0;
+
+               if (!refresh) {
+                       memset(name, 0, sizeof(name));
+                       tmp = get_device_name(&di.bdaddr, &(info+i)->bdaddr);
+                       if (tmp) {
+                               strncpy(name, tmp, 249);
+                               free(tmp);
+                               nc = 1;
+                       } else
+                               nc = 0;
+               } else
+                       nc = 0;
+
+               if (!extcls && !extinf && !extoui) {
+                       ba2str(&(info+i)->bdaddr, addr);
+
+                       if (nc) {
+                               printf("\t%s\t%s\n", addr, name);
+                               continue;
+                       }
+
+                       if (hci_read_remote_name_with_clock_offset(dd,
+                                       &(info+i)->bdaddr,
+                                       (info+i)->pscan_rep_mode,
+                                       (info+i)->clock_offset | 0x8000,
+                                       sizeof(name), name, 100000) < 0)
+                               strcpy(name, "n/a");
+
+                       for (n = 0; n < 248 && name[n]; n++) {
+                               if ((unsigned char) name[i] < 32 || name[i] == 127)
+                                       name[i] = '.';
+                       }
+
+                       name[248] = '\0';
+
+                       printf("\t%s\t%s\n", addr, name);
+                       continue;
+               }
+
+               ba2str(&(info+i)->bdaddr, addr);
+               printf("BD Address:\t%s [mode %d, clkoffset 0x%4.4x]\n", addr,
+                       (info+i)->pscan_rep_mode, btohs((info+i)->clock_offset));
+
+               if (extoui) {
+                       ba2oui(&(info+i)->bdaddr, oui);
+                       comp = ouitocomp(oui);
+                       if (comp) {
+                               printf("OUI company:\t%s (%s)\n", comp, oui);
+                               free(comp);
+                       }
+               }
+
+               cc = 0;
+
+               if (extinf) {
+                       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+                       if (cr) {
+                               bacpy(&cr->bdaddr, &(info+i)->bdaddr);
+                               cr->type = ACL_LINK;
+                               if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+                                       handle = 0;
+                                       cc = 1;
+                               } else {
+                                       handle = htobs(cr->conn_info->handle);
+                                       cc = 0;
+                               }
+                               free(cr);
+                       }
+
+                       if (cc) {
+                               if (hci_create_connection(dd, &(info+i)->bdaddr,
+                                               htobs(di.pkt_type & ACL_PTYPE_MASK),
+                                               (info+i)->clock_offset | 0x8000,
+                                               0x01, &handle, 25000) < 0) {
+                                       handle = 0;
+                                       cc = 0;
+                               }
+                       }
+               }
+
+               if (handle > 0 || !nc) {
+                       if (hci_read_remote_name_with_clock_offset(dd,
+                                       &(info+i)->bdaddr,
+                                       (info+i)->pscan_rep_mode,
+                                       (info+i)->clock_offset | 0x8000,
+                                       sizeof(name), name, 100000) < 0) {
+                               if (!nc)
+                                       strcpy(name, "n/a");
+                       } else {
+                               for (n = 0; n < 248 && name[n]; n++) {
+                                       if ((unsigned char) name[i] < 32 || name[i] == 127)
+                                               name[i] = '.';
+                               }
+
+                               name[248] = '\0';
+                               nc = 0;
+                       }
+               }
+
+               if (strlen(name) > 0)
+                       printf("Device name:\t%s%s\n", name, nc ? " [cached]" : "");
+
+               if (extcls) {
+                       memcpy(cls, (info+i)->dev_class, 3);
+                       printf("Device class:\t");
+                       if ((cls[1] & 0x1f) > sizeof(major_classes) / sizeof(char *))
+                               printf("Invalid");
+                       else
+                               printf("%s, %s", major_classes[cls[1] & 0x1f],
+                                       get_minor_device_name(cls[1] & 0x1f, cls[0] >> 2));
+                       printf(" (0x%2.2x%2.2x%2.2x)\n", cls[2], cls[1], cls[0]);
+               }
+
+               if (extinf && handle > 0) {
+                       if (hci_read_remote_version(dd, handle, &version, 20000) == 0) {
+                               char *ver = lmp_vertostr(version.lmp_ver);
+                               printf("Manufacturer:\t%s (%d)\n",
+                                       bt_compidtostr(version.manufacturer),
+                                       version.manufacturer);
+                               printf("LMP version:\t%s (0x%x) [subver 0x%x]\n",
+                                       ver ? ver : "n/a",
+                                       version.lmp_ver, version.lmp_subver);
+                               if (ver)
+                                       bt_free(ver);
+                       }
+
+                       if (hci_read_remote_features(dd, handle, features, 20000) == 0) {
+                               char *tmp = lmp_featurestostr(features, "\t\t", 63);
+                               printf("LMP features:\t0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x"
+                                       " 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+                                       features[0], features[1],
+                                       features[2], features[3],
+                                       features[4], features[5],
+                                       features[6], features[7]);
+                               printf("%s\n", tmp);
+                               bt_free(tmp);
+                       }
+
+                       if (cc) {
+                               usleep(10000);
+                               hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
+                       }
+               }
+
+               printf("\n");
+       }
+
+       bt_free(info);
+
+       hci_close_dev(dd);
+}
+
+/* Remote name */
+
+static struct option name_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *name_help =
+       "Usage:\n"
+       "\tname <bdaddr>\n";
+
+static void cmd_name(int dev_id, int argc, char **argv)
+{
+       bdaddr_t bdaddr;
+       char name[248];
+       int opt, dd;
+
+       for_each_opt(opt, name_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", name_help);
+                       return;
+               }
+       }
+       helper_arg(1, 1, &argc, &argv, name_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_get_route(&bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Device is not available.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0)
+               printf("%s\n", name);
+
+       hci_close_dev(dd);
+}
+
+/* Info about remote device */
+
+static struct option info_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *info_help =
+       "Usage:\n"
+       "\tinfo <bdaddr>\n";
+
+static void cmd_info(int dev_id, int argc, char **argv)
+{
+       bdaddr_t bdaddr;
+       uint16_t handle;
+       uint8_t features[8], max_page = 0;
+       char name[249], oui[9], *comp, *tmp;
+       struct hci_version version;
+       struct hci_dev_info di;
+       struct hci_conn_info_req *cr;
+       int i, opt, dd, cc = 0;
+
+       for_each_opt(opt, info_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", info_help);
+                       return;
+               }
+       }
+       helper_arg(1, 1, &argc, &argv, info_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0)
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(&bdaddr);
+
+       if (dev_id < 0) {
+               fprintf(stderr, "Device is not available or not connected.\n");
+               exit(1);
+       }
+
+       if (hci_devinfo(dev_id, &di) < 0) {
+               perror("Can't get device info");
+               exit(1);
+       }
+
+       printf("Requesting information ...\n");
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't get connection info");
+               close(dd);
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               if (hci_create_connection(dd, &bdaddr,
+                                       htobs(di.pkt_type & ACL_PTYPE_MASK),
+                                       0, 0x01, &handle, 25000) < 0) {
+                       perror("Can't create connection");
+                       close(dd);
+                       exit(1);
+               }
+               sleep(1);
+               cc = 1;
+       } else
+               handle = htobs(cr->conn_info->handle);
+
+       printf("\tBD Address:  %s\n", argv[0]);
+
+       ba2oui(&bdaddr, oui);
+       comp = ouitocomp(oui);
+       if (comp) {
+               printf("\tOUI Company: %s (%s)\n", comp, oui);
+               free(comp);
+       }
+
+       if (hci_read_remote_name(dd, &bdaddr, sizeof(name), name, 25000) == 0)
+               printf("\tDevice Name: %s\n", name);
+
+       if (hci_read_remote_version(dd, handle, &version, 20000) == 0) {
+               char *ver = lmp_vertostr(version.lmp_ver);
+               printf("\tLMP Version: %s (0x%x) LMP Subversion: 0x%x\n"
+                       "\tManufacturer: %s (%d)\n",
+                       ver ? ver : "n/a",
+                       version.lmp_ver,
+                       version.lmp_subver,
+                       bt_compidtostr(version.manufacturer),
+                       version.manufacturer);
+               if (ver)
+                       bt_free(ver);
+       }
+
+       memset(features, 0, sizeof(features));
+       hci_read_remote_features(dd, handle, features, 20000);
+
+       if ((di.features[7] & LMP_EXT_FEAT) && (features[7] & LMP_EXT_FEAT))
+               hci_read_remote_ext_features(dd, handle, 0, &max_page,
+                                                       features, 20000);
+
+       printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                               "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+               (max_page > 0) ? " page 0" : "",
+               features[0], features[1], features[2], features[3],
+               features[4], features[5], features[6], features[7]);
+
+       tmp = lmp_featurestostr(features, "\t\t", 63);
+       printf("%s\n", tmp);
+       bt_free(tmp);
+
+       for (i = 1; i <= max_page; i++) {
+               if (hci_read_remote_ext_features(dd, handle, i, NULL,
+                                                       features, 20000) < 0)
+                       continue;
+
+               printf("\tFeatures page %d: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                                       "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", i,
+                       features[0], features[1], features[2], features[3],
+                       features[4], features[5], features[6], features[7]);
+       }
+
+       if (cc) {
+               usleep(10000);
+               hci_disconnect(dd, handle, HCI_OE_USER_ENDED_CONNECTION, 10000);
+       }
+
+       hci_close_dev(dd);
+}
+
+/* Start periodic inquiry */
+
+static struct option spinq_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *spinq_help =
+       "Usage:\n"
+       "\tspinq\n";
+
+static void cmd_spinq(int dev_id, int argc, char **argv)
+{
+       uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
+       struct hci_request rq;
+       periodic_inquiry_cp cp;
+       int opt, dd;
+
+       for_each_opt(opt, spinq_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", spinq_help);
+                       return;
+               }
+       }
+       helper_arg(0, 0, &argc, &argv, spinq_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Device open failed");
+               exit(EXIT_FAILURE);
+       }
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(cp.lap, lap, 3);
+       cp.max_period = htobs(16);
+       cp.min_period = htobs(10);
+       cp.length     = 8;
+       cp.num_rsp    = 0;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_PERIODIC_INQUIRY;
+       rq.cparam = &cp;
+       rq.clen   = PERIODIC_INQUIRY_CP_SIZE;
+
+       if (hci_send_req(dd, &rq, 100) < 0) {
+               perror("Periodic inquiry failed");
+               exit(EXIT_FAILURE);
+       }
+
+       hci_close_dev(dd);
+}
+
+/* Exit periodic inquiry */
+
+static struct option epinq_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *epinq_help =
+       "Usage:\n"
+       "\tepinq\n";
+
+static void cmd_epinq(int dev_id, int argc, char **argv)
+{
+       int opt, dd;
+
+       for_each_opt(opt, epinq_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", epinq_help);
+                       return;
+               }
+       }
+       helper_arg(0, 0, &argc, &argv, epinq_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Device open failed");
+               exit(EXIT_FAILURE);
+       }
+
+       if (hci_send_cmd(dd, OGF_LINK_CTL,
+                               OCF_EXIT_PERIODIC_INQUIRY, 0, NULL) < 0) {
+               perror("Exit periodic inquiry failed");
+               exit(EXIT_FAILURE);
+       }
+
+       hci_close_dev(dd);
+}
+
+/* Send arbitrary HCI commands */
+
+static struct option cmd_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *cmd_help =
+       "Usage:\n"
+       "\tcmd <ogf> <ocf> [parameters]\n"
+       "Example:\n"
+       "\tcmd 0x03 0x0013 0x41 0x42 0x43 0x44\n";
+
+static void cmd_cmd(int dev_id, int argc, char **argv)
+{
+       unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr = buf;
+       struct hci_filter flt;
+       hci_event_hdr *hdr;
+       int i, opt, len, dd;
+       uint16_t ocf;
+       uint8_t ogf;
+
+       for_each_opt(opt, cmd_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", cmd_help);
+                       return;
+               }
+       }
+       helper_arg(2, -1, &argc, &argv, cmd_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       errno = 0;
+       ogf = strtol(argv[0], NULL, 16);
+       ocf = strtol(argv[1], NULL, 16);
+       if (errno == ERANGE || (ogf > 0x3f) || (ocf > 0x3ff)) {
+               printf("%s", cmd_help);
+               return;
+       }
+
+       for (i = 2, len = 0; i < argc && len < (int) sizeof(buf); i++, len++)
+               *ptr++ = (uint8_t) strtol(argv[i], NULL, 16);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Device open failed");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Setup filter */
+       hci_filter_clear(&flt);
+       hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+       hci_filter_all_events(&flt);
+       if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+               perror("HCI filter setup failed");
+               exit(EXIT_FAILURE);
+       }
+
+       printf("< HCI Command: ogf 0x%02x, ocf 0x%04x, plen %d\n", ogf, ocf, len);
+       hex_dump("  ", 20, buf, len); fflush(stdout);
+
+       if (hci_send_cmd(dd, ogf, ocf, len, buf) < 0) {
+               perror("Send failed");
+               exit(EXIT_FAILURE);
+       }
+
+       len = read(dd, buf, sizeof(buf));
+       if (len < 0) {
+               perror("Read failed");
+               exit(EXIT_FAILURE);
+       }
+
+       hdr = (void *)(buf + 1);
+       ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+       len -= (1 + HCI_EVENT_HDR_SIZE);
+
+       printf("> HCI Event: 0x%02x plen %d\n", hdr->evt, hdr->plen);
+       hex_dump("  ", 20, ptr, len); fflush(stdout);
+
+       hci_close_dev(dd);
+}
+
+/* Display active connections */
+
+static struct option con_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *con_help =
+       "Usage:\n"
+       "\tcon\n";
+
+static void cmd_con(int dev_id, int argc, char **argv)
+{
+       int opt;
+
+       for_each_opt(opt, con_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", con_help);
+                       return;
+               }
+       }
+       helper_arg(0, 0, &argc, &argv, con_help);
+
+       printf("Connections:\n");
+
+       hci_for_each_dev(HCI_UP, conn_list, dev_id);
+}
+
+/* Create connection */
+
+static struct option cc_options[] = {
+       { "help",       0, 0, 'h' },
+       { "role",       1, 0, 'r' },
+       { "ptype",      1, 0, 'p' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *cc_help =
+       "Usage:\n"
+       "\tcc [--role=m|s] [--ptype=pkt_types] <bdaddr>\n"
+       "Example:\n"
+       "\tcc --ptype=dm1,dh3,dh5 01:02:03:04:05:06\n"
+       "\tcc --role=m 01:02:03:04:05:06\n";
+
+static void cmd_cc(int dev_id, int argc, char **argv)
+{
+       bdaddr_t bdaddr;
+       uint16_t handle;
+       uint8_t role;
+       unsigned int ptype;
+       int dd, opt;
+
+       role = 0x01;
+       ptype = HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5;
+
+       for_each_opt(opt, cc_options, NULL) {
+               switch (opt) {
+               case 'p':
+                       hci_strtoptype(optarg, &ptype);
+                       break;
+
+               case 'r':
+                       role = optarg[0] == 'm' ? 0 : 1;
+                       break;
+
+               default:
+                       printf("%s", cc_help);
+                       return;
+               }
+       }
+       helper_arg(1, 1, &argc, &argv, cc_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_get_route(&bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Device is not available.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       if (hci_create_connection(dd, &bdaddr, htobs(ptype),
+                               htobs(0x0000), role, &handle, 25000) < 0)
+               perror("Can't create connection");
+
+       hci_close_dev(dd);
+}
+
+/* Close connection */
+
+static struct option dc_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *dc_help =
+       "Usage:\n"
+       "\tdc <bdaddr> [reason]\n";
+
+static void cmd_dc(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       uint8_t reason;
+       int opt, dd;
+
+       for_each_opt(opt, dc_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", dc_help);
+                       return;
+               }
+       }
+       helper_arg(1, 2, &argc, &argv, dc_help);
+
+       str2ba(argv[0], &bdaddr);
+       reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION;
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       if (hci_disconnect(dd, htobs(cr->conn_info->handle),
+                                               reason, 10000) < 0)
+               perror("Disconnect failed");
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Role switch */
+
+static struct option sr_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *sr_help =
+       "Usage:\n"
+       "\tsr <bdaddr> <role>\n";
+
+static void cmd_sr(int dev_id, int argc, char **argv)
+{
+       bdaddr_t bdaddr;
+       uint8_t role;
+       int opt, dd;
+
+       for_each_opt(opt, sr_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", sr_help);
+                       return;
+               }
+       }
+       helper_arg(2, 2, &argc, &argv, sr_help);
+
+       str2ba(argv[0], &bdaddr);
+       switch (argv[1][0]) {
+       case 'm':
+               role = 0;
+               break;
+       case 's':
+               role = 1;
+               break;
+       default:
+               role = atoi(argv[1]);
+               break;
+       }
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       if (hci_switch_role(dd, &bdaddr, role, 10000) < 0) {
+               perror("Switch role request failed");
+               exit(1);
+       }
+
+       hci_close_dev(dd);
+}
+
+/* Read RSSI */
+
+static struct option rssi_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *rssi_help =
+       "Usage:\n"
+       "\trssi <bdaddr>\n";
+
+static void cmd_rssi(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       int8_t rssi;
+       int opt, dd;
+
+       for_each_opt(opt, rssi_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", rssi_help);
+                       return;
+               }
+       }
+       helper_arg(1, 1, &argc, &argv, rssi_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       if (hci_read_rssi(dd, htobs(cr->conn_info->handle), &rssi, 1000) < 0) {
+               perror("Read RSSI failed");
+               exit(1);
+       }
+
+       printf("RSSI return value: %d\n", rssi);
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Get link quality */
+
+static struct option lq_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *lq_help =
+       "Usage:\n"
+       "\tlq <bdaddr>\n";
+
+static void cmd_lq(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       uint8_t lq;
+       int opt, dd;
+
+       for_each_opt(opt, lq_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", lq_help);
+                       return;
+               }
+       }
+       helper_arg(1, 1, &argc, &argv, lq_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       if (hci_read_link_quality(dd, htobs(cr->conn_info->handle), &lq, 1000) < 0) {
+               perror("HCI read_link_quality request failed");
+               exit(1);
+       }
+
+       printf("Link quality: %d\n", lq);
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Get transmit power level */
+
+static struct option tpl_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *tpl_help =
+       "Usage:\n"
+       "\ttpl <bdaddr> [type]\n";
+
+static void cmd_tpl(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       uint8_t type;
+       int8_t level;
+       int opt, dd;
+
+       for_each_opt(opt, tpl_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", tpl_help);
+                       return;
+               }
+       }
+       helper_arg(1, 2, &argc, &argv, tpl_help);
+
+       str2ba(argv[0], &bdaddr);
+       type = (argc > 1) ? atoi(argv[1]) : 0;
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       if (hci_read_transmit_power_level(dd, htobs(cr->conn_info->handle), type, &level, 1000) < 0) {
+               perror("HCI read transmit power level request failed");
+               exit(1);
+       }
+
+       printf("%s transmit power level: %d\n",
+               (type == 0) ? "Current" : "Maximum", level);
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Get AFH channel map */
+
+static struct option afh_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *afh_help =
+       "Usage:\n"
+       "\tafh <bdaddr>\n";
+
+static void cmd_afh(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       uint16_t handle;
+       uint8_t mode, map[10];
+       int opt, dd;
+
+       for_each_opt(opt, afh_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", afh_help);
+                       return;
+               }
+       }
+       helper_arg(1, 1, &argc, &argv, afh_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       handle = htobs(cr->conn_info->handle);
+
+       if (hci_read_afh_map(dd, handle, &mode, map, 1000) < 0) {
+               perror("HCI read AFH map request failed");
+               exit(1);
+       }
+
+       if (mode == 0x01) {
+               int i;
+               printf("AFH map: 0x");
+               for (i = 0; i < 10; i++)
+                       printf("%02x", map[i]);
+               printf("\n");
+       } else
+               printf("AFH disabled\n");
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Set connection packet type */
+
+static struct option cpt_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *cpt_help =
+       "Usage:\n"
+       "\tcpt <bdaddr> <packet_types>\n";
+
+static void cmd_cpt(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       struct hci_request rq;
+       set_conn_ptype_cp cp;
+       evt_conn_ptype_changed rp;
+       bdaddr_t bdaddr;
+       unsigned int ptype;
+       int dd, opt;
+
+       for_each_opt(opt, cpt_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", cpt_help);
+                       return;
+               }
+       }
+       helper_arg(2, 2, &argc, &argv, cpt_help);
+
+       str2ba(argv[0], &bdaddr);
+       hci_strtoptype(argv[1], &ptype);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       cp.handle   = htobs(cr->conn_info->handle);
+       cp.pkt_type = ptype;
+
+       memset(&rq, 0, sizeof(rq));
+       rq.ogf    = OGF_LINK_CTL;
+       rq.ocf    = OCF_SET_CONN_PTYPE;
+       rq.cparam = &cp;
+       rq.clen   = SET_CONN_PTYPE_CP_SIZE;
+       rq.rparam = &rp;
+       rq.rlen   = EVT_CONN_PTYPE_CHANGED_SIZE;
+       rq.event  = EVT_CONN_PTYPE_CHANGED;
+
+       if (hci_send_req(dd, &rq, 100) < 0) {
+               perror("Packet type change failed");
+               exit(1);
+       }
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Get/Set link policy settings */
+
+static struct option lp_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *lp_help =
+       "Usage:\n"
+       "\tlp <bdaddr> [link policy]\n";
+
+static void cmd_lp(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       uint16_t policy;
+       int opt, dd;
+
+       for_each_opt(opt, lp_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", lp_help);
+                       return;
+               }
+       }
+       helper_arg(1, 2, &argc, &argv, lp_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       if (argc == 1) {
+               char *str;
+               if (hci_read_link_policy(dd, htobs(cr->conn_info->handle),
+                                                       &policy, 1000) < 0) {
+                       perror("HCI read_link_policy_settings request failed");
+                       exit(1);
+               }
+
+               policy = btohs(policy);
+               str = hci_lptostr(policy);
+               if (str) {
+                       printf("Link policy settings: %s\n", str);
+                       bt_free(str);
+               } else {
+                       fprintf(stderr, "Invalig settings\n");
+                       exit(1);
+               }
+       } else {
+               unsigned int val;
+               if (hci_strtolp(argv[1], &val) < 0) {
+                       fprintf(stderr, "Invalig arguments\n");
+                       exit(1);
+               }
+               policy = val;
+
+               if (hci_write_link_policy(dd, htobs(cr->conn_info->handle),
+                                               htobs(policy), 1000) < 0) {
+                       perror("HCI write_link_policy_settings request failed");
+                       exit(1);
+               }
+       }
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Get/Set link supervision timeout */
+
+static struct option lst_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *lst_help =
+       "Usage:\n"
+       "\tlst <bdaddr> [new value in slots]\n";
+
+static void cmd_lst(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       uint16_t timeout;
+       int opt, dd;
+
+       for_each_opt(opt, lst_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", lst_help);
+                       return;
+               }
+       }
+       helper_arg(1, 2, &argc, &argv, lst_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       if (argc == 1) {
+               if (hci_read_link_supervision_timeout(dd, htobs(cr->conn_info->handle),
+                                                       &timeout, 1000) < 0) {
+                       perror("HCI read_link_supervision_timeout request failed");
+                       exit(1);
+               }
+
+               timeout = btohs(timeout);
+
+               if (timeout)
+                       printf("Link supervision timeout: %u slots (%.2f msec)\n",
+                               timeout, (float) timeout * 0.625);
+               else
+                       printf("Link supervision timeout never expires\n");
+       } else {
+               timeout = strtol(argv[1], NULL, 10);
+
+               if (hci_write_link_supervision_timeout(dd, htobs(cr->conn_info->handle),
+                                                       htobs(timeout), 1000) < 0) {
+                       perror("HCI write_link_supervision_timeout request failed");
+                       exit(1);
+               }
+       }
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Request authentication */
+
+static struct option auth_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *auth_help =
+       "Usage:\n"
+       "\tauth <bdaddr>\n";
+
+static void cmd_auth(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       int opt, dd;
+
+       for_each_opt(opt, auth_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", auth_help);
+                       return;
+               }
+       }
+       helper_arg(1, 1, &argc, &argv, auth_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       if (hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000) < 0) {
+               perror("HCI authentication request failed");
+               exit(1);
+       }
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Activate encryption */
+
+static struct option enc_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *enc_help =
+       "Usage:\n"
+       "\tenc <bdaddr> [encrypt enable]\n";
+
+static void cmd_enc(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       uint8_t encrypt;
+       int opt, dd;
+
+       for_each_opt(opt, enc_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", enc_help);
+                       return;
+               }
+       }
+       helper_arg(1, 2, &argc, &argv, enc_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       encrypt = (argc > 1) ? atoi(argv[1]) : 1;
+
+       if (hci_encrypt_link(dd, htobs(cr->conn_info->handle), encrypt, 25000) < 0) {
+               perror("HCI set encryption request failed");
+               exit(1);
+       }
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Change connection link key */
+
+static struct option key_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *key_help =
+       "Usage:\n"
+       "\tkey <bdaddr>\n";
+
+static void cmd_key(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       int opt, dd;
+
+       for_each_opt(opt, key_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", key_help);
+                       return;
+               }
+       }
+       helper_arg(1, 1, &argc, &argv, key_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       if (hci_change_link_key(dd, htobs(cr->conn_info->handle), 25000) < 0) {
+               perror("Changing link key failed");
+               exit(1);
+       }
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Read clock offset */
+
+static struct option clkoff_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *clkoff_help =
+       "Usage:\n"
+       "\tclkoff <bdaddr>\n";
+
+static void cmd_clkoff(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       uint16_t offset;
+       int opt, dd;
+
+       for_each_opt(opt, clkoff_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", clkoff_help);
+                       return;
+               }
+       }
+       helper_arg(1, 1, &argc, &argv, clkoff_help);
+
+       str2ba(argv[0], &bdaddr);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+       if (!cr) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       bacpy(&cr->bdaddr, &bdaddr);
+       cr->type = ACL_LINK;
+       if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+               perror("Get connection info failed");
+               exit(1);
+       }
+
+       if (hci_read_clock_offset(dd, htobs(cr->conn_info->handle), &offset, 1000) < 0) {
+               perror("Reading clock offset failed");
+               exit(1);
+       }
+
+       printf("Clock offset: 0x%4.4x\n", btohs(offset));
+
+       free(cr);
+
+       hci_close_dev(dd);
+}
+
+/* Read clock */
+
+static struct option clock_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *clock_help =
+       "Usage:\n"
+       "\tclock [bdaddr] [which clock]\n";
+
+static void cmd_clock(int dev_id, int argc, char **argv)
+{
+       struct hci_conn_info_req *cr;
+       bdaddr_t bdaddr;
+       uint8_t which;
+       uint32_t handle, clock;
+       uint16_t accuracy;
+       int opt, dd;
+
+       for_each_opt(opt, clock_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", clock_help);
+                       return;
+               }
+       }
+       helper_arg(0, 2, &argc, &argv, clock_help);
+
+       if (argc > 0)
+               str2ba(argv[0], &bdaddr);
+       else
+               bacpy(&bdaddr, BDADDR_ANY);
+
+       if (dev_id < 0 && !bacmp(&bdaddr, BDADDR_ANY))
+               dev_id = hci_get_route(NULL);
+
+       if (dev_id < 0) {
+               dev_id = hci_for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+               if (dev_id < 0) {
+                       fprintf(stderr, "Not connected.\n");
+                       exit(1);
+               }
+       }
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("HCI device open failed");
+               exit(1);
+       }
+
+       if (bacmp(&bdaddr, BDADDR_ANY)) {
+               cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+               if (!cr) {
+                       perror("Can't allocate memory");
+                       exit(1);
+               }
+
+               bacpy(&cr->bdaddr, &bdaddr);
+               cr->type = ACL_LINK;
+               if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+                       perror("Get connection info failed");
+                       free(cr);
+                       exit(1);
+               }
+
+               handle = htobs(cr->conn_info->handle);
+               which = (argc > 1) ? atoi(argv[1]) : 0x01;
+
+               free(cr);
+       } else {
+               handle = 0x00;
+               which = 0x00;
+       }
+
+       if (hci_read_clock(dd, handle, which, &clock, &accuracy, 1000) < 0) {
+               perror("Reading clock failed");
+               exit(1);
+       }
+
+       accuracy = btohs(accuracy);
+
+       printf("Clock:    0x%4.4x\n", btohl(clock));
+       printf("Accuracy: %.2f msec\n", (float) accuracy * 0.3125);
+
+       hci_close_dev(dd);
+}
+
+static int read_flags(uint8_t *flags, const uint8_t *data, size_t size)
+{
+       size_t offset;
+
+       if (!flags || !data)
+               return -EINVAL;
+
+       offset = 0;
+       while (offset < size) {
+               uint8_t len = data[offset];
+               uint8_t type;
+
+               /* Check if it is the end of the significant part */
+               if (len == 0)
+                       break;
+
+               if (len + offset > size)
+                       break;
+
+               type = data[offset + 1];
+
+               if (type == FLAGS_AD_TYPE) {
+                       *flags = data[offset + 2];
+                       return 0;
+               }
+
+               offset += 1 + len;
+       }
+
+       return -ENOENT;
+}
+
+static int check_report_filter(uint8_t procedure, le_advertising_info *info)
+{
+       uint8_t flags;
+
+       /* If no discovery procedure is set, all reports are treat as valid */
+       if (procedure == 0)
+               return 1;
+
+       /* Read flags AD type value from the advertising report if it exists */
+       if (read_flags(&flags, info->data, info->length))
+               return 0;
+
+       switch (procedure) {
+       case 'l': /* Limited Discovery Procedure */
+               if (flags & FLAGS_LIMITED_MODE_BIT)
+                       return 1;
+               break;
+       case 'g': /* General Discovery Procedure */
+               if (flags & (FLAGS_LIMITED_MODE_BIT | FLAGS_GENERAL_MODE_BIT))
+                       return 1;
+               break;
+       default:
+               fprintf(stderr, "Unknown discovery procedure\n");
+       }
+
+       return 0;
+}
+
+static void sigint_handler(int sig)
+{
+       signal_received = sig;
+}
+
+static void eir_parse_name(uint8_t *eir, size_t eir_len,
+                                               char *buf, size_t buf_len)
+{
+       size_t offset;
+
+       offset = 0;
+       while (offset < eir_len) {
+               uint8_t field_len = eir[0];
+               size_t name_len;
+
+               /* Check for the end of EIR */
+               if (field_len == 0)
+                       break;
+
+               if (offset + field_len > eir_len)
+                       goto failed;
+
+               switch (eir[1]) {
+               case EIR_NAME_SHORT:
+               case EIR_NAME_COMPLETE:
+                       name_len = field_len - 1;
+                       if (name_len > buf_len)
+                               goto failed;
+
+                       memcpy(buf, &eir[2], name_len);
+                       return;
+               }
+
+               offset += field_len + 1;
+               eir += field_len + 1;
+       }
+
+failed:
+       snprintf(buf, buf_len, "(unknown)");
+}
+
+static int print_advertising_devices(int dd, uint8_t filter_type)
+{
+       unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
+       struct hci_filter nf, of;
+       struct sigaction sa;
+       socklen_t olen;
+       int len;
+
+       olen = sizeof(of);
+       if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
+               printf("Could not get socket options\n");
+               return -1;
+       }
+
+       hci_filter_clear(&nf);
+       hci_filter_set_ptype(HCI_EVENT_PKT, &nf);
+       hci_filter_set_event(EVT_LE_META_EVENT, &nf);
+
+       if (setsockopt(dd, SOL_HCI, HCI_FILTER, &nf, sizeof(nf)) < 0) {
+               printf("Could not set socket options\n");
+               return -1;
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags = SA_NOCLDSTOP;
+       sa.sa_handler = sigint_handler;
+       sigaction(SIGINT, &sa, NULL);
+
+       while (1) {
+               evt_le_meta_event *meta;
+               le_advertising_info *info;
+               char addr[18];
+
+               while ((len = read(dd, buf, sizeof(buf))) < 0) {
+                       if (errno == EINTR && signal_received == SIGINT) {
+                               len = 0;
+                               goto done;
+                       }
+
+                       if (errno == EAGAIN || errno == EINTR)
+                               continue;
+                       goto done;
+               }
+
+               ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
+               len -= (1 + HCI_EVENT_HDR_SIZE);
+
+               meta = (void *) ptr;
+
+               if (meta->subevent != 0x02)
+                       goto done;
+
+               /* Ignoring multiple reports */
+               info = (le_advertising_info *) (meta->data + 1);
+               if (check_report_filter(filter_type, info)) {
+                       char name[30];
+
+                       memset(name, 0, sizeof(name));
+
+                       ba2str(&info->bdaddr, addr);
+                       eir_parse_name(info->data, info->length,
+                                                       name, sizeof(name) - 1);
+
+                       printf("%s %s\n", addr, name);
+               }
+       }
+
+done:
+       setsockopt(dd, SOL_HCI, HCI_FILTER, &of, sizeof(of));
+
+       if (len < 0)
+               return -1;
+
+       return 0;
+}
+
+static struct option lescan_options[] = {
+       { "help",       0, 0, 'h' },
+       { "privacy",    0, 0, 'p' },
+       { "passive",    0, 0, 'P' },
+       { "discovery",  1, 0, 'd' },
+       { "duplicates", 0, 0, 'D' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *lescan_help =
+       "Usage:\n"
+       "\tlescan [--privacy] enable privacy\n"
+       "\tlescan [--passive] set scan type passive (default active)\n"
+       "\tlescan [--discovery=g|l] enable general or limited discovery"
+               "procedure\n"
+       "\tlescan [--duplicates] don't filter duplicates\n";
+
+static void cmd_lescan(int dev_id, int argc, char **argv)
+{
+       int err, opt, dd;
+       uint8_t own_type = 0x00;
+       uint8_t scan_type = 0x01;
+       uint8_t filter_type = 0;
+       uint16_t interval = htobs(0x0010);
+       uint16_t window = htobs(0x0010);
+       uint8_t filter_dup = 1;
+
+       for_each_opt(opt, lescan_options, NULL) {
+               switch (opt) {
+               case 'p':
+                       own_type = 0x01; /* Random */
+                       break;
+               case 'P':
+                       scan_type = 0x00; /* Passive */
+                       break;
+               case 'd':
+                       filter_type = optarg[0];
+                       if (filter_type != 'g' && filter_type != 'l') {
+                               fprintf(stderr, "Unknown discovery procedure\n");
+                               exit(1);
+                       }
+
+                       interval = htobs(0x0012);
+                       window = htobs(0x0012);
+                       break;
+               case 'D':
+                       filter_dup = 0x00;
+                       break;
+               default:
+                       printf("%s", lescan_help);
+                       return;
+               }
+       }
+       helper_arg(0, 1, &argc, &argv, lescan_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Could not open device");
+               exit(1);
+       }
+
+       err = hci_le_set_scan_parameters(dd, scan_type, interval, window,
+                                                       own_type, 0x00, 1000);
+       if (err < 0) {
+               perror("Set scan parameters failed");
+               exit(1);
+       }
+
+       err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 1000);
+       if (err < 0) {
+               perror("Enable scan failed");
+               exit(1);
+       }
+
+       printf("LE Scan ...\n");
+
+       err = print_advertising_devices(dd, filter_type);
+       if (err < 0) {
+               perror("Could not receive advertising events");
+               exit(1);
+       }
+
+       err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 1000);
+       if (err < 0) {
+               perror("Disable scan failed");
+               exit(1);
+       }
+
+       hci_close_dev(dd);
+}
+
+static struct option lecc_options[] = {
+       { "help",       0, 0, 'h' },
+       { "random",     0, 0, 'r' },
+       { "whitelist",  0, 0, 'w' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *lecc_help =
+       "Usage:\n"
+       "\tlecc [--random] <bdaddr>\n"
+       "\tlecc --whitelist\n";
+
+static void cmd_lecc(int dev_id, int argc, char **argv)
+{
+       int err, opt, dd;
+       bdaddr_t bdaddr;
+       uint16_t interval, latency, max_ce_length, max_interval, min_ce_length;
+       uint16_t min_interval, supervision_timeout, window, handle;
+       uint8_t initiator_filter, own_bdaddr_type, peer_bdaddr_type;
+
+       peer_bdaddr_type = LE_PUBLIC_ADDRESS;
+       initiator_filter = 0; /* Use peer address */
+
+       for_each_opt(opt, lecc_options, NULL) {
+               switch (opt) {
+               case 'r':
+                       peer_bdaddr_type = LE_RANDOM_ADDRESS;
+                       break;
+               case 'w':
+                       initiator_filter = 0x01; /* Use white list */
+                       break;
+               default:
+                       printf("%s", lecc_help);
+                       return;
+               }
+       }
+       helper_arg(0, 1, &argc, &argv, lecc_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Could not open device");
+               exit(1);
+       }
+
+       memset(&bdaddr, 0, sizeof(bdaddr_t));
+       if (argv[0])
+               str2ba(argv[0], &bdaddr);
+
+       interval = htobs(0x0004);
+       window = htobs(0x0004);
+       own_bdaddr_type = 0x00;
+       min_interval = htobs(0x000F);
+       max_interval = htobs(0x000F);
+       latency = htobs(0x0000);
+       supervision_timeout = htobs(0x0C80);
+       min_ce_length = htobs(0x0001);
+       max_ce_length = htobs(0x0001);
+
+       err = hci_le_create_conn(dd, interval, window, initiator_filter,
+                       peer_bdaddr_type, bdaddr, own_bdaddr_type, min_interval,
+                       max_interval, latency, supervision_timeout,
+                       min_ce_length, max_ce_length, &handle, 25000);
+       if (err < 0) {
+               perror("Could not create connection");
+               exit(1);
+       }
+
+       printf("Connection handle %d\n", handle);
+
+       hci_close_dev(dd);
+}
+
+static struct option lewladd_options[] = {
+       { "help",       0, 0, 'h' },
+       { "random",     0, 0, 'r' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *lewladd_help =
+       "Usage:\n"
+       "\tlewladd [--random] <bdaddr>\n";
+
+static void cmd_lewladd(int dev_id, int argc, char **argv)
+{
+       int err, opt, dd;
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type = LE_PUBLIC_ADDRESS;
+
+       for_each_opt(opt, lewladd_options, NULL) {
+               switch (opt) {
+               case 'r':
+                       bdaddr_type = LE_RANDOM_ADDRESS;
+                       break;
+               default:
+                       printf("%s", lewladd_help);
+                       return;
+               }
+       }
+
+       helper_arg(1, 1, &argc, &argv, lewladd_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Could not open device");
+               exit(1);
+       }
+
+       str2ba(argv[0], &bdaddr);
+
+       err = hci_le_add_white_list(dd, &bdaddr, bdaddr_type, 1000);
+       hci_close_dev(dd);
+
+       if (err < 0) {
+               err = -errno;
+               fprintf(stderr, "Can't add to white list: %s(%d)\n",
+                                                       strerror(-err), -err);
+               exit(1);
+       }
+}
+
+static struct option lewlrm_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *lewlrm_help =
+       "Usage:\n"
+       "\tlewlrm <bdaddr>\n";
+
+static void cmd_lewlrm(int dev_id, int argc, char **argv)
+{
+       int err, opt, dd;
+       bdaddr_t bdaddr;
+
+       for_each_opt(opt, lewlrm_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", lewlrm_help);
+                       return;
+               }
+       }
+
+       helper_arg(1, 1, &argc, &argv, lewlrm_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Could not open device");
+               exit(1);
+       }
+
+       str2ba(argv[0], &bdaddr);
+
+       err = hci_le_rm_white_list(dd, &bdaddr, LE_PUBLIC_ADDRESS, 1000);
+       hci_close_dev(dd);
+
+       if (err < 0) {
+               err = errno;
+               fprintf(stderr, "Can't remove from white list: %s(%d)\n",
+                                                       strerror(err), err);
+               exit(1);
+       }
+}
+
+static struct option lewlsz_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *lewlsz_help =
+       "Usage:\n"
+       "\tlewlsz\n";
+
+static void cmd_lewlsz(int dev_id, int argc, char **argv)
+{
+       int err, dd, opt;
+       uint8_t size;
+
+       for_each_opt(opt, lewlsz_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", lewlsz_help);
+                       return;
+               }
+       }
+
+       helper_arg(0, 0, &argc, &argv, lewlsz_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Could not open device");
+               exit(1);
+       }
+
+       err = hci_le_read_white_list_size(dd, &size, 1000);
+       hci_close_dev(dd);
+
+       if (err < 0) {
+               err = -errno;
+               fprintf(stderr, "Can't read white list size: %s(%d)\n",
+                                                       strerror(-err), -err);
+               exit(1);
+       }
+
+       printf("White list size: %d\n", size);
+}
+
+static struct option lewlclr_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *lewlclr_help =
+       "Usage:\n"
+       "\tlewlclr\n";
+
+static void cmd_lewlclr(int dev_id, int argc, char **argv)
+{
+       int err, dd, opt;
+
+       for_each_opt(opt, lewlclr_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", lewlclr_help);
+                       return;
+               }
+       }
+
+       helper_arg(0, 0, &argc, &argv, lewlclr_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Could not open device");
+               exit(1);
+       }
+
+       err = hci_le_clear_white_list(dd, 1000);
+       hci_close_dev(dd);
+
+       if (err < 0) {
+               err = -errno;
+               fprintf(stderr, "Can't clear white list: %s(%d)\n",
+                                                       strerror(-err), -err);
+               exit(1);
+       }
+}
+
+static struct option ledc_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *ledc_help =
+       "Usage:\n"
+       "\tledc <handle> [reason]\n";
+
+static void cmd_ledc(int dev_id, int argc, char **argv)
+{
+       int err, opt, dd;
+       uint16_t handle;
+       uint8_t reason;
+
+       for_each_opt(opt, ledc_options, NULL) {
+               switch (opt) {
+               default:
+                       printf("%s", ledc_help);
+                       return;
+               }
+       }
+       helper_arg(1, 2, &argc, &argv, ledc_help);
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               perror("Could not open device");
+               exit(1);
+       }
+
+       handle = atoi(argv[0]);
+
+       reason = (argc > 1) ? atoi(argv[1]) : HCI_OE_USER_ENDED_CONNECTION;
+
+       err = hci_disconnect(dd, handle, reason, 10000);
+       if (err < 0) {
+               perror("Could not disconnect");
+               exit(1);
+       }
+
+       hci_close_dev(dd);
+}
+
+static struct option lecup_options[] = {
+       { "help",       0, 0, 'h' },
+       { "handle",     1, 0, 'H' },
+       { "min",        1, 0, 'm' },
+       { "max",        1, 0, 'M' },
+       { "latency",    1, 0, 'l' },
+       { "timeout",    1, 0, 't' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *lecup_help =
+       "Usage:\n"
+       "\tlecup <handle> <min> <max> <latency> <timeout>\n"
+       "\tOptions:\n"
+       "\t    -H, --handle <0xXXXX>  LE connection handle\n"
+       "\t    -m, --min <interval>   Range: 0x0006 to 0x0C80\n"
+       "\t    -M, --max <interval>   Range: 0x0006 to 0x0C80\n"
+       "\t    -l, --latency <range>  Slave latency. Range: 0x0000 to 0x03E8\n"
+       "\t    -t, --timeout  <time>  N * 10ms. Range: 0x000A to 0x0C80\n"
+       "\n\t min/max range: 7.5ms to 4s. Multiply factor: 1.25ms"
+       "\n\t timeout range: 100ms to 32.0s. Larger than max interval\n";
+
+static void cmd_lecup(int dev_id, int argc, char **argv)
+{
+       uint16_t handle = 0, min, max, latency, timeout;
+       int opt, dd, base;
+
+       /* Aleatory valid values */
+       min = 0x0C8;
+       max = 0x0960;
+       latency = 0x0007;
+       timeout = 0x0C80;
+
+       for_each_opt(opt, lecup_options, NULL) {
+               if (optarg && strncasecmp("0x", optarg, 2) == 0)
+                       base = 16;
+               else
+                       base = 10;
+
+               switch (opt) {
+               case 'H':
+                       handle = strtoul(optarg, NULL, base);
+                       break;
+               case 'm':
+                       min = strtoul(optarg, NULL, base);
+                       break;
+               case 'M':
+                       max = strtoul(optarg, NULL, base);
+                       break;
+               case 'l':
+                       latency = strtoul(optarg, NULL, base);
+                       break;
+               case 't':
+                       timeout = strtoul(optarg, NULL, base);
+                       break;
+               default:
+                       printf("%s", lecup_help);
+                       return;
+               }
+       }
+
+       if (handle == 0) {
+               printf("%s", lecup_help);
+               return;
+       }
+
+       if (dev_id < 0)
+               dev_id = hci_get_route(NULL);
+
+       dd = hci_open_dev(dev_id);
+       if (dd < 0) {
+               fprintf(stderr, "HCI device open failed\n");
+               exit(1);
+       }
+
+       if (hci_le_conn_update(dd, htobs(handle), htobs(min), htobs(max),
+                               htobs(latency), htobs(timeout), 5000) < 0) {
+               int err = -errno;
+               fprintf(stderr, "Could not change connection params: %s(%d)\n",
+                                                       strerror(-err), -err);
+       }
+
+       hci_close_dev(dd);
+}
+
+static struct {
+       char *cmd;
+       void (*func)(int dev_id, int argc, char **argv);
+       char *doc;
+} command[] = {
+       { "dev",      cmd_dev,     "Display local devices"                },
+       { "inq",      cmd_inq,     "Inquire remote devices"               },
+       { "scan",     cmd_scan,    "Scan for remote devices"              },
+       { "name",     cmd_name,    "Get name from remote device"          },
+       { "info",     cmd_info,    "Get information from remote device"   },
+       { "spinq",    cmd_spinq,   "Start periodic inquiry"               },
+       { "epinq",    cmd_epinq,   "Exit periodic inquiry"                },
+       { "cmd",      cmd_cmd,     "Submit arbitrary HCI commands"        },
+       { "con",      cmd_con,     "Display active connections"           },
+       { "cc",       cmd_cc,      "Create connection to remote device"   },
+       { "dc",       cmd_dc,      "Disconnect from remote device"        },
+       { "sr",       cmd_sr,      "Switch master/slave role"             },
+       { "cpt",      cmd_cpt,     "Change connection packet type"        },
+       { "rssi",     cmd_rssi,    "Display connection RSSI"              },
+       { "lq",       cmd_lq,      "Display link quality"                 },
+       { "tpl",      cmd_tpl,     "Display transmit power level"         },
+       { "afh",      cmd_afh,     "Display AFH channel map"              },
+       { "lp",       cmd_lp,      "Set/display link policy settings"     },
+       { "lst",      cmd_lst,     "Set/display link supervision timeout" },
+       { "auth",     cmd_auth,    "Request authentication"               },
+       { "enc",      cmd_enc,     "Set connection encryption"            },
+       { "key",      cmd_key,     "Change connection link key"           },
+       { "clkoff",   cmd_clkoff,  "Read clock offset"                    },
+       { "clock",    cmd_clock,   "Read local or remote clock"           },
+       { "lescan",   cmd_lescan,  "Start LE scan"                        },
+       { "lewladd",  cmd_lewladd, "Add device to LE White List"          },
+       { "lewlrm",   cmd_lewlrm,  "Remove device from LE White List"     },
+       { "lewlsz",   cmd_lewlsz,  "Read size of LE White List"           },
+       { "lewlclr",  cmd_lewlclr, "Clear LE White list"                  },
+       { "lecc",     cmd_lecc,    "Create a LE Connection"               },
+       { "ledc",     cmd_ledc,    "Disconnect a LE Connection"           },
+       { "lecup",    cmd_lecup,   "LE Connection Update"                 },
+       { NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+       int i;
+
+       printf("hcitool - HCI Tool ver %s\n", VERSION);
+       printf("Usage:\n"
+               "\thcitool [options] <command> [command parameters]\n");
+       printf("Options:\n"
+               "\t--help\tDisplay help\n"
+               "\t-i dev\tHCI device\n");
+       printf("Commands:\n");
+       for (i = 0; command[i].cmd; i++)
+               printf("\t%-4s\t%s\n", command[i].cmd,
+               command[i].doc);
+       printf("\n"
+               "For more information on the usage of each command use:\n"
+               "\thcitool <command> --help\n" );
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "device",     1, 0, 'i' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       int opt, i, dev_id = -1;
+       bdaddr_t ba;
+
+       while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'i':
+                       dev_id = hci_devid(optarg);
+                       if (dev_id < 0) {
+                               perror("Invalid device");
+                               exit(1);
+                       }
+                       break;
+
+               case 'h':
+               default:
+                       usage();
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               exit(0);
+       }
+
+       if (dev_id != -1 && hci_devba(dev_id, &ba) < 0) {
+               perror("Device is not available");
+               exit(1);
+       }
+
+       for (i = 0; command[i].cmd; i++) {
+               if (strncmp(command[i].cmd,
+                               argv[0], strlen(command[i].cmd)))
+                       continue;
+
+               command[i].func(dev_id, argc, argv);
+               break;
+       }
+
+       if (command[i].cmd == 0) {
+               fprintf(stderr, "Unknown command - \"%s\"\n", *argv);
+               exit(1);
+       }
+
+       return 0;
+}
diff --git a/tools/hid2hci.8 b/tools/hid2hci.8
new file mode 100644 (file)
index 0000000..6ea7ed8
--- /dev/null
@@ -0,0 +1,46 @@
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH HID2HCI 8 "MAY 15, 2009" "" ""
+
+.SH NAME
+hid2hci \- Bluetooth HID to HCI mode switching utility
+.SH SYNOPSIS
+.BR "hid2hci
+[
+.I options
+]
+.SH DESCRIPTION
+.B hid2hci
+is used to set up switch supported Bluetooth devices into the HCI
+mode and back.
+.SH OPTIONS
+.TP
+.B --mode= [hid, hci]
+Sets the mode to switch the device into
+.TP
+.B --method= [csr, logitech-hid, dell]
+Which vendor method to use for switching the device.
+.TP
+.B --devpath=
+Specifies the device path in /sys
+.TP
+.B --help
+Gives a list of possible options.
+.TP
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/hid2hci.c b/tools/hid2hci.c
new file mode 100644 (file)
index 0000000..e3a5b2e
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * hid2hci : switch the radio on devices that support
+ *           it from HID to HCI and back
+ *
+ *  Copyright (C) 2003-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2008-2009  Mario Limonciello <mario_limonciello@dell.com>
+ *  Copyright (C) 2009-2011  Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/hiddev.h>
+#include <usb.h>
+
+#include "libudev.h"
+
+enum mode {
+       HCI = 0,
+       HID = 1,
+};
+
+static int usb_switch_csr(struct usb_dev_handle *dev, enum mode mode)
+{
+       int err;
+
+       err = usb_control_msg(dev,
+                       USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                       0, mode, 0, NULL, 0, 10000);
+       if (err == 0) {
+               err = -1;
+               errno = EALREADY;
+       } else if (errno == ETIMEDOUT)
+               err = 0;
+
+       return err;
+}
+
+static int hid_logitech_send_report(int fd, const char *buf, size_t size)
+{
+       struct hiddev_report_info rinfo;
+       struct hiddev_usage_ref uref;
+       unsigned int i;
+       int err;
+
+       for (i = 0; i < size; i++) {
+               memset(&uref, 0, sizeof(uref));
+               uref.report_type = HID_REPORT_TYPE_OUTPUT;
+               uref.report_id   = 0x10;
+               uref.field_index = 0;
+               uref.usage_index = i;
+               uref.usage_code  = 0xff000001;
+               uref.value       = buf[i] & 0x000000ff;
+               err = ioctl(fd, HIDIOCSUSAGE, &uref);
+               if (err < 0)
+                       return err;
+       }
+
+       memset(&rinfo, 0, sizeof(rinfo));
+       rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
+       rinfo.report_id   = 0x10;
+       rinfo.num_fields  = 1;
+       err = ioctl(fd, HIDIOCSREPORT, &rinfo);
+
+       return err;
+}
+
+static int hid_switch_logitech(const char *filename)
+{
+       char rep1[] = { 0xff, 0x80, 0x80, 0x01, 0x00, 0x00 };
+       char rep2[] = { 0xff, 0x80, 0x00, 0x00, 0x30, 0x00 };
+       char rep3[] = { 0xff, 0x81, 0x80, 0x00, 0x00, 0x00 };
+       int fd;
+       int err = -1;
+
+       fd = open(filename, O_RDWR);
+       if (fd < 0)
+               return err;
+
+       err = ioctl(fd, HIDIOCINITREPORT, 0);
+       if (err < 0)
+               goto out;
+
+       err = hid_logitech_send_report(fd, rep1, sizeof(rep1));
+       if (err < 0)
+               goto out;
+
+       err = hid_logitech_send_report(fd, rep2, sizeof(rep2));
+       if (err < 0)
+               goto out;
+
+       err = hid_logitech_send_report(fd, rep3, sizeof(rep3));
+out:
+       close(fd);
+       return err;
+}
+
+static int usb_switch_dell(struct usb_dev_handle *dev, enum mode mode)
+{
+       char report[] = { 0x7f, 0x00, 0x00, 0x00 };
+       int err;
+
+       switch (mode) {
+       case HCI:
+               report[1] = 0x13;
+               break;
+       case HID:
+               report[1] = 0x14;
+               break;
+       }
+
+       /* Don't need to check return, as might not be in use */
+       usb_detach_kernel_driver_np(dev, 0);
+
+       if (usb_claim_interface(dev, 0) < 0)
+               return -EIO;
+
+       err = usb_control_msg(dev,
+                       USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0,
+                       report, sizeof(report), 5000);
+
+       if (err == 0) {
+               err = -1;
+               errno = EALREADY;
+       } else {
+               if (errno == ETIMEDOUT)
+                       err = 0;
+       }
+       return err;
+}
+
+/*
+ * libusb needs to scan and open all devices, just to to find the
+ * device we already have. This should be fixed in libusb.
+ */
+static struct usb_device *usb_device_open_from_udev(struct udev_device *usb_dev)
+{
+       struct usb_bus *bus;
+       const char *str;
+       int busnum;
+       int devnum;
+
+       str = udev_device_get_sysattr_value(usb_dev, "busnum");
+       if (str == NULL)
+               return NULL;
+       busnum = strtol(str, NULL, 0);
+
+       str = udev_device_get_sysattr_value(usb_dev, "devnum");
+       if (str == NULL)
+               return NULL;
+       devnum = strtol(str, NULL, 0);
+
+       usb_init();
+       usb_find_busses();
+       usb_find_devices();
+
+       for (bus = usb_get_busses(); bus; bus = bus->next) {
+               struct usb_device *dev;
+
+               if (strtol(bus->dirname, NULL, 10) != busnum)
+                       continue;
+
+               for (dev = bus->devices; dev; dev = dev->next) {
+                       if (dev->devnum == devnum)
+                               return dev;
+               }
+       }
+
+       return NULL;
+}
+
+static struct usb_dev_handle *find_device(struct udev_device *udev_dev)
+{
+       struct usb_device *dev;
+
+       dev = usb_device_open_from_udev(udev_dev);
+       if (dev == NULL)
+               return NULL;
+       return usb_open(dev);
+}
+
+static void usage(const char *error)
+{
+       if (error)
+               fprintf(stderr,"\n%s\n", error);
+       else
+               printf("hid2hci - Bluetooth HID to HCI mode switching utility\n\n");
+
+       printf("Usage: hid2hci [options]\n"
+               "  --mode=               mode to switch to [hid|hci] (default hci)\n"
+               "  --devpath=            sys device path\n"
+               "  --method=             method to use to switch [csr|logitech-hid|dell]\n"
+               "  --help\n\n");
+}
+
+int main(int argc, char *argv[])
+{
+       static const struct option options[] = {
+               { "help", no_argument, NULL, 'h' },
+               { "mode", required_argument, NULL, 'm' },
+               { "devpath", required_argument, NULL, 'p' },
+               { "method", required_argument, NULL, 'M' },
+               { }
+       };
+       enum method {
+               METHOD_UNDEF,
+               METHOD_CSR,
+               METHOD_LOGITECH_HID,
+               METHOD_DELL,
+       } method = METHOD_UNDEF;
+       struct udev *udev;
+       struct udev_device *udev_dev = NULL;
+       char syspath[PATH_MAX];
+       int (*usb_switch)(struct usb_dev_handle *dev, enum mode mode) = NULL;
+       enum mode mode = HCI;
+       const char *devpath = NULL;
+       int err = -1;
+       int rc = 1;
+
+       for (;;) {
+               int option;
+
+               option = getopt_long(argc, argv, "m:p:M:h", options, NULL);
+               if (option == -1)
+                       break;
+
+               switch (option) {
+               case 'm':
+                       if (!strcmp(optarg, "hid")) {
+                               mode = HID;
+                       } else if (!strcmp(optarg, "hci")) {
+                               mode = HCI;
+                       } else {
+                               usage("error: undefined radio mode\n");
+                               exit(1);
+                       }
+                       break;
+               case 'p':
+                       devpath = optarg;
+                       break;
+               case 'M':
+                       if (!strcmp(optarg, "csr")) {
+                               method = METHOD_CSR;
+                               usb_switch = usb_switch_csr;
+                       } else if (!strcmp(optarg, "logitech-hid")) {
+                               method = METHOD_LOGITECH_HID;
+                       } else if (!strcmp(optarg, "dell")) {
+                               method = METHOD_DELL;
+                               usb_switch = usb_switch_dell;
+                       } else {
+                               usage("error: undefined switching method\n");
+                               exit(1);
+                       }
+                       break;
+               case 'h':
+                       usage(NULL);
+               }
+       }
+
+       if (!devpath || method == METHOD_UNDEF) {
+               usage("error: --devpath= and --method= must be defined\n");
+               exit(1);
+       }
+
+       udev = udev_new();
+       if (udev == NULL)
+               goto exit;
+
+       snprintf(syspath, sizeof(syspath), "/sys/%s", devpath);
+       udev_dev = udev_device_new_from_syspath(udev, syspath);
+       if (udev_dev == NULL) {
+               fprintf(stderr, "error: could not find '%s'\n", devpath);
+               goto exit;
+       }
+
+       switch (method) {
+       case METHOD_CSR:
+       case METHOD_DELL: {
+               struct udev_device *dev;
+               struct usb_dev_handle *handle;
+               const char *type;
+
+               /* get the parent usb_device if needed */
+               dev = udev_dev;
+               type = udev_device_get_devtype(dev);
+               if (type == NULL || strcmp(type, "usb_device") != 0) {
+                       dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");
+                       if (dev == NULL) {
+                               fprintf(stderr, "error: could not find usb_device for '%s'\n", devpath);
+                               goto exit;
+                       }
+               }
+
+               handle = find_device(dev);
+               if (handle == NULL) {
+                       fprintf(stderr, "error: unable to handle '%s'\n",
+                               udev_device_get_syspath(dev));
+                       goto exit;
+               }
+               err = usb_switch(handle, mode);
+               break;
+       }
+       case METHOD_LOGITECH_HID: {
+               const char *device;
+
+               device = udev_device_get_devnode(udev_dev);
+               if (device == NULL) {
+                       fprintf(stderr, "error: could not find hiddev device node\n");
+                       goto exit;
+               }
+               err = hid_switch_logitech(device);
+               break;
+       }
+       default:
+               break;
+       }
+
+       if (err < 0)
+               fprintf(stderr, "error: switching device '%s' failed.\n",
+                       udev_device_get_syspath(udev_dev));
+exit:
+       udev_device_unref(udev_dev);
+       udev_unref(udev);
+       return rc;
+}
diff --git a/tools/kword.c b/tools/kword.c
new file mode 100644 (file)
index 0000000..62e24fe
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+#include "parser.h"
+
+int lineno;
+
+struct keyword_t rfcomm_keyword[] = {
+       { "bind",       K_BIND          },
+       { "device",     K_DEVICE        },
+       { "channel",    K_CHANNEL       },
+       { "comment",    K_COMMENT       },
+
+       { "yes",        K_YES           },
+       { "no",         K_NO            },
+       { "enable",     K_YES           },
+       { "disable",    K_NO            },
+
+       { NULL , 0 }
+};
+
+int rfcomm_find_keyword(struct keyword_t *keyword, char *string)
+{
+       while (keyword->string) {
+               if (!strcmp(string, keyword->string))
+                       return keyword->type;
+               keyword++;
+       }
+
+       return -1;
+}
+
+struct rfcomm_opts rfcomm_opts[RFCOMM_MAX_DEV];
diff --git a/tools/kword.h b/tools/kword.h
new file mode 100644 (file)
index 0000000..81a2a88
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+extern int lineno;
+
+struct keyword_t {
+       char *string;
+       int type;
+};
+
+extern struct keyword_t rfcomm_keyword[];
+
+int rfcomm_find_keyword(struct keyword_t *keyword, char *string);
+
+#define MAXCOMMENTLEN  100
+
+struct rfcomm_opts {
+       int bind;
+       bdaddr_t bdaddr;
+       int channel;
+       char comment[MAXCOMMENTLEN + 1];
+};
+
+extern struct rfcomm_opts rfcomm_opts[RFCOMM_MAX_DEV];
+
+int rfcomm_read_config(char *filename);
diff --git a/tools/l2ping.8 b/tools/l2ping.8
new file mode 100644 (file)
index 0000000..8b77ee2
--- /dev/null
@@ -0,0 +1,76 @@
+.TH L2PING 8 "Jan 22 2002" BlueZ "Linux System Administration"
+.SH NAME
+l2ping \- Send L2CAP echo request and receive answer
+.SH SYNOPSIS
+.B l2ping
+.RB [\| \-i
+.IR <hciX> \|]
+.RB [\| \-s
+.IR size \|]
+.RB [\| \-c
+.IR count \|]
+.RB [\| \-t
+.IR timeout \|]
+.RB [\| \-d
+.IR delay \|]
+.RB [\| \-f \|]
+.RB [\| \-r \|]
+.RB [\| \-v \|]
+.I bd_addr
+
+.SH DESCRIPTION
+.LP
+L2ping sends a L2CAP echo request to the Bluetooth MAC address
+.I bd_addr
+given in dotted hex notation.
+.SH OPTIONS
+.TP
+.BI \-i " <hciX>"
+The command is applied to device
+.BI
+hciX
+, which must be the name of an installed Bluetooth device (X = 0, 1, 2, ...)
+If not specified, the command will be sent to the first available Bluetooth
+device.
+.TP
+.BI \-s " size"
+The
+.I size
+of the data packets to be sent.
+.TP
+.BI \-c " count"
+Send
+.I count
+number of packets then exit.
+.TP
+.BI \-t " timeout"
+Wait
+.I timeout
+seconds for the response.
+.TP
+.BI \-d " delay"
+Wait
+.I delay
+seconds between pings.
+.TP
+.B \-f
+Kind of flood ping. Use with care! It reduces the delay time between packets
+to 0.
+.TP
+.B \-r
+Reverse ping (gnip?). Send echo response instead of echo request.
+.TP
+.B \-v
+Verify response payload is identical to request payload. It is not required for
+remote stacks to return the request payload, but most stacks do (including
+Bluez).
+.TP
+.I bd_addr
+The Bluetooth MAC address to be pinged in dotted hex notation like
+.B 01:02:03:ab:cd:ef
+or
+.B 01:EF:cd:aB:02:03
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
+.PP
+man page by Nils Faerber <nils@kernelconcepts.de>, Adam Laurie <adam@algroup.co.uk>.
diff --git a/tools/l2ping.c b/tools/l2ping.c
new file mode 100644 (file)
index 0000000..29fb3d0
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2000-2001  Qualcomm Incorporated
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+/* Defaults */
+static bdaddr_t bdaddr;
+static int size    = 44;
+static int ident   = 200;
+static int delay   = 1;
+static int count   = -1;
+static int timeout = 10;
+static int reverse = 0;
+static int verify = 0;
+
+/* Stats */
+static int sent_pkt = 0;
+static int recv_pkt = 0;
+
+static float tv2fl(struct timeval tv)
+{
+       return (float)(tv.tv_sec*1000.0) + (float)(tv.tv_usec/1000.0);
+}
+
+static void stat(int sig)
+{
+       int loss = sent_pkt ? (float)((sent_pkt-recv_pkt)/(sent_pkt/100.0)) : 0;
+       printf("%d sent, %d received, %d%% loss\n", sent_pkt, recv_pkt, loss);
+       exit(0);
+}
+
+static void ping(char *svr)
+{
+       struct sigaction sa;
+       struct sockaddr_l2 addr;
+       socklen_t optlen;
+       unsigned char *send_buf;
+       unsigned char *recv_buf;
+       char str[18];
+       int i, sk, lost;
+       uint8_t id;
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = stat;
+       sigaction(SIGINT, &sa, NULL);
+
+       send_buf = malloc(L2CAP_CMD_HDR_SIZE + size);
+       recv_buf = malloc(L2CAP_CMD_HDR_SIZE + size);
+       if (!send_buf || !recv_buf) {
+               perror("Can't allocate buffer");
+               exit(1);
+       }
+
+       /* Create socket */
+       sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+       if (sk < 0) {
+               perror("Can't create socket");
+               goto error;
+       }
+
+       /* Bind to local address */
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       bacpy(&addr.l2_bdaddr, &bdaddr);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Can't bind socket");
+               goto error;
+       }
+
+       /* Connect to remote device */
+       memset(&addr, 0, sizeof(addr));
+       addr.l2_family = AF_BLUETOOTH;
+       str2ba(svr, &addr.l2_bdaddr);
+
+       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Can't connect");
+               goto error;
+       }
+
+       /* Get local address */
+       memset(&addr, 0, sizeof(addr));
+       optlen = sizeof(addr);
+
+       if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+               perror("Can't get local address");
+               goto error;
+       }
+
+       ba2str(&addr.l2_bdaddr, str);
+       printf("Ping: %s from %s (data size %d) ...\n", svr, str, size);
+
+       /* Initialize send buffer */
+       for (i = 0; i < size; i++)
+               send_buf[L2CAP_CMD_HDR_SIZE + i] = (i % 40) + 'A';
+
+       id = ident;
+
+       while (count == -1 || count-- > 0) {
+               struct timeval tv_send, tv_recv, tv_diff;
+               l2cap_cmd_hdr *send_cmd = (l2cap_cmd_hdr *) send_buf;
+               l2cap_cmd_hdr *recv_cmd = (l2cap_cmd_hdr *) recv_buf;
+
+               /* Build command header */
+               send_cmd->ident = id;
+               send_cmd->len   = htobs(size);
+
+               if (reverse)
+                       send_cmd->code = L2CAP_ECHO_RSP;
+               else
+                       send_cmd->code = L2CAP_ECHO_REQ;
+
+               gettimeofday(&tv_send, NULL);
+
+               /* Send Echo Command */
+               if (send(sk, send_buf, L2CAP_CMD_HDR_SIZE + size, 0) <= 0) {
+                       perror("Send failed");
+                       goto error;
+               }
+
+               /* Wait for Echo Response */
+               lost = 0;
+               while (1) {
+                       struct pollfd pf[1];
+                       int err;
+
+                       pf[0].fd = sk;
+                       pf[0].events = POLLIN;
+
+                       if ((err = poll(pf, 1, timeout * 1000)) < 0) {
+                               perror("Poll failed");
+                               goto error;
+                       }
+
+                       if (!err) {
+                               lost = 1;
+                               break;
+                       }
+
+                       if ((err = recv(sk, recv_buf, L2CAP_CMD_HDR_SIZE + size, 0)) < 0) {
+                               perror("Recv failed");
+                               goto error;
+                       }
+
+                       if (!err){
+                               printf("Disconnected\n");
+                               goto error;
+                       }
+
+                       recv_cmd->len = btohs(recv_cmd->len);
+
+                       /* Check for our id */
+                       if (recv_cmd->ident != id)
+                               continue;
+
+                       /* Check type */
+                       if (!reverse && recv_cmd->code == L2CAP_ECHO_RSP)
+                               break;
+
+                       if (recv_cmd->code == L2CAP_COMMAND_REJ) {
+                               printf("Peer doesn't support Echo packets\n");
+                               goto error;
+                       }
+
+               }
+               sent_pkt++;
+
+               if (!lost) {
+                       recv_pkt++;
+
+                       gettimeofday(&tv_recv, NULL);
+                       timersub(&tv_recv, &tv_send, &tv_diff);
+
+                       if (verify) {
+                               /* Check payload length */
+                               if (recv_cmd->len != size) {
+                                       fprintf(stderr, "Received %d bytes, expected %d\n",
+                                                  recv_cmd->len, size);
+                                       goto error;
+                               }
+
+                               /* Check payload */
+                               if (memcmp(&send_buf[L2CAP_CMD_HDR_SIZE],
+                                                  &recv_buf[L2CAP_CMD_HDR_SIZE], size)) {
+                                       fprintf(stderr, "Response payload different.\n");
+                                       goto error;
+                               }
+                       }
+
+                       printf("%d bytes from %s id %d time %.2fms\n", recv_cmd->len, svr,
+                                  id - ident, tv2fl(tv_diff));
+
+                       if (delay)
+                               sleep(delay);
+               } else {
+                       printf("no response from %s: id %d\n", svr, id - ident);
+               }
+
+               if (++id > 254)
+                       id = ident;
+       }
+       stat(0);
+       free(send_buf);
+       free(recv_buf);
+       return;
+
+error:
+       close(sk);
+       free(send_buf);
+       free(recv_buf);
+       exit(1);
+}
+
+static void usage(void)
+{
+       printf("l2ping - L2CAP ping\n");
+       printf("Usage:\n");
+       printf("\tl2ping [-i device] [-s size] [-c count] [-t timeout] [-d delay] [-f] [-r] [-v] <bdaddr>\n");
+       printf("\t-f  Flood ping (delay = 0)\n");
+       printf("\t-r  Reverse ping\n");
+       printf("\t-v  Verify request and response payload\n");
+}
+
+int main(int argc, char *argv[])
+{
+       int opt;
+
+       /* Default options */
+       bacpy(&bdaddr, BDADDR_ANY);
+
+       while ((opt=getopt(argc,argv,"i:d:s:c:t:frv")) != EOF) {
+               switch(opt) {
+               case 'i':
+                       if (!strncasecmp(optarg, "hci", 3))
+                               hci_devba(atoi(optarg + 3), &bdaddr);
+                       else
+                               str2ba(optarg, &bdaddr);
+                       break;
+
+               case 'd':
+                       delay = atoi(optarg);
+                       break;
+
+               case 'f':
+                       /* Kinda flood ping */
+                       delay = 0;
+                       break;
+
+               case 'r':
+                       /* Use responses instead of requests */
+                       reverse = 1;
+                       break;
+
+               case 'v':
+                       verify = 1;
+                       break;
+
+               case 'c':
+                       count = atoi(optarg);
+                       break;
+
+               case 't':
+                       timeout = atoi(optarg);
+                       break;
+
+               case 's':
+                       size = atoi(optarg);
+                       break;
+
+               default:
+                       usage();
+                       exit(1);
+               }
+       }
+
+       if (!(argc - optind)) {
+               usage();
+               exit(1);
+       }
+
+       ping(argv[optind]);
+
+       return 0;
+}
diff --git a/tools/lexer.c b/tools/lexer.c
new file mode 100644 (file)
index 0000000..5228bc0
--- /dev/null
@@ -0,0 +1,1848 @@
+
+#line 3 "tools/lexer.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types. 
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else  /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               *yy_cp = (yy_hold_char); \
+               YY_RESTORE_YY_MORE_OFFSET \
+               (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+               YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+               } \
+       while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+       {
+       FILE *yy_input_file;
+
+       char *yy_ch_buf;                /* input buffer */
+       char *yy_buf_pos;               /* current position in input buffer */
+
+       /* Size of input buffer in bytes, not including room for EOB
+        * characters.
+        */
+       yy_size_t yy_buf_size;
+
+       /* Number of characters read into yy_ch_buf, not including EOB
+        * characters.
+        */
+       int yy_n_chars;
+
+       /* Whether we "own" the buffer - i.e., we know we created it,
+        * and can realloc() it to grow it, and should free() it to
+        * delete it.
+        */
+       int yy_is_our_buffer;
+
+       /* Whether this is an "interactive" input source; if so, and
+        * if we're using stdio for input, then we want to use getc()
+        * instead of fread(), to make sure we stop fetching input after
+        * each newline.
+        */
+       int yy_is_interactive;
+
+       /* Whether we're considered to be at the beginning of a line.
+        * If so, '^' rules will be active on the next match, otherwise
+        * not.
+        */
+       int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+       /* Whether to try to fill the input buffer when we reach the
+        * end of it.
+        */
+       int yy_fill_buffer;
+
+       int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+       /* When an EOF's been seen but there's still some text to process
+        * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+        * shouldn't try reading from the input source any more.  We might
+        * still have a bunch of tokens to match, though, because of
+        * possible backing-up.
+        *
+        * When we actually see the EOF, we change the status to "new"
+        * (via yyrestart()), so that the user can continue scanning by
+        * just pointing yyin at a new input file.
+        */
+#define YY_BUFFER_EOF_PENDING 2
+
+       };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;         /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0;                /* whether we need to initialize */
+static int yy_start = 0;       /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file  );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size  );
+void yy_delete_buffer (YY_BUFFER_STATE b  );
+void yy_flush_buffer (YY_BUFFER_STATE b  );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer  );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len  );
+
+void *yyalloc (yy_size_t  );
+void *yyrealloc (void *,yy_size_t  );
+void yyfree (void *  );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+       }
+
+#define yy_set_bol(at_bol) \
+       { \
+       if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (); \
+               YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer(yyin,YY_BUF_SIZE ); \
+       } \
+       YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+       }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[]  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+       (yytext_ptr) = yy_bp; \
+       yyleng = (size_t) (yy_cp - yy_bp); \
+       (yy_hold_char) = *yy_cp; \
+       *yy_cp = '\0'; \
+       (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 9
+#define YY_END_OF_BUFFER 10
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+       {
+       flex_int32_t yy_verify;
+       flex_int32_t yy_nxt;
+       };
+static yyconst flex_int16_t yy_accept[36] =
+    {   0,
+        0,    0,   10,    8,    1,    7,    8,    8,    6,    3,
+        6,    0,    4,    0,    2,    6,    3,    6,    3,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    5,    0
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    1,    4,    5,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    6,    1,    1,    7,    7,    7,
+        7,    7,    7,    7,    7,    7,    7,    8,    1,    1,
+        1,    1,    1,    1,    9,    9,    9,    9,    9,    9,
+        9,    9,    9,    9,    9,    9,    9,    9,    9,    9,
+        9,    9,    9,    9,    9,    9,    9,    9,    9,    9,
+        1,    1,    1,    1,    6,    1,    9,    9,    9,    9,
+
+        9,    9,    9,    9,    9,    9,    9,    9,    9,    9,
+        9,    9,    9,    9,    9,    9,    9,    9,    9,    9,
+        9,    9,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst flex_int32_t yy_meta[10] =
+    {   0,
+        1,    1,    2,    1,    1,    3,    4,    1,    4
+    } ;
+
+static yyconst flex_int16_t yy_base[49] =
+    {   0,
+        0,    0,   46,   47,   47,   47,   41,   41,    0,    4,
+       36,   38,   37,   37,   47,    0,    7,   31,   31,    0,
+        0,   29,    0,    0,   28,    0,    0,   27,    0,    0,
+       26,    0,    0,   47,   47,   15,   19,   21,   29,   28,
+       27,   26,   25,   24,   23,   22,   13,    8
+    } ;
+
+static yyconst flex_int16_t yy_def[49] =
+    {   0,
+       35,    1,   35,   35,   35,   35,   36,   37,   38,   35,
+       10,   36,   36,   37,   35,   38,   38,   38,   38,   39,
+       40,   35,   41,   42,   35,   43,   44,   35,   45,   46,
+       35,   47,   48,   35,    0,   35,   35,   35,   35,   35,
+       35,   35,   35,   35,   35,   35,   35,   35
+    } ;
+
+static yyconst flex_int16_t yy_nxt[57] =
+    {   0,
+        4,    5,    6,    7,    8,    9,   10,    4,   11,   16,
+       17,   34,   18,   19,   20,   12,   33,   12,   12,   14,
+       14,   14,   14,   16,   16,   31,   30,   28,   27,   25,
+       24,   22,   21,   32,   29,   26,   23,   19,   20,   15,
+       13,   13,   18,   15,   13,   35,    3,   35,   35,   35,
+       35,   35,   35,   35,   35,   35
+    } ;
+
+static yyconst flex_int16_t yy_chk[57] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,   10,
+       10,   48,   10,   17,   17,   36,   47,   36,   36,   37,
+       37,   37,   37,   38,   38,   46,   45,   44,   43,   42,
+       41,   40,   39,   31,   28,   25,   22,   19,   18,   14,
+       13,   12,   11,    8,    7,    3,   35,   35,   35,   35,
+       35,   35,   35,   35,   35,   35
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "lexer.l"
+#line 2 "lexer.l"
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Nasty workaround, but flex defines isatty() twice */
+#define _UNISTD_H
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+#include "parser.h"
+
+int yylex(void);
+
+#define YY_NO_INPUT
+
+#define ECHO {;}
+#define YY_DECL int yylex(void)
+
+int yyerror(char *str);
+
+#line 529 "tools/lexer.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (void );
+
+int yyget_debug (void );
+
+void yyset_debug (int debug_flag  );
+
+YY_EXTRA_TYPE yyget_extra (void );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined  );
+
+FILE *yyget_in (void );
+
+void yyset_in  (FILE * in_str  );
+
+FILE *yyget_out (void );
+
+void yyset_out  (FILE * out_str  );
+
+int yyget_leng (void );
+
+char *yyget_text (void );
+
+int yyget_lineno (void );
+
+void yyset_lineno (int line_number  );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+               { \
+               int c = '*'; \
+               size_t n; \
+               for ( n = 0; n < max_size && \
+                            (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+                       buf[n] = (char) c; \
+               if ( c == '\n' ) \
+                       buf[n++] = (char) c; \
+               if ( c == EOF && ferror( yyin ) ) \
+                       YY_FATAL_ERROR( "input in flex scanner failed" ); \
+               result = n; \
+               } \
+       else \
+               { \
+               errno=0; \
+               while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+                       { \
+                       if( errno != EINTR) \
+                               { \
+                               YY_FATAL_ERROR( "input in flex scanner failed" ); \
+                               break; \
+                               } \
+                       errno=0; \
+                       clearerr(yyin); \
+                       } \
+               }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+       YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp, *yy_bp;
+       register int yy_act;
+    
+#line 64 "lexer.l"
+
+
+#line 717 "tools/lexer.c"
+
+       if ( !(yy_init) )
+               {
+               (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+               YY_USER_INIT;
+#endif
+
+               if ( ! (yy_start) )
+                       (yy_start) = 1; /* first start state */
+
+               if ( ! yyin )
+                       yyin = stdin;
+
+               if ( ! yyout )
+                       yyout = stdout;
+
+               if ( ! YY_CURRENT_BUFFER ) {
+                       yyensure_buffer_stack ();
+                       YY_CURRENT_BUFFER_LVALUE =
+                               yy_create_buffer(yyin,YY_BUF_SIZE );
+               }
+
+               yy_load_buffer_state( );
+               }
+
+       while ( 1 )             /* loops until end-of-file is reached */
+               {
+               yy_cp = (yy_c_buf_p);
+
+               /* Support of yytext. */
+               *yy_cp = (yy_hold_char);
+
+               /* yy_bp points to the position in yy_ch_buf of the start of
+                * the current run.
+                */
+               yy_bp = yy_cp;
+
+               yy_current_state = (yy_start);
+yy_match:
+               do
+                       {
+                       register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+                       if ( yy_accept[yy_current_state] )
+                               {
+                               (yy_last_accepting_state) = yy_current_state;
+                               (yy_last_accepting_cpos) = yy_cp;
+                               }
+                       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+                               {
+                               yy_current_state = (int) yy_def[yy_current_state];
+                               if ( yy_current_state >= 36 )
+                                       yy_c = yy_meta[(unsigned int) yy_c];
+                               }
+                       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+                       ++yy_cp;
+                       }
+               while ( yy_base[yy_current_state] != 47 );
+
+yy_find_action:
+               yy_act = yy_accept[yy_current_state];
+               if ( yy_act == 0 )
+                       { /* have to back up */
+                       yy_cp = (yy_last_accepting_cpos);
+                       yy_current_state = (yy_last_accepting_state);
+                       yy_act = yy_accept[yy_current_state];
+                       }
+
+               YY_DO_BEFORE_ACTION;
+
+do_action:     /* This label is used only to access EOF actions. */
+
+               switch ( yy_act )
+       { /* beginning of action switch */
+                       case 0: /* must back up */
+                       /* undo the effects of YY_DO_BEFORE_ACTION */
+                       *yy_cp = (yy_hold_char);
+                       yy_cp = (yy_last_accepting_cpos);
+                       yy_current_state = (yy_last_accepting_state);
+                       goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 66 "lexer.l"
+{
+                       /* Skip spaces and tabs */
+                       ;
+               }
+       YY_BREAK
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+#line 71 "lexer.l"
+{
+                       /* Skip comments */
+                       lineno++; 
+               }
+       YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 76 "lexer.l"
+{
+                       yylval.number = atoi(yytext);
+                       return NUMBER;
+               }
+       YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 81 "lexer.l"
+{
+                       yylval.string = yytext;
+                       return STRING;
+               }
+       YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 86 "lexer.l"
+{
+                       bdaddr_t *ba = malloc(sizeof(bdaddr_t));
+                       str2ba(yytext, ba);
+                       yylval.bdaddr = ba;
+                       return BDADDR;
+               }
+       YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 93 "lexer.l"
+{
+                       int keyword = rfcomm_find_keyword(rfcomm_keyword, yytext);
+                       if (keyword != -1)
+                               return keyword;
+
+                       if (strncmp(yytext, "rfcomm", 6) == 0) {
+                               yylval.number = atoi(yytext + 6);
+                               return RFCOMM;
+                       }
+
+                       yylval.string = yytext;
+                       return WORD;
+               }
+       YY_BREAK
+case 7:
+/* rule 7 can match eol */
+YY_RULE_SETUP
+#line 107 "lexer.l"
+{
+                       lineno++;
+               }
+       YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 111 "lexer.l"
+{
+                       return *yytext;
+               }
+       YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 115 "lexer.l"
+ECHO;
+       YY_BREAK
+#line 880 "tools/lexer.c"
+case YY_STATE_EOF(INITIAL):
+       yyterminate();
+
+       case YY_END_OF_BUFFER:
+               {
+               /* Amount of text matched not including the EOB char. */
+               int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+               /* Undo the effects of YY_DO_BEFORE_ACTION. */
+               *yy_cp = (yy_hold_char);
+               YY_RESTORE_YY_MORE_OFFSET
+
+               if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+                       {
+                       /* We're scanning a new file or input source.  It's
+                        * possible that this happened because the user
+                        * just pointed yyin at a new source and called
+                        * yylex().  If so, then we have to assure
+                        * consistency between YY_CURRENT_BUFFER and our
+                        * globals.  Here is the right place to do so, because
+                        * this is the first action (other than possibly a
+                        * back-up) that will match for the new input source.
+                        */
+                       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+                       YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+                       }
+
+               /* Note that here we test for yy_c_buf_p "<=" to the position
+                * of the first EOB in the buffer, since yy_c_buf_p will
+                * already have been incremented past the NUL character
+                * (since all states make transitions on EOB to the
+                * end-of-buffer state).  Contrast this with the test
+                * in input().
+                */
+               if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       { /* This was really a NUL. */
+                       yy_state_type yy_next_state;
+
+                       (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+                       yy_current_state = yy_get_previous_state(  );
+
+                       /* Okay, we're now positioned to make the NUL
+                        * transition.  We couldn't have
+                        * yy_get_previous_state() go ahead and do it
+                        * for us because it doesn't know how to deal
+                        * with the possibility of jamming (and we don't
+                        * want to build jamming into it because then it
+                        * will run more slowly).
+                        */
+
+                       yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+                       yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+                       if ( yy_next_state )
+                               {
+                               /* Consume the NUL. */
+                               yy_cp = ++(yy_c_buf_p);
+                               yy_current_state = yy_next_state;
+                               goto yy_match;
+                               }
+
+                       else
+                               {
+                               yy_cp = (yy_c_buf_p);
+                               goto yy_find_action;
+                               }
+                       }
+
+               else switch ( yy_get_next_buffer(  ) )
+                       {
+                       case EOB_ACT_END_OF_FILE:
+                               {
+                               (yy_did_buffer_switch_on_eof) = 0;
+
+                               if ( yywrap( ) )
+                                       {
+                                       /* Note: because we've taken care in
+                                        * yy_get_next_buffer() to have set up
+                                        * yytext, we can now set up
+                                        * yy_c_buf_p so that if some total
+                                        * hoser (like flex itself) wants to
+                                        * call the scanner after we return the
+                                        * YY_NULL, it'll still work - another
+                                        * YY_NULL will get returned.
+                                        */
+                                       (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+                                       yy_act = YY_STATE_EOF(YY_START);
+                                       goto do_action;
+                                       }
+
+                               else
+                                       {
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+                                       }
+                               break;
+                               }
+
+                       case EOB_ACT_CONTINUE_SCAN:
+                               (yy_c_buf_p) =
+                                       (yytext_ptr) + yy_amount_of_matched_text;
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_match;
+
+                       case EOB_ACT_LAST_MATCH:
+                               (yy_c_buf_p) =
+                               &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+                               yy_current_state = yy_get_previous_state(  );
+
+                               yy_cp = (yy_c_buf_p);
+                               yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+                               goto yy_find_action;
+                       }
+               break;
+               }
+
+       default:
+               YY_FATAL_ERROR(
+                       "fatal flex scanner internal error--no action found" );
+       } /* end of action switch */
+               } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *     EOB_ACT_LAST_MATCH -
+ *     EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *     EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+       register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+       register char *source = (yytext_ptr);
+       register int number_to_move, i;
+       int ret_val;
+
+       if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+               YY_FATAL_ERROR(
+               "fatal flex scanner internal error--end of buffer missed" );
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+               { /* Don't try to fill the buffer, so this is an EOF. */
+               if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+                       {
+                       /* We matched a single character, the EOB, so
+                        * treat this as a final EOF.
+                        */
+                       return EOB_ACT_END_OF_FILE;
+                       }
+
+               else
+                       {
+                       /* We matched some text prior to the EOB, first
+                        * process it.
+                        */
+                       return EOB_ACT_LAST_MATCH;
+                       }
+               }
+
+       /* Try to read more data. */
+
+       /* First move last chars to start of buffer. */
+       number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+       for ( i = 0; i < number_to_move; ++i )
+               *(dest++) = *(source++);
+
+       if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+               /* don't do the read, it's not guaranteed to return an EOF,
+                * just force an EOF
+                */
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+       else
+               {
+                       int num_to_read =
+                       YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+               while ( num_to_read <= 0 )
+                       { /* Not enough room in the buffer - grow it. */
+
+                       /* just a shorter name for the current buffer */
+                       YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+                       int yy_c_buf_p_offset =
+                               (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+                       if ( b->yy_is_our_buffer )
+                               {
+                               int new_size = b->yy_buf_size * 2;
+
+                               if ( new_size <= 0 )
+                                       b->yy_buf_size += b->yy_buf_size / 8;
+                               else
+                                       b->yy_buf_size *= 2;
+
+                               b->yy_ch_buf = (char *)
+                                       /* Include room in for 2 EOB chars. */
+                                       yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+                               }
+                       else
+                               /* Can't grow it, we don't own it. */
+                               b->yy_ch_buf = 0;
+
+                       if ( ! b->yy_ch_buf )
+                               YY_FATAL_ERROR(
+                               "fatal error - scanner input buffer overflow" );
+
+                       (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+                       num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+                                               number_to_move - 1;
+
+                       }
+
+               if ( num_to_read > YY_READ_BUF_SIZE )
+                       num_to_read = YY_READ_BUF_SIZE;
+
+               /* Read in more data. */
+               YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+                       (yy_n_chars), (size_t) num_to_read );
+
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       if ( (yy_n_chars) == 0 )
+               {
+               if ( number_to_move == YY_MORE_ADJ )
+                       {
+                       ret_val = EOB_ACT_END_OF_FILE;
+                       yyrestart(yyin  );
+                       }
+
+               else
+                       {
+                       ret_val = EOB_ACT_LAST_MATCH;
+                       YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+                               YY_BUFFER_EOF_PENDING;
+                       }
+               }
+
+       else
+               ret_val = EOB_ACT_CONTINUE_SCAN;
+
+       if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+               /* Extend the array by 50%, plus the number we really need. */
+               yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+               YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size  );
+               if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+                       YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+       }
+
+       (yy_n_chars) += number_to_move;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+       YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+       (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+       return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+       register yy_state_type yy_current_state;
+       register char *yy_cp;
+    
+       yy_current_state = (yy_start);
+
+       for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+               {
+               register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+               if ( yy_accept[yy_current_state] )
+                       {
+                       (yy_last_accepting_state) = yy_current_state;
+                       (yy_last_accepting_cpos) = yy_cp;
+                       }
+               while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+                       {
+                       yy_current_state = (int) yy_def[yy_current_state];
+                       if ( yy_current_state >= 36 )
+                               yy_c = yy_meta[(unsigned int) yy_c];
+                       }
+               yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+               }
+
+       return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *     next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+       register int yy_is_jam;
+       register char *yy_cp = (yy_c_buf_p);
+
+       register YY_CHAR yy_c = 1;
+       if ( yy_accept[yy_current_state] )
+               {
+               (yy_last_accepting_state) = yy_current_state;
+               (yy_last_accepting_cpos) = yy_cp;
+               }
+       while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+               {
+               yy_current_state = (int) yy_def[yy_current_state];
+               if ( yy_current_state >= 36 )
+                       yy_c = yy_meta[(unsigned int) yy_c];
+               }
+       yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+       yy_is_jam = (yy_current_state == 35);
+
+       return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+       int c;
+    
+       *(yy_c_buf_p) = (yy_hold_char);
+
+       if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+               {
+               /* yy_c_buf_p now points to the character we want to return.
+                * If this occurs *before* the EOB characters, then it's a
+                * valid NUL; if not, then we've hit the end of the buffer.
+                */
+               if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+                       /* This was really a NUL. */
+                       *(yy_c_buf_p) = '\0';
+
+               else
+                       { /* need more input */
+                       int offset = (yy_c_buf_p) - (yytext_ptr);
+                       ++(yy_c_buf_p);
+
+                       switch ( yy_get_next_buffer(  ) )
+                               {
+                               case EOB_ACT_LAST_MATCH:
+                                       /* This happens because yy_g_n_b()
+                                        * sees that we've accumulated a
+                                        * token and flags that we need to
+                                        * try matching the token before
+                                        * proceeding.  But for input(),
+                                        * there's no matching to consider.
+                                        * So convert the EOB_ACT_LAST_MATCH
+                                        * to EOB_ACT_END_OF_FILE.
+                                        */
+
+                                       /* Reset buffer status. */
+                                       yyrestart(yyin );
+
+                                       /*FALLTHROUGH*/
+
+                               case EOB_ACT_END_OF_FILE:
+                                       {
+                                       if ( yywrap( ) )
+                                               return EOF;
+
+                                       if ( ! (yy_did_buffer_switch_on_eof) )
+                                               YY_NEW_FILE;
+#ifdef __cplusplus
+                                       return yyinput();
+#else
+                                       return input();
+#endif
+                                       }
+
+                               case EOB_ACT_CONTINUE_SCAN:
+                                       (yy_c_buf_p) = (yytext_ptr) + offset;
+                                       break;
+                               }
+                       }
+               }
+
+       c = *(unsigned char *) (yy_c_buf_p);    /* cast for 8-bit char's */
+       *(yy_c_buf_p) = '\0';   /* preserve yytext */
+       (yy_hold_char) = *++(yy_c_buf_p);
+
+       return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void yyrestart  (FILE * input_file )
+{
+    
+       if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack ();
+               YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer(yyin,YY_BUF_SIZE );
+       }
+
+       yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+       yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+    
+       /* TODO. We should be able to replace this entire function body
+        * with
+        *              yypop_buffer_state();
+        *              yypush_buffer_state(new_buffer);
+     */
+       yyensure_buffer_stack ();
+       if ( YY_CURRENT_BUFFER == new_buffer )
+               return;
+
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+       yy_load_buffer_state( );
+
+       /* We don't actually know whether we did this switch during
+        * EOF (yywrap()) processing, but the only time this flag
+        * is looked at is after yywrap() is called, so it's safe
+        * to go ahead and always set it.
+        */
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state  (void)
+{
+       (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+       (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+       yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+       (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
+{
+       YY_BUFFER_STATE b;
+    
+       b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+       b->yy_buf_size = size;
+
+       /* yy_ch_buf has to be 2 characters longer than the size given because
+        * we need to put in 2 end-of-buffer characters.
+        */
+       b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2  );
+       if ( ! b->yy_ch_buf )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+       b->yy_is_our_buffer = 1;
+
+       yy_init_buffer(b,file );
+
+       return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * 
+ */
+    void yy_delete_buffer (YY_BUFFER_STATE  b )
+{
+    
+       if ( ! b )
+               return;
+
+       if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+               YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+       if ( b->yy_is_our_buffer )
+               yyfree((void *) b->yy_ch_buf  );
+
+       yyfree((void *) b  );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+    
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+       int oerrno = errno;
+    
+       yy_flush_buffer(b );
+
+       b->yy_input_file = file;
+       b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+    
+       errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+    void yy_flush_buffer (YY_BUFFER_STATE  b )
+{
+       if ( ! b )
+               return;
+
+       b->yy_n_chars = 0;
+
+       /* We always need two end-of-buffer characters.  The first causes
+        * a transition to the end-of-buffer state.  The second causes
+        * a jam in that state.
+        */
+       b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+       b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+       b->yy_buf_pos = &b->yy_ch_buf[0];
+
+       b->yy_at_bol = 1;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       if ( b == YY_CURRENT_BUFFER )
+               yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+       if (new_buffer == NULL)
+               return;
+
+       yyensure_buffer_stack();
+
+       /* This block is copied from yy_switch_to_buffer. */
+       if ( YY_CURRENT_BUFFER )
+               {
+               /* Flush out information for old buffer. */
+               *(yy_c_buf_p) = (yy_hold_char);
+               YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+               YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+               }
+
+       /* Only push if top exists. Otherwise, replace top. */
+       if (YY_CURRENT_BUFFER)
+               (yy_buffer_stack_top)++;
+       YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+       /* copied from yy_switch_to_buffer. */
+       yy_load_buffer_state( );
+       (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+void yypop_buffer_state (void)
+{
+       if (!YY_CURRENT_BUFFER)
+               return;
+
+       yy_delete_buffer(YY_CURRENT_BUFFER );
+       YY_CURRENT_BUFFER_LVALUE = NULL;
+       if ((yy_buffer_stack_top) > 0)
+               --(yy_buffer_stack_top);
+
+       if (YY_CURRENT_BUFFER) {
+               yy_load_buffer_state( );
+               (yy_did_buffer_switch_on_eof) = 1;
+       }
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+       int num_to_alloc;
+    
+       if (!(yy_buffer_stack)) {
+
+               /* First allocation is just for 2 elements, since we don't know if this
+                * scanner will even need a stack. We use 2 instead of 1 to avoid an
+                * immediate realloc on the next call.
+         */
+               num_to_alloc = 1;
+               (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+                                                               (num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+               if ( ! (yy_buffer_stack) )
+                       YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+                                                                 
+               memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+                               
+               (yy_buffer_stack_max) = num_to_alloc;
+               (yy_buffer_stack_top) = 0;
+               return;
+       }
+
+       if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+               /* Increase the buffer to prepare for a possible push. */
+               int grow_size = 8 /* arbitrary grow size */;
+
+               num_to_alloc = (yy_buffer_stack_max) + grow_size;
+               (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+                                                               ((yy_buffer_stack),
+                                                               num_to_alloc * sizeof(struct yy_buffer_state*)
+                                                               );
+               if ( ! (yy_buffer_stack) )
+                       YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+               /* zero only the new slots.*/
+               memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+               (yy_buffer_stack_max) = num_to_alloc;
+       }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object. 
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
+{
+       YY_BUFFER_STATE b;
+    
+       if ( size < 2 ||
+            base[size-2] != YY_END_OF_BUFFER_CHAR ||
+            base[size-1] != YY_END_OF_BUFFER_CHAR )
+               /* They forgot to leave room for the EOB's. */
+               return 0;
+
+       b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state )  );
+       if ( ! b )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+       b->yy_buf_size = size - 2;      /* "- 2" to take care of EOB's */
+       b->yy_buf_pos = b->yy_ch_buf = base;
+       b->yy_is_our_buffer = 0;
+       b->yy_input_file = 0;
+       b->yy_n_chars = b->yy_buf_size;
+       b->yy_is_interactive = 0;
+       b->yy_at_bol = 1;
+       b->yy_fill_buffer = 0;
+       b->yy_buffer_status = YY_BUFFER_NEW;
+
+       yy_switch_to_buffer(b  );
+
+       return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+    
+       return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (yyconst char * yybytes, int  _yybytes_len )
+{
+       YY_BUFFER_STATE b;
+       char *buf;
+       yy_size_t n;
+       int i;
+    
+       /* Get memory for full buffer, including space for trailing EOB's. */
+       n = _yybytes_len + 2;
+       buf = (char *) yyalloc(n  );
+       if ( ! buf )
+               YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+       for ( i = 0; i < _yybytes_len; ++i )
+               buf[i] = yybytes[i];
+
+       buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+       b = yy_scan_buffer(buf,n );
+       if ( ! b )
+               YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+       /* It's okay to grow etc. this buffer, and we should throw it
+        * away when we're done.
+        */
+       b->yy_is_our_buffer = 1;
+
+       return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+       (void) fprintf( stderr, "%s\n", msg );
+       exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+       do \
+               { \
+               /* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+               yytext[yyleng] = (yy_hold_char); \
+               (yy_c_buf_p) = yytext + yyless_macro_arg; \
+               (yy_hold_char) = *(yy_c_buf_p); \
+               *(yy_c_buf_p) = '\0'; \
+               yyleng = yyless_macro_arg; \
+               } \
+       while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ * 
+ */
+int yyget_lineno  (void)
+{
+        
+    return yylineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *yyget_in  (void)
+{
+        return yyin;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *yyget_out  (void)
+{
+        return yyout;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int yyget_leng  (void)
+{
+        return yyleng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *yyget_text  (void)
+{
+        return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * 
+ */
+void yyset_lineno (int  line_number )
+{
+    
+    yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * 
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  in_str )
+{
+        yyin = in_str ;
+}
+
+void yyset_out (FILE *  out_str )
+{
+        yyout = out_str ;
+}
+
+int yyget_debug  (void)
+{
+        return yy_flex_debug;
+}
+
+void yyset_debug (int  bdebug )
+{
+        yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+        /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from yylex_destroy(), so don't allocate here.
+     */
+
+    (yy_buffer_stack) = 0;
+    (yy_buffer_stack_top) = 0;
+    (yy_buffer_stack_max) = 0;
+    (yy_c_buf_p) = (char *) 0;
+    (yy_init) = 0;
+    (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = (FILE *) 0;
+    yyout = (FILE *) 0;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * yylex_init()
+     */
+    return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+       while(YY_CURRENT_BUFFER){
+               yy_delete_buffer(YY_CURRENT_BUFFER  );
+               YY_CURRENT_BUFFER_LVALUE = NULL;
+               yypop_buffer_state();
+       }
+
+       /* Destroy the stack itself. */
+       yyfree((yy_buffer_stack) );
+       (yy_buffer_stack) = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * yylex() is called, initialization will occur. */
+    yy_init_globals( );
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+       register int i;
+       for ( i = 0; i < n; ++i )
+               s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+       register int n;
+       for ( n = 0; s[n]; ++n )
+               ;
+
+       return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size )
+{
+       return (void *) malloc( size );
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size )
+{
+       /* The cast to (char *) in the following accommodates both
+        * implementations that use char* generic pointers, and those
+        * that use void* generic pointers.  It works with the latter
+        * because both ANSI C and C++ allow castless assignment from
+        * any pointer type to void*, and deal with argument conversions
+        * as though doing an assignment.
+        */
+       return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+       free( (char *) ptr );   /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 115 "lexer.l"
+
+
+
+int yywrap(void) 
+{
+       return 1;
+}
+
diff --git a/tools/lexer.l b/tools/lexer.l
new file mode 100644 (file)
index 0000000..ff9ce81
--- /dev/null
@@ -0,0 +1,120 @@
+%{
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Nasty workaround, but flex defines isatty() twice */
+#define _UNISTD_H
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+#include "parser.h"
+
+int yylex(void);
+
+#define YY_NO_INPUT
+
+#define ECHO {;}
+#define YY_DECL int yylex(void)
+
+int yyerror(char *str);
+
+%}
+
+%option nounput
+
+space          [ \t]
+linebreak      \n
+comment                \#.*\n
+keyword                [A-Za-z0-9\_\-]+
+
+number         [0-9]+
+string         \".*\"
+bdaddr         [A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}:[A-Za-z0-9]{2}
+
+%%
+
+{space}                {
+                       /* Skip spaces and tabs */
+                       ;
+               }
+
+{comment}      {
+                       /* Skip comments */
+                       lineno++; 
+               }
+
+{number}       {
+                       yylval.number = atoi(yytext);
+                       return NUMBER;
+               }
+
+{string}       {
+                       yylval.string = yytext;
+                       return STRING;
+               }
+
+{bdaddr}       {
+                       bdaddr_t *ba = malloc(sizeof(bdaddr_t));
+                       str2ba(yytext, ba);
+                       yylval.bdaddr = ba;
+                       return BDADDR;
+               }
+
+{keyword}      {
+                       int keyword = rfcomm_find_keyword(rfcomm_keyword, yytext);
+                       if (keyword != -1)
+                               return keyword;
+
+                       if (strncmp(yytext, "rfcomm", 6) == 0) {
+                               yylval.number = atoi(yytext + 6);
+                               return RFCOMM;
+                       }
+
+                       yylval.string = yytext;
+                       return WORD;
+               }
+
+{linebreak}    {
+                       lineno++;
+               }
+
+.              {
+                       return *yytext;
+               }
+
+%%
+
+int yywrap(void) 
+{
+       return 1;
+}
diff --git a/tools/parser.c b/tools/parser.c
new file mode 100644 (file)
index 0000000..5733ab7
--- /dev/null
@@ -0,0 +1,1817 @@
+/* A Bison parser, made by GNU Bison 2.5.  */
+
+/* Bison implementation for Yacc-like parsers in C
+   
+      Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+   
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+   
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.5"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+
+
+/* Copy the first part of user declarations.  */
+
+/* Line 268 of yacc.c  */
+#line 1 "parser.y"
+
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+
+int yylex(void);
+int yyerror(char *s); 
+
+struct rfcomm_opts *opts;
+
+
+
+/* Line 268 of yacc.c  */
+#line 120 "tools/parser.c"
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     K_BIND = 258,
+     K_DEVICE = 259,
+     K_CHANNEL = 260,
+     K_COMMENT = 261,
+     K_YES = 262,
+     K_NO = 263,
+     NUMBER = 264,
+     RFCOMM = 265,
+     STRING = 266,
+     WORD = 267,
+     BDADDR = 268
+   };
+#endif
+/* Tokens.  */
+#define K_BIND 258
+#define K_DEVICE 259
+#define K_CHANNEL 260
+#define K_COMMENT 261
+#define K_YES 262
+#define K_NO 263
+#define NUMBER 264
+#define RFCOMM 265
+#define STRING 266
+#define WORD 267
+#define BDADDR 268
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 293 of yacc.c  */
+#line 49 "parser.y"
+
+       int number;
+       char *string;
+       bdaddr_t *bdaddr;
+
+
+
+/* Line 293 of yacc.c  */
+#line 190 "tools/parser.c"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 343 of yacc.c  */
+#line 202 "tools/parser.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int yyi)
+#else
+static int
+YYID (yyi)
+    int yyi;
+#endif
+{
+  return yyi;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+            && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+        || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)                          \
+    do                                                                 \
+      {                                                                        \
+       YYSIZE_T yynewbytes;                                            \
+       YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
+       Stack = &yyptr->Stack_alloc;                                    \
+       yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+       yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                        \
+    while (YYID (0))
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)             \
+      do                                       \
+       {                                       \
+         YYSIZE_T yyi;                         \
+         for (yyi = 0; yyi < (Count); yyi++)   \
+           (To)[yyi] = (From)[yyi];            \
+       }                                       \
+      while (YYID (0))
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  8
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   40
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  17
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  8
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  20
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  33
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   268
+
+#define YYTRANSLATE(YYX)                                               \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,    16,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,    14,     2,    15,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint8 yyprhs[] =
+{
+       0,     0,     3,     4,     6,     9,    14,    19,    21,    23,
+      25,    27,    30,    33,    37,    40,    43,    46,    49,    51,
+      53
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int8 yyrhs[] =
+{
+      18,     0,    -1,    -1,    19,    -1,    18,    19,    -1,    20,
+      14,    22,    15,    -1,    21,    14,    22,    15,    -1,    12,
+      -1,     1,    -1,    12,    -1,    10,    -1,    23,    16,    -1,
+       1,    16,    -1,    22,    23,    16,    -1,     3,    24,    -1,
+       4,    13,    -1,     5,     9,    -1,     6,    11,    -1,    12,
+      -1,     7,    -1,     8,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint8 yyrline[] =
+{
+       0,    66,    66,    67,    68,    71,    72,    73,    76,    83,
+      89,    98,    99,   100,   103,   108,   113,   118,   123,   129,
+     130
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "K_BIND", "K_DEVICE", "K_CHANNEL",
+  "K_COMMENT", "K_YES", "K_NO", "NUMBER", "RFCOMM", "STRING", "WORD",
+  "BDADDR", "'{'", "'}'", "';'", "$accept", "config", "statement",
+  "section", "rfcomm", "rfcomm_options", "rfcomm_option", "bool", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   123,   125,    59
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    17,    18,    18,    18,    19,    19,    19,    19,    20,
+      21,    22,    22,    22,    23,    23,    23,    23,    23,    24,
+      24
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     0,     1,     2,     4,     4,     1,     1,     1,
+       1,     2,     2,     3,     2,     2,     2,     2,     1,     1,
+       1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM.
+   Performed when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       0,     8,    10,     7,     0,     3,     0,     0,     1,     4,
+       0,     0,     0,     0,     0,     0,     0,    18,     0,     0,
+       0,    12,    19,    20,    14,    15,    16,    17,     5,     0,
+      11,     6,    13
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,     4,     5,     6,     7,    18,    19,    24
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -7
+static const yytype_int8 yypact[] =
+{
+      10,    -7,    -7,    -6,    14,    -7,     4,     7,    -7,    -7,
+      24,    24,    15,    25,    21,    26,    12,    -7,    -3,    22,
+       1,    -7,    -7,    -7,    -7,    -7,    -7,    -7,    -7,    23,
+      -7,    -7,    -7
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+      -7,    -7,    33,    -7,    -7,    29,    -1,    -7
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -10
+static const yytype_int8 yytable[] =
+{
+      13,    14,    15,    16,    13,    14,    15,    16,    -9,    17,
+      -2,     1,    28,    17,     8,     1,    31,    29,    10,    29,
+       2,    11,     3,    27,     2,    12,     3,    13,    14,    15,
+      16,    21,    22,    23,    25,    26,    17,     9,    30,    32,
+      20
+};
+
+#define yypact_value_is_default(yystate) \
+  ((yystate) == (-7))
+
+#define yytable_value_is_error(yytable_value) \
+  YYID (0)
+
+static const yytype_uint8 yycheck[] =
+{
+       3,     4,     5,     6,     3,     4,     5,     6,    14,    12,
+       0,     1,    15,    12,     0,     1,    15,    18,    14,    20,
+      10,    14,    12,    11,    10,     1,    12,     3,     4,     5,
+       6,    16,     7,     8,    13,     9,    12,     4,    16,    16,
+      11
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,     1,    10,    12,    18,    19,    20,    21,     0,    19,
+      14,    14,     1,     3,     4,     5,     6,    12,    22,    23,
+      22,    16,     7,     8,    24,    13,     9,    11,    15,    23,
+      16,    15,    16
+};
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                (-2)
+#define YYEOF          0
+
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT                goto yyabortlab
+#define YYERROR                goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  However,
+   YYFAIL appears to be in use.  Nevertheless, it is formally deprecated
+   in Bison 2.4.2's NEWS entry, where a plan to phase it out is
+   discussed.  */
+
+#define YYFAIL         goto yyerrlab
+#if defined YYFAIL
+  /* This is here to suppress warnings from the GCC cpp's
+     -Wunused-macros.  Normally we don't worry about that warning, but
+     some users do, and we want to make it easy for users to remove
+     YYFAIL uses, which will produce warnings from Bison 2.5.  */
+#endif
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                 \
+do                                                             \
+  if (yychar == YYEMPTY && yylen == 1)                         \
+    {                                                          \
+      yychar = (Token);                                                \
+      yylval = (Value);                                                \
+      YYPOPSTACK (1);                                          \
+      goto yybackup;                                           \
+    }                                                          \
+  else                                                         \
+    {                                                          \
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;                                                 \
+    }                                                          \
+while (YYID (0))
+
+
+#define YYTERROR       1
+#define YYERRCODE      256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)                               \
+    do                                                                 \
+      if (YYID (N))                                                    \
+       {                                                               \
+         (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;        \
+         (Current).first_column = YYRHSLOC (Rhs, 1).first_column;      \
+         (Current).last_line    = YYRHSLOC (Rhs, N).last_line;         \
+         (Current).last_column  = YYRHSLOC (Rhs, N).last_column;       \
+       }                                                               \
+      else                                                             \
+       {                                                               \
+         (Current).first_line   = (Current).last_line   =              \
+           YYRHSLOC (Rhs, 0).last_line;                                \
+         (Current).first_column = (Current).last_column =              \
+           YYRHSLOC (Rhs, 0).last_column;                              \
+       }                                                               \
+    while (YYID (0))
+#endif
+
+
+/* This macro is provided for backward compatibility. */
+
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                       \
+do {                                           \
+  if (yydebug)                                 \
+    YYFPRINTF Args;                            \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                   \
+do {                                                                     \
+  if (yydebug)                                                           \
+    {                                                                    \
+      YYFPRINTF (stderr, "%s ", Title);                                          \
+      yy_symbol_print (stderr,                                           \
+                 Type, Value); \
+      YYFPRINTF (stderr, "\n");                                                  \
+    }                                                                    \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+       break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+#else
+static void
+yy_stack_print (yybottom, yytop)
+    yytype_int16 *yybottom;
+    yytype_int16 *yytop;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                           \
+do {                                                           \
+  if (yydebug)                                                 \
+    yy_stack_print ((Bottom), (Top));                          \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+    YYSTYPE *yyvsp;
+    int yyrule;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+            yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+                      &(yyvsp[(yyi + 1) - (yynrhs)])
+                                      );
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)         \
+do {                                   \
+  if (yydebug)                         \
+    yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef        YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+       switch (*++yyp)
+         {
+         case '\'':
+         case ',':
+           goto do_not_strip_quotes;
+
+         case '\\':
+           if (*++yyp != '\\')
+             goto do_not_strip_quotes;
+           /* Fall through.  */
+         default:
+           if (yyres)
+             yyres[yyn] = *yyp;
+           yyn++;
+           break;
+
+         case '"':
+           if (yyres)
+             yyres[yyn] = '\0';
+           return yyn;
+         }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+   about the unexpected token YYTOKEN for the state stack whose top is
+   YYSSP.
+
+   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
+   not large enough to hold the message.  In that case, also set
+   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
+   required number of bytes is too large to store.  */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+                yytype_int16 *yyssp, int yytoken)
+{
+  YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]);
+  YYSIZE_T yysize = yysize0;
+  YYSIZE_T yysize1;
+  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+  /* Internationalized format string. */
+  const char *yyformat = 0;
+  /* Arguments of yyformat. */
+  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+  /* Number of reported tokens (one for the "unexpected", one per
+     "expected"). */
+  int yycount = 0;
+
+  /* There are many possibilities here to consider:
+     - Assume YYFAIL is not used.  It's too flawed to consider.  See
+       <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html>
+       for details.  YYERROR is fine as it does not invoke this
+       function.
+     - If this state is a consistent state with a default action, then
+       the only way this function was invoked is if the default action
+       is an error action.  In that case, don't check for expected
+       tokens because there are none.
+     - The only way there can be no lookahead present (in yychar) is if
+       this state is a consistent state with a default action.  Thus,
+       detecting the absence of a lookahead is sufficient to determine
+       that there is no unexpected or expected token to report.  In that
+       case, just report a simple "syntax error".
+     - Don't assume there isn't a lookahead just because this state is a
+       consistent state with a default action.  There might have been a
+       previous inconsistent state, consistent state with a non-default
+       action, or user semantic action that manipulated yychar.
+     - Of course, the expected token list depends on states to have
+       correct lookahead information, and it depends on the parser not
+       to perform extra reductions after fetching a lookahead from the
+       scanner and before detecting a syntax error.  Thus, state merging
+       (from LALR or IELR) and default reductions corrupt the expected
+       token list.  However, the list is correct for canonical LR with
+       one exception: it will still contain any token that will not be
+       accepted due to an error action in a later state.
+  */
+  if (yytoken != YYEMPTY)
+    {
+      int yyn = yypact[*yyssp];
+      yyarg[yycount++] = yytname[yytoken];
+      if (!yypact_value_is_default (yyn))
+        {
+          /* Start YYX at -YYN if negative to avoid negative indexes in
+             YYCHECK.  In other words, skip the first -YYN actions for
+             this state because they are default actions.  */
+          int yyxbegin = yyn < 0 ? -yyn : 0;
+          /* Stay within bounds of both yycheck and yytname.  */
+          int yychecklim = YYLAST - yyn + 1;
+          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+          int yyx;
+
+          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+                && !yytable_value_is_error (yytable[yyx + yyn]))
+              {
+                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+                  {
+                    yycount = 1;
+                    yysize = yysize0;
+                    break;
+                  }
+                yyarg[yycount++] = yytname[yyx];
+                yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+                if (! (yysize <= yysize1
+                       && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                  return 2;
+                yysize = yysize1;
+              }
+        }
+    }
+
+  switch (yycount)
+    {
+# define YYCASE_(N, S)                      \
+      case N:                               \
+        yyformat = S;                       \
+      break
+      YYCASE_(0, YY_("syntax error"));
+      YYCASE_(1, YY_("syntax error, unexpected %s"));
+      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+    }
+
+  yysize1 = yysize + yystrlen (yyformat);
+  if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+    return 2;
+  yysize = yysize1;
+
+  if (*yymsg_alloc < yysize)
+    {
+      *yymsg_alloc = 2 * yysize;
+      if (! (yysize <= *yymsg_alloc
+             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+      return 1;
+    }
+
+  /* Avoid sprintf, as that infringes on the user's name space.
+     Don't have undefined behavior even if the translation
+     produced a string with the wrong number of "%s"s.  */
+  {
+    char *yyp = *yymsg;
+    int yyi = 0;
+    while ((*yyp = *yyformat) != '\0')
+      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+        {
+          yyp += yytnamerr (yyp, yyarg[yyi++]);
+          yyformat += 2;
+        }
+      else
+        {
+          yyp++;
+          yyformat++;
+        }
+  }
+  return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  YYUSE (yyvaluep);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+       break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+/* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+    int yystate;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus;
+
+    /* The stacks and their tools:
+       `yyss': related to states.
+       `yyvs': related to semantic values.
+
+       Refer to the stacks thru separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* The state stack.  */
+    yytype_int16 yyssa[YYINITDEPTH];
+    yytype_int16 *yyss;
+    yytype_int16 *yyssp;
+
+    /* The semantic value stack.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs;
+    YYSTYPE *yyvsp;
+
+    YYSIZE_T yystacksize;
+
+  int yyn;
+  int yyresult;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  yytoken = 0;
+  yyss = yyssa;
+  yyvs = yyvsa;
+  yystacksize = YYINITDEPTH;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY; /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+       /* Give user a chance to reallocate the stack.  Use copies of
+          these so that the &'s don't force the real ones into
+          memory.  */
+       YYSTYPE *yyvs1 = yyvs;
+       yytype_int16 *yyss1 = yyss;
+
+       /* Each stack pointer address is followed by the size of the
+          data in use in that stack, in bytes.  This used to be a
+          conditional around just the two extra args, but that might
+          be undefined if yyoverflow is a macro.  */
+       yyoverflow (YY_("memory exhausted"),
+                   &yyss1, yysize * sizeof (*yyssp),
+                   &yyvs1, yysize * sizeof (*yyvsp),
+                   &yystacksize);
+
+       yyss = yyss1;
+       yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+       goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+       yystacksize = YYMAXDEPTH;
+
+      {
+       yytype_int16 *yyss1 = yyss;
+       union yyalloc *yyptr =
+         (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+       if (! yyptr)
+         goto yyexhaustedlab;
+       YYSTACK_RELOCATE (yyss_alloc, yyss);
+       YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+       if (yyss1 != yyssa)
+         YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                 (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+       YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 7:
+
+/* Line 1806 of yacc.c  */
+#line 74 "parser.y"
+    {
+                       }
+    break;
+
+  case 8:
+
+/* Line 1806 of yacc.c  */
+#line 77 "parser.y"
+    {
+                               yyclearin;
+                               yyerrok;
+                       }
+    break;
+
+  case 9:
+
+/* Line 1806 of yacc.c  */
+#line 84 "parser.y"
+    {
+                               opts = NULL;
+                       }
+    break;
+
+  case 10:
+
+/* Line 1806 of yacc.c  */
+#line 90 "parser.y"
+    {
+                               if (((yyvsp[(1) - (1)].number) >= 0) && ((yyvsp[(1) - (1)].number) < RFCOMM_MAX_DEV))
+                                       opts = &rfcomm_opts[(yyvsp[(1) - (1)].number)];
+                               else
+                                       opts = NULL;
+                       }
+    break;
+
+  case 14:
+
+/* Line 1806 of yacc.c  */
+#line 104 "parser.y"
+    {
+                               if (opts)
+                                       opts->bind = (yyvsp[(2) - (2)].number);
+                       }
+    break;
+
+  case 15:
+
+/* Line 1806 of yacc.c  */
+#line 109 "parser.y"
+    {
+                               if (opts)
+                                       bacpy(&opts->bdaddr, (yyvsp[(2) - (2)].bdaddr));
+                       }
+    break;
+
+  case 16:
+
+/* Line 1806 of yacc.c  */
+#line 114 "parser.y"
+    {
+                               if (opts)
+                                       opts->channel = (yyvsp[(2) - (2)].number);
+                       }
+    break;
+
+  case 17:
+
+/* Line 1806 of yacc.c  */
+#line 119 "parser.y"
+    {
+                               if (opts)
+                                       snprintf(opts->comment, MAXCOMMENTLEN, "%s", (yyvsp[(2) - (2)].string));
+                       }
+    break;
+
+  case 18:
+
+/* Line 1806 of yacc.c  */
+#line 124 "parser.y"
+    {
+                               // Unknown option
+                       }
+    break;
+
+  case 19:
+
+/* Line 1806 of yacc.c  */
+#line 129 "parser.y"
+    { (yyval.number) = 1; }
+    break;
+
+  case 20:
+
+/* Line 1806 of yacc.c  */
+#line 130 "parser.y"
+    { (yyval.number) = 0; }
+    break;
+
+
+
+/* Line 1806 of yacc.c  */
+#line 1547 "tools/parser.c"
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+                                        yyssp, yytoken)
+      {
+        char const *yymsgp = YY_("syntax error");
+        int yysyntax_error_status;
+        yysyntax_error_status = YYSYNTAX_ERROR;
+        if (yysyntax_error_status == 0)
+          yymsgp = yymsg;
+        else if (yysyntax_error_status == 1)
+          {
+            if (yymsg != yymsgbuf)
+              YYSTACK_FREE (yymsg);
+            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+            if (!yymsg)
+              {
+                yymsg = yymsgbuf;
+                yymsg_alloc = sizeof yymsgbuf;
+                yysyntax_error_status = 2;
+              }
+            else
+              {
+                yysyntax_error_status = YYSYNTAX_ERROR;
+                yymsgp = yymsg;
+              }
+          }
+        yyerror (yymsgp);
+        if (yysyntax_error_status == 2)
+          goto yyexhaustedlab;
+      }
+# undef YYSYNTAX_ERROR
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+        error, discard it.  */
+
+      if (yychar <= YYEOF)
+       {
+         /* Return failure if at end of input.  */
+         if (yychar == YYEOF)
+           YYABORT;
+       }
+      else
+       {
+         yydestruct ("Error: discarding",
+                     yytoken, &yylval);
+         yychar = YYEMPTY;
+       }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;     /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+       {
+         yyn += YYTERROR;
+         if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+           {
+             yyn = yytable[yyn];
+             if (0 < yyn)
+               break;
+           }
+       }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+       YYABORT;
+
+
+      yydestruct ("Error: popping",
+                 yystos[yystate], yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  *++yyvsp = yylval;
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#if !defined(yyoverflow) || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval);
+    }
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                 yystos[*yyssp], yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+
+/* Line 2067 of yacc.c  */
+#line 133 "parser.y"
+
+
+int yyerror(char *s) 
+{
+       fprintf(stderr, "%s line %d\n", s, lineno);
+       return 0;
+}
+
+int rfcomm_read_config(char *filename)
+{
+       extern FILE *yyin;
+       char file[MAXPATHLEN + 1];
+       int i;
+
+       for (i = 0; i < RFCOMM_MAX_DEV; i++) {
+               rfcomm_opts[i].bind = 0;
+               bacpy(&rfcomm_opts[i].bdaddr, BDADDR_ANY);
+               rfcomm_opts[i].channel = 1;
+       }
+
+       if (filename) {
+               snprintf(file, MAXPATHLEN,  "%s", filename);
+       } else {
+               snprintf(file, MAXPATHLEN, "%s/.bluetooth/rfcomm.conf", getenv("HOME"));
+
+               if ((getuid() == 0) || (access(file, R_OK) < 0))
+                       snprintf(file, MAXPATHLEN, "%s/rfcomm.conf", CONFIGDIR);
+       }
+
+       if (!(yyin = fopen(file, "r")))
+               return -1;
+
+       lineno = 1;
+       yyparse();
+
+       fclose(yyin);
+
+       return 0;
+}
+
diff --git a/tools/parser.h b/tools/parser.h
new file mode 100644 (file)
index 0000000..20149b2
--- /dev/null
@@ -0,0 +1,92 @@
+/* A Bison parser, made by GNU Bison 2.5.  */
+
+/* Bison interface for Yacc-like parsers in C
+   
+      Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
+   
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+   
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     K_BIND = 258,
+     K_DEVICE = 259,
+     K_CHANNEL = 260,
+     K_COMMENT = 261,
+     K_YES = 262,
+     K_NO = 263,
+     NUMBER = 264,
+     RFCOMM = 265,
+     STRING = 266,
+     WORD = 267,
+     BDADDR = 268
+   };
+#endif
+/* Tokens.  */
+#define K_BIND 258
+#define K_DEVICE 259
+#define K_CHANNEL 260
+#define K_COMMENT 261
+#define K_YES 262
+#define K_NO 263
+#define NUMBER 264
+#define RFCOMM 265
+#define STRING 266
+#define WORD 267
+#define BDADDR 268
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+{
+
+/* Line 2068 of yacc.c  */
+#line 49 "parser.y"
+
+       int number;
+       char *string;
+       bdaddr_t *bdaddr;
+
+
+
+/* Line 2068 of yacc.c  */
+#line 84 "tools/parser.h"
+} YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+extern YYSTYPE yylval;
+
+
diff --git a/tools/parser.y b/tools/parser.y
new file mode 100644 (file)
index 0000000..96e6a56
--- /dev/null
@@ -0,0 +1,171 @@
+%{
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+
+int yylex(void);
+int yyerror(char *s); 
+
+struct rfcomm_opts *opts;
+
+%}
+
+%union {
+       int number;
+       char *string;
+       bdaddr_t *bdaddr;
+}
+
+%token K_BIND K_DEVICE K_CHANNEL K_COMMENT
+%token K_YES K_NO
+
+%token <number> NUMBER RFCOMM
+%token <string> STRING WORD
+%token <bdaddr> BDADDR
+
+%type <number> bool
+
+%%
+
+config         :
+               | statement
+               | config statement
+               ;
+
+statement      : section '{' rfcomm_options '}'
+               | rfcomm  '{' rfcomm_options '}'
+               | WORD
+                       {
+                       }
+               | error
+                       {
+                               yyclearin;
+                               yyerrok;
+                       }
+               ;
+
+section                : WORD
+                       {
+                               opts = NULL;
+                       }
+               ;
+
+rfcomm         : RFCOMM
+                       {
+                               if (($1 >= 0) && ($1 < RFCOMM_MAX_DEV))
+                                       opts = &rfcomm_opts[$1];
+                               else
+                                       opts = NULL;
+                       }
+               ;
+
+rfcomm_options : rfcomm_option ';'
+               | error ';'
+               | rfcomm_options rfcomm_option ';'
+               ;
+
+rfcomm_option  : K_BIND bool
+                       {
+                               if (opts)
+                                       opts->bind = $2;
+                       }
+               | K_DEVICE BDADDR
+                       {
+                               if (opts)
+                                       bacpy(&opts->bdaddr, $2);
+                       }
+               | K_CHANNEL NUMBER
+                       {
+                               if (opts)
+                                       opts->channel = $2;
+                       }
+               | K_COMMENT STRING
+                       {
+                               if (opts)
+                                       snprintf(opts->comment, MAXCOMMENTLEN, "%s", $2);
+                       }
+               | WORD
+                       {
+                               // Unknown option
+                       }
+               ;
+
+bool           : K_YES { $$ = 1; }
+               | K_NO  { $$ = 0; }
+               ;
+
+%%
+
+int yyerror(char *s) 
+{
+       fprintf(stderr, "%s line %d\n", s, lineno);
+       return 0;
+}
+
+int rfcomm_read_config(char *filename)
+{
+       extern FILE *yyin;
+       char file[MAXPATHLEN + 1];
+       int i;
+
+       for (i = 0; i < RFCOMM_MAX_DEV; i++) {
+               rfcomm_opts[i].bind = 0;
+               bacpy(&rfcomm_opts[i].bdaddr, BDADDR_ANY);
+               rfcomm_opts[i].channel = 1;
+       }
+
+       if (filename) {
+               snprintf(file, MAXPATHLEN,  "%s", filename);
+       } else {
+               snprintf(file, MAXPATHLEN, "%s/.bluetooth/rfcomm.conf", getenv("HOME"));
+
+               if ((getuid() == 0) || (access(file, R_OK) < 0))
+                       snprintf(file, MAXPATHLEN, "%s/rfcomm.conf", CONFIGDIR);
+       }
+
+       if (!(yyin = fopen(file, "r")))
+               return -1;
+
+       lineno = 1;
+       yyparse();
+
+       fclose(yyin);
+
+       return 0;
+}
diff --git a/tools/ppporc.c b/tools/ppporc.c
new file mode 100644 (file)
index 0000000..ca44b40
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+/* IO cancelation */
+static volatile sig_atomic_t __io_canceled;
+
+static inline void io_init(void)
+{
+       __io_canceled = 0;
+}
+
+static inline void io_cancel(void)
+{
+       __io_canceled = 1;
+}
+
+/* Signal functions */
+static void sig_hup(int sig)
+{
+       return;
+}
+
+static void sig_term(int sig)
+{
+       syslog(LOG_INFO, "Closing RFCOMM channel");
+       io_cancel();
+}
+
+/* Read exactly len bytes (Signal safe)*/
+static inline int read_n(int fd, char *buf, int len)
+{
+       register int t = 0, w;
+
+       while (!__io_canceled && len > 0) {
+               if ((w = read(fd, buf, len)) < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       return -1;
+               }
+               if (!w)
+                       return 0;
+               len -= w;
+               buf += w;
+               t += w;
+       }
+
+       return t;
+}
+
+/* Write exactly len bytes (Signal safe)*/
+static inline int write_n(int fd, char *buf, int len)
+{
+       register int t = 0, w;
+
+       while (!__io_canceled && len > 0) {
+               if ((w = write(fd, buf, len)) < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       return -1;
+               }
+               if (!w)
+                       return 0;
+               len -= w;
+               buf += w;
+               t += w;
+       }
+
+       return t;
+}
+
+/* Create the RFCOMM connection */
+static int create_connection(bdaddr_t *bdaddr, uint8_t channel)
+{
+       struct sockaddr_rc remote_addr, local_addr;
+       int fd, err;
+
+       if ((fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0)
+               return fd;
+
+       memset(&local_addr, 0, sizeof(local_addr));
+       local_addr.rc_family = AF_BLUETOOTH;
+       bacpy(&local_addr.rc_bdaddr, BDADDR_ANY);
+       if ((err = bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr))) < 0) {
+               close(fd);
+               return err;
+       }
+
+       memset(&remote_addr, 0, sizeof(remote_addr));
+       remote_addr.rc_family = AF_BLUETOOTH;
+       bacpy(&remote_addr.rc_bdaddr, bdaddr);
+       remote_addr.rc_channel = channel;
+       if ((err = connect(fd, (struct sockaddr *)&remote_addr, sizeof(remote_addr))) < 0) {
+               close(fd);
+               return err;
+       }
+
+       syslog(LOG_INFO, "RFCOMM channel %d connected", channel);
+
+       return fd;
+}
+
+/* Process the data from socket and pseudo tty */
+static int process_data(int fd)
+{
+       struct pollfd p[2];
+       char buf[1024];
+       int err, r;
+
+       p[0].fd = 0;
+       p[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
+
+       p[1].fd = fd;
+       p[1].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
+
+       err = 0;
+
+       while (!__io_canceled) {
+               p[0].revents = 0;
+               p[1].revents = 0;
+
+               err = poll(p, 2, -1);
+               if (err < 0)
+                       break;
+
+               err = 0;
+
+               if (p[0].revents) {
+                       if (p[0].revents & (POLLERR | POLLHUP | POLLNVAL))
+                         break;
+                       r = read(0, buf, sizeof(buf));
+                       if (r < 0) {
+                               if (errno != EINTR && errno != EAGAIN) {
+                                       err = r;
+                                       break;
+                               }
+                       }
+
+                       err = write_n(fd, buf, r);
+                       if (err < 0)
+                               break;
+               }
+
+               if (p[1].revents) {
+                       if (p[1].revents & (POLLERR | POLLHUP | POLLNVAL))
+                               break;
+                       r = read(fd, buf, sizeof(buf));
+                       if (r < 0) {
+                               if (errno != EINTR && errno != EAGAIN) {
+                                       err = r;
+                                       break;
+                               }
+                       }
+
+                       err = write_n(1, buf, r);
+                       if (err < 0)
+                               break;
+               }
+       }
+
+       return err;
+}
+
+static void usage(void)
+{
+       printf("Usage:\tppporc <bdaddr> [channel]\n");
+}
+
+int main(int argc, char** argv)
+{
+       struct sigaction sa;
+       int fd, err, opt;
+
+       bdaddr_t bdaddr;
+       uint8_t channel;
+
+       /* Parse command line options */
+       while ((opt = getopt(argc, argv, "h")) != EOF) {
+               switch(opt) {
+               case 'h':
+                       usage();
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       switch (argc) {
+       case 1:
+               str2ba(argv[0], &bdaddr);
+               channel = 1;
+               break;
+       case 2:
+               str2ba(argv[0], &bdaddr);
+               channel = atoi(argv[1]);
+               break;
+       default:
+               usage();
+               exit(0);
+       }
+
+       /* Initialize syslog */
+       openlog("ppporc", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+       syslog(LOG_INFO, "PPP over RFCOMM");
+
+       /* Initialize signals */
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       syslog(LOG_INFO, "Connecting to %s", argv[0]);
+
+       if ((fd = create_connection(&bdaddr, channel)) < 0) {
+               syslog(LOG_ERR, "Can't connect to remote device (%s)", strerror(errno));
+               return fd;
+       }
+
+       err = process_data(fd);
+
+       close(fd);
+
+       return err;
+}
diff --git a/tools/rfcomm.1 b/tools/rfcomm.1
new file mode 100644 (file)
index 0000000..06303cd
--- /dev/null
@@ -0,0 +1,137 @@
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH RFCOMM 1 "APRIL 28, 2002" "" ""
+
+.SH NAME
+rfcomm \- RFCOMM configuration utility
+.SH SYNOPSIS
+.BR "rfcomm
+[
+.I options
+] <
+.I command
+> <
+.I dev
+>
+.SH DESCRIPTION
+.B rfcomm
+is used to set up, maintain, and inspect the RFCOMM configuration
+of the Bluetooth subsystem in the Linux kernel. If no
+.B command
+is given, or if the option
+.B -a
+is used,
+.B rfcomm
+prints information about the configured RFCOMM devices.
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -a
+Prints information about all configured RFCOMM devices.
+.TP
+.BI -r
+Switch TTY into raw mode (doesn't work with "bind").
+.TP
+.BI -f " <file>"
+Specify alternate config file.
+.TP
+.BI -i " <hciX> | <bdaddr>"
+The command is applied to device
+.BI -A
+Enable authentication.
+.BI -E
+Enable encryption.
+.BI -S
+Secure connection.
+.BI -M
+Become the master of a piconet.
+.I
+hciX
+, which must be the name or the address of an installed Bluetooth
+device. If not specified, the command will be use the first
+available Bluetooth device.
+.TP
+.BI -A
+Enable authentification
+.TP
+.BI -E
+Enable encryption
+.TP
+.BI -S
+Secure connection
+.TP
+.BI -M
+Become the master of a piconet
+.TP
+.BI -L " <seconds>"
+Set linger timeout
+.SH COMMANDS
+.TP
+.BI show " <dev>"
+Display the information about the specified device.
+.TP
+.BI connect " <dev> [bdaddr] [channel]"
+Connect the RFCOMM device to the remote Bluetooth device on the
+specified channel. If no channel is specified, it will use the
+channel number 1. If also the Bluetooth address is left out, it
+tries to read the data from the config file. This command can
+be terminated with the key sequence CTRL-C.
+.TP
+.BI listen " <dev> [channel] [cmd]"
+Listen on a specified RFCOMM channel for incoming connections.
+If no channel is specified, it will use the channel number 1, but
+a channel must be specified before cmd. If cmd is given, it will be
+executed as soon as a client connects. When the child process
+terminates or the client disconnect, the command will terminate.
+Occurrences of {} in cmd will be replaced by the name of the device
+used by the connection. This command can be terminated with the key
+sequence CTRL-C.
+.TP
+.BI watch " <dev> [channel] [cmd]"
+Watch is identical to
+.B listen
+except that when the child process terminates or the client
+disconnect, the command will restart listening with the same
+parameters.
+.TP
+.BI bind " <dev> [bdaddr] [channel]"
+This binds the RFCOMM device to a remote Bluetooth device. The
+command did not establish a connection to the remote device, it
+only creates the binding. The connection will be established right
+after an application tries to open the RFCOMM device. If no channel
+number is specified, it uses the channel number 1. If the Bluetooth
+address is also left out, it tries to read the data from the config
+file.
+
+If
+.B all
+is specified for the RFCOMM device, then all devices that have
+.B "bind yes"
+set in the config will be bound.
+.TP
+.BI release " <dev>"
+This command releases a defined RFCOMM binding.
+
+If
+.B all
+is specified for the RFCOMM device, then all bindings will be removed.
+This command didn't care about the settings in the config file.
+.SH AUTHOR
+Written by Marcel Holtmann <marcel@holtmann.org>.
+.br
diff --git a/tools/rfcomm.c b/tools/rfcomm.c
new file mode 100644 (file)
index 0000000..e73b0ba
--- /dev/null
@@ -0,0 +1,849 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/rfcomm.h>
+
+#include "kword.h"
+
+#ifdef NEED_PPOLL
+#include "ppoll.h"
+#endif
+
+static char *rfcomm_config_file = NULL;
+static int rfcomm_raw_tty = 0;
+static int auth = 0;
+static int encryption = 0;
+static int secure = 0;
+static int master = 0;
+static int linger = 0;
+
+static char *rfcomm_state[] = {
+       "unknown",
+       "connected",
+       "clean",
+       "bound",
+       "listening",
+       "connecting",
+       "connecting",
+       "config",
+       "disconnecting",
+       "closed"
+};
+
+static volatile sig_atomic_t __io_canceled = 0;
+
+static void sig_hup(int sig)
+{
+       return;
+}
+
+static void sig_term(int sig)
+{
+       __io_canceled = 1;
+}
+
+static char *rfcomm_flagstostr(uint32_t flags)
+{
+       static char str[100];
+       str[0] = 0;
+
+       strcat(str, "[");
+
+       if (flags & (1 << RFCOMM_REUSE_DLC))
+               strcat(str, "reuse-dlc ");
+
+       if (flags & (1 << RFCOMM_RELEASE_ONHUP))
+               strcat(str, "release-on-hup ");
+
+       if (flags & (1 << RFCOMM_TTY_ATTACHED))
+               strcat(str, "tty-attached");
+
+       strcat(str, "]");
+       return str;
+}
+
+static void print_dev_info(struct rfcomm_dev_info *di)
+{
+       char src[18], dst[18], addr[40];
+
+       ba2str(&di->src, src); ba2str(&di->dst, dst);
+
+       if (bacmp(&di->src, BDADDR_ANY) == 0)
+               sprintf(addr, "%s", dst);
+       else
+               sprintf(addr, "%s -> %s", src, dst);
+
+       printf("rfcomm%d: %s channel %d %s %s\n",
+               di->id, addr, di->channel,
+               rfcomm_state[di->state],
+               di->flags ? rfcomm_flagstostr(di->flags) : "");
+}
+
+static void print_dev_list(int ctl, int flags)
+{
+       struct rfcomm_dev_list_req *dl;
+       struct rfcomm_dev_info *di;
+       int i;
+
+       dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+       if (!dl) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       dl->dev_num = RFCOMM_MAX_DEV;
+       di = dl->dev_info;
+
+       if (ioctl(ctl, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+               perror("Can't get device list");
+               free(dl);
+               exit(1);
+       }
+
+       for (i = 0; i < dl->dev_num; i++)
+               print_dev_info(di + i);
+       free(dl);
+}
+
+static int create_dev(int ctl, int dev, uint32_t flags, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       struct rfcomm_dev_req req;
+       int err;
+
+       memset(&req, 0, sizeof(req));
+       req.dev_id = dev;
+       req.flags = flags;
+       bacpy(&req.src, bdaddr);
+
+       if (argc < 2) {
+               err = rfcomm_read_config(rfcomm_config_file);
+               if (err < 0) {
+                       perror("Can't open RFCOMM config file");
+                       return err;
+               }
+
+               bacpy(&req.dst, &rfcomm_opts[dev].bdaddr);
+               req.channel = rfcomm_opts[dev].channel;
+
+               if (bacmp(&req.dst, BDADDR_ANY) == 0) {
+                       fprintf(stderr, "Can't find a config entry for rfcomm%d\n", dev);
+                       return -EFAULT;
+               }
+       } else {
+               str2ba(argv[1], &req.dst);
+
+               if (argc > 2)
+                       req.channel = atoi(argv[2]);
+               else
+                       req.channel = 1;
+       }
+
+       err = ioctl(ctl, RFCOMMCREATEDEV, &req);
+       if (err == EOPNOTSUPP)
+               fprintf(stderr, "RFCOMM TTY support not available\n");
+       else if (err < 0)
+               perror("Can't create device");
+
+       return err;
+}
+
+static int create_all(int ctl)
+{
+       struct rfcomm_dev_req req;
+       int i, err;
+
+       err = rfcomm_read_config(rfcomm_config_file);
+       if (err < 0) {
+               perror("Can't open RFCOMM config file");
+               return err;
+       }
+
+       for (i = 0; i < RFCOMM_MAX_DEV; i++) {
+               if (!rfcomm_opts[i].bind)
+                       continue;
+
+               memset(&req, 0, sizeof(req));
+               req.dev_id = i;
+               req.flags = 0;
+               bacpy(&req.src, BDADDR_ANY);
+               bacpy(&req.dst, &rfcomm_opts[i].bdaddr);
+               req.channel = rfcomm_opts[i].channel;
+
+               if (bacmp(&req.dst, BDADDR_ANY) != 0)
+                       ioctl(ctl, RFCOMMCREATEDEV, &req);
+       }
+
+       return 0;
+}
+
+static int release_dev(int ctl, int dev, uint32_t flags)
+{
+       struct rfcomm_dev_req req;
+       int err;
+
+       memset(&req, 0, sizeof(req));
+       req.dev_id = dev;
+
+       err = ioctl(ctl, RFCOMMRELEASEDEV, &req);
+       if (err < 0)
+               perror("Can't release device");
+
+       return err;
+}
+
+static int release_all(int ctl)
+{
+       struct rfcomm_dev_list_req *dl;
+       struct rfcomm_dev_info *di;
+       int i;
+
+       dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di));
+       if (!dl) {
+               perror("Can't allocate memory");
+               exit(1);
+       }
+
+       dl->dev_num = RFCOMM_MAX_DEV;
+       di = dl->dev_info;
+
+       if (ioctl(ctl, RFCOMMGETDEVLIST, (void *) dl) < 0) {
+               perror("Can't get device list");
+               free(dl);
+               exit(1);
+       }
+
+       for (i = 0; i < dl->dev_num; i++)
+               release_dev(ctl, (di + i)->id, 0);
+
+       free(dl);
+       return 0;
+}
+
+static void run_cmdline(struct pollfd *p, sigset_t* sigs, char *devname,
+                       int argc, char **argv)
+{
+       int i;
+       pid_t pid;
+       char **cmdargv;
+
+       cmdargv = malloc((argc + 1) * sizeof(char*));
+       if (!cmdargv)
+               return;
+
+       for (i = 0; i < argc; i++)
+               cmdargv[i] = (strcmp(argv[i], "{}") == 0) ? devname : argv[i];
+       cmdargv[i] = NULL;
+
+       pid = fork();
+
+       switch (pid) {
+       case 0:
+               i = execvp(cmdargv[0], cmdargv);
+               fprintf(stderr, "Couldn't execute command %s (errno=%d:%s)\n",
+                               cmdargv[0], errno, strerror(errno));
+               break;
+       case -1:
+               fprintf(stderr, "Couldn't fork to execute command %s\n",
+                               cmdargv[0]);
+               break;
+       default:
+               while (1) {
+                       int status;
+                       pid_t child;
+                       struct timespec ts;
+
+                       child = waitpid(-1, &status, WNOHANG);
+                       if (child == pid || (child < 0 && errno != EAGAIN))
+                               break;
+
+                       p->revents = 0;
+                       ts.tv_sec  = 0;
+                       ts.tv_nsec = 200;
+                       if (ppoll(p, 1, &ts, sigs) || __io_canceled) {
+                               kill(pid, SIGTERM);
+                               waitpid(pid, &status, 0);
+                               break;
+                       }
+               }
+               break;
+       }
+
+       free(cmdargv);
+}
+
+static void cmd_connect(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       struct sockaddr_rc laddr, raddr;
+       struct rfcomm_dev_req req;
+       struct termios ti;
+       struct sigaction sa;
+       struct pollfd p;
+       sigset_t sigs;
+       socklen_t alen;
+       char dst[18], devname[MAXPATHLEN];
+       int sk, fd, try = 30;
+
+       laddr.rc_family = AF_BLUETOOTH;
+       bacpy(&laddr.rc_bdaddr, bdaddr);
+       laddr.rc_channel = 0;
+
+       if (argc < 2) {
+               if (rfcomm_read_config(rfcomm_config_file) < 0) {
+                       perror("Can't open RFCOMM config file");
+                       return;
+               }
+
+               raddr.rc_family = AF_BLUETOOTH;
+               bacpy(&raddr.rc_bdaddr, &rfcomm_opts[dev].bdaddr);
+               raddr.rc_channel = rfcomm_opts[dev].channel;
+
+               if (bacmp(&raddr.rc_bdaddr, BDADDR_ANY) == 0) {
+                       fprintf(stderr, "Can't find a config entry for rfcomm%d\n", dev);
+                       return;
+               }
+       } else {
+               raddr.rc_family = AF_BLUETOOTH;
+               str2ba(argv[1], &raddr.rc_bdaddr);
+
+               if (argc > 2)
+                       raddr.rc_channel = atoi(argv[2]);
+               else
+                       raddr.rc_channel = 1;
+       }
+
+       sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+       if (sk < 0) {
+               perror("Can't create RFCOMM socket");
+               return;
+       }
+
+       if (linger) {
+               struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+               if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+                       perror("Can't set linger option");
+                       return;
+               }
+       }
+
+       if (bind(sk, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) {
+               perror("Can't bind RFCOMM socket");
+               close(sk);
+               return;
+       }
+
+       if (connect(sk, (struct sockaddr *) &raddr, sizeof(raddr)) < 0) {
+               perror("Can't connect RFCOMM socket");
+               close(sk);
+               return;
+       }
+
+       alen = sizeof(laddr);
+       if (getsockname(sk, (struct sockaddr *)&laddr, &alen) < 0) {
+               perror("Can't get RFCOMM socket name");
+               close(sk);
+               return;
+       }
+
+       memset(&req, 0, sizeof(req));
+       req.dev_id = dev;
+       req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+
+       bacpy(&req.src, &laddr.rc_bdaddr);
+       bacpy(&req.dst, &raddr.rc_bdaddr);
+       req.channel = raddr.rc_channel;
+
+       dev = ioctl(sk, RFCOMMCREATEDEV, &req);
+       if (dev < 0) {
+               perror("Can't create RFCOMM TTY");
+               close(sk);
+               return;
+       }
+
+       snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+       while ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+               if (errno == EACCES) {
+                       perror("Can't open RFCOMM device");
+                       goto release;
+               }
+
+               snprintf(devname, MAXPATHLEN - 1, "/dev/bluetooth/rfcomm/%d", dev);
+               if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+                       if (try--) {
+                               snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+                               usleep(100 * 1000);
+                               continue;
+                       }
+                       perror("Can't open RFCOMM device");
+                       goto release;
+               }
+       }
+
+       if (rfcomm_raw_tty) {
+               tcflush(fd, TCIOFLUSH);
+
+               cfmakeraw(&ti);
+               tcsetattr(fd, TCSANOW, &ti);
+       }
+
+       close(sk);
+
+       ba2str(&req.dst, dst);
+       printf("Connected %s to %s on channel %d\n", devname, dst, req.channel);
+       printf("Press CTRL-C for hangup\n");
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       sigfillset(&sigs);
+       sigdelset(&sigs, SIGCHLD);
+       sigdelset(&sigs, SIGPIPE);
+       sigdelset(&sigs, SIGTERM);
+       sigdelset(&sigs, SIGINT);
+       sigdelset(&sigs, SIGHUP);
+
+       p.fd = fd;
+       p.events = POLLERR | POLLHUP;
+
+       while (!__io_canceled) {
+               p.revents = 0;
+               if (ppoll(&p, 1, NULL, &sigs) > 0)
+                       break;
+       }
+
+       printf("Disconnected\n");
+
+       close(fd);
+       return;
+
+release:
+       memset(&req, 0, sizeof(req));
+       req.dev_id = dev;
+       req.flags = (1 << RFCOMM_HANGUP_NOW);
+       ioctl(ctl, RFCOMMRELEASEDEV, &req);
+
+       close(sk);
+}
+
+static void cmd_listen(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       struct sockaddr_rc laddr, raddr;
+       struct rfcomm_dev_req req;
+       struct termios ti;
+       struct sigaction sa;
+       struct pollfd p;
+       sigset_t sigs;
+       socklen_t alen;
+       char dst[18], devname[MAXPATHLEN];
+       int sk, nsk, fd, lm, try = 30;
+
+       laddr.rc_family = AF_BLUETOOTH;
+       bacpy(&laddr.rc_bdaddr, bdaddr);
+       laddr.rc_channel = (argc < 2) ? 1 : atoi(argv[1]);
+
+       sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+       if (sk < 0) {
+               perror("Can't create RFCOMM socket");
+               return;
+       }
+
+       lm = 0;
+       if (master)
+               lm |= RFCOMM_LM_MASTER;
+       if (auth)
+               lm |= RFCOMM_LM_AUTH;
+       if (encryption)
+               lm |= RFCOMM_LM_ENCRYPT;
+       if (secure)
+               lm |= RFCOMM_LM_SECURE;
+
+       if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
+               perror("Can't set RFCOMM link mode");
+               close(sk);
+               return;
+       }
+
+       if (bind(sk, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
+               perror("Can't bind RFCOMM socket");
+               close(sk);
+               return;
+       }
+
+       printf("Waiting for connection on channel %d\n", laddr.rc_channel);
+
+       listen(sk, 10);
+
+       alen = sizeof(raddr);
+       nsk = accept(sk, (struct sockaddr *) &raddr, &alen);
+
+       alen = sizeof(laddr);
+       if (getsockname(nsk, (struct sockaddr *)&laddr, &alen) < 0) {
+               perror("Can't get RFCOMM socket name");
+               close(nsk);
+               return;
+       }
+
+       if (linger) {
+               struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+               if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+                       perror("Can't set linger option");
+                       close(nsk);
+                       return;
+               }
+       }
+
+       memset(&req, 0, sizeof(req));
+       req.dev_id = dev;
+       req.flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP);
+
+       bacpy(&req.src, &laddr.rc_bdaddr);
+       bacpy(&req.dst, &raddr.rc_bdaddr);
+       req.channel = raddr.rc_channel;
+
+       dev = ioctl(nsk, RFCOMMCREATEDEV, &req);
+       if (dev < 0) {
+               perror("Can't create RFCOMM TTY");
+               close(sk);
+               return;
+       }
+
+       snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+       while ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+               if (errno == EACCES) {
+                       perror("Can't open RFCOMM device");
+                       goto release;
+               }
+
+               snprintf(devname, MAXPATHLEN - 1, "/dev/bluetooth/rfcomm/%d", dev);
+               if ((fd = open(devname, O_RDONLY | O_NOCTTY)) < 0) {
+                       if (try--) {
+                               snprintf(devname, MAXPATHLEN - 1, "/dev/rfcomm%d", dev);
+                               usleep(100 * 1000);
+                               continue;
+                       }
+                       perror("Can't open RFCOMM device");
+                       goto release;
+               }
+       }
+
+       if (rfcomm_raw_tty) {
+               tcflush(fd, TCIOFLUSH);
+
+               cfmakeraw(&ti);
+               tcsetattr(fd, TCSANOW, &ti);
+       }
+
+       close(sk);
+       close(nsk);
+
+       ba2str(&req.dst, dst);
+       printf("Connection from %s to %s\n", dst, devname);
+       printf("Press CTRL-C for hangup\n");
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_flags   = SA_NOCLDSTOP;
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGCHLD, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+
+       sa.sa_handler = sig_term;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       sa.sa_handler = sig_hup;
+       sigaction(SIGHUP, &sa, NULL);
+
+       sigfillset(&sigs);
+       sigdelset(&sigs, SIGCHLD);
+       sigdelset(&sigs, SIGPIPE);
+       sigdelset(&sigs, SIGTERM);
+       sigdelset(&sigs, SIGINT);
+       sigdelset(&sigs, SIGHUP);
+
+       p.fd = fd;
+       p.events = POLLERR | POLLHUP;
+
+       if (argc <= 2) {
+               while (!__io_canceled) {
+                       p.revents = 0;
+                       if (ppoll(&p, 1, NULL, &sigs) > 0)
+                               break;
+               }
+       } else
+               run_cmdline(&p, &sigs, devname, argc - 2, argv + 2);
+
+       sa.sa_handler = NULL;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT,  &sa, NULL);
+
+       printf("Disconnected\n");
+
+       close(fd);
+       return;
+
+release:
+       memset(&req, 0, sizeof(req));
+       req.dev_id = dev;
+       req.flags = (1 << RFCOMM_HANGUP_NOW);
+       ioctl(ctl, RFCOMMRELEASEDEV, &req);
+
+       close(sk);
+}
+
+static void cmd_watch(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       while (!__io_canceled) {
+               cmd_listen(ctl, dev, bdaddr, argc, argv);
+               usleep(10000);
+       }
+}
+
+static void cmd_create(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       if (strcmp(argv[0], "all") == 0)
+               create_all(ctl);
+       else
+               create_dev(ctl, dev, 0, bdaddr, argc, argv);
+}
+
+static void cmd_release(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       if (strcmp(argv[0], "all") == 0)
+               release_all(ctl);
+       else
+               release_dev(ctl, dev, 0);
+}
+
+static void cmd_show(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv)
+{
+       if (strcmp(argv[0], "all") == 0)
+               print_dev_list(ctl, 0);
+       else {
+               struct rfcomm_dev_info di = { .id = atoi(argv[0]) };
+               if (ioctl(ctl, RFCOMMGETDEVINFO, &di) < 0) {
+                       perror("Get info failed");
+                       exit(1);
+               }
+
+               print_dev_info(&di);
+       }
+}
+
+struct {
+       char *cmd;
+       char *alt;
+       void (*func)(int ctl, int dev, bdaddr_t *bdaddr, int argc, char **argv);
+       char *opt;
+       char *doc;
+} command[] = {
+       { "bind",    "create", cmd_create,  "<dev> <bdaddr> [channel]", "Bind device"    },
+       { "release", "unbind", cmd_release, "<dev>",                    "Release device" },
+       { "show",    "info",   cmd_show,    "<dev>",                    "Show device"    },
+       { "connect", "conn",   cmd_connect, "<dev> <bdaddr> [channel]", "Connect device" },
+       { "listen",  "server", cmd_listen,  "<dev> [channel [cmd]]",    "Listen"         },
+       { "watch",   "watch",  cmd_watch,   "<dev> [channel [cmd]]",    "Watch"          },
+       { NULL, NULL, NULL, 0, 0 }
+};
+
+static void usage(void)
+{
+       int i;
+
+       printf("RFCOMM configuration utility ver %s\n", VERSION);
+
+       printf("Usage:\n"
+               "\trfcomm [options] <command> <dev>\n"
+               "\n");
+
+       printf("Options:\n"
+               "\t-i [hciX|bdaddr]      Local HCI device or BD Address\n"
+               "\t-h, --help            Display help\n"
+               "\t-r, --raw             Switch TTY into raw mode\n"
+               "\t-A, --auth            Enable authentication\n"
+               "\t-E, --encrypt         Enable encryption\n"
+               "\t-S, --secure          Secure connection\n"
+               "\t-M, --master          Become the master of a piconet\n"
+               "\t-f, --config [file]   Specify alternate config file\n"
+               "\t-a                    Show all devices (default)\n"
+               "\n");
+
+       printf("Commands:\n");
+       for (i = 0; command[i].cmd; i++)
+               printf("\t%-8s %-24s\t%s\n",
+                       command[i].cmd,
+                       command[i].opt ? command[i].opt : " ",
+                       command[i].doc);
+       printf("\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "device",     1, 0, 'i' },
+       { "config",     1, 0, 'f' },
+       { "raw",        0, 0, 'r' },
+       { "auth",       0, 0, 'A' },
+       { "encrypt",    0, 0, 'E' },
+       { "secure",     0, 0, 'S' },
+       { "master",     0, 0, 'M' },
+       { "linger",     1, 0, 'L' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       bdaddr_t bdaddr;
+       int i, opt, ctl, dev_id, show_all = 0;
+
+       bacpy(&bdaddr, BDADDR_ANY);
+
+       while ((opt = getopt_long(argc, argv, "+i:f:rahAESML:", main_options, NULL)) != -1) {
+               switch(opt) {
+               case 'i':
+                       if (strncmp(optarg, "hci", 3) == 0)
+                               hci_devba(atoi(optarg + 3), &bdaddr);
+                       else
+                               str2ba(optarg, &bdaddr);
+                       break;
+
+               case 'f':
+                       rfcomm_config_file = strdup(optarg);
+                       break;
+
+               case 'r':
+                       rfcomm_raw_tty = 1;
+                       break;
+
+               case 'a':
+                       show_all = 1;
+                       break;
+
+               case 'h':
+                       usage();
+                       exit(0);
+
+               case 'A':
+                       auth = 1;
+                       break;
+
+               case 'E':
+                       encryption = 1;
+                       break;
+
+               case 'S':
+                       secure = 1;
+                       break;
+
+               case 'M':
+                       master = 1;
+                       break;
+
+               case 'L':
+                       linger = atoi(optarg);
+                       break;
+
+               default:
+                       exit(0);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 2) {
+               if (argc != 0) {
+                       usage();
+                       exit(1);
+               } else
+                       show_all = 1;
+       }
+
+       ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+       if (ctl < 0) {
+               perror("Can't open RFCOMM control socket");
+               exit(1);
+       }
+
+       if (show_all) {
+               print_dev_list(ctl, 0);
+               close(ctl);
+               exit(0);
+       }
+
+       if (strncmp(argv[1], "/dev/rfcomm", 11) == 0)
+               dev_id = atoi(argv[1] + 11);
+       else if (strncmp(argv[1], "rfcomm", 6) == 0)
+               dev_id = atoi(argv[1] + 6);
+       else
+               dev_id = atoi(argv[1]);
+
+       for (i = 0; command[i].cmd; i++) {
+               if (strncmp(command[i].cmd, argv[0], 4) && strncmp(command[i].alt, argv[0], 4))
+                       continue;
+               argc--;
+               argv++;
+               command[i].func(ctl, dev_id, &bdaddr, argc, argv);
+               close(ctl);
+               exit(0);
+       }
+
+       usage();
+
+       close(ctl);
+
+       return 0;
+}
diff --git a/tools/rfcomm.conf b/tools/rfcomm.conf
new file mode 100644 (file)
index 0000000..6179ef7
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# RFCOMM configuration file.
+#
+
+#rfcomm0 {
+#      # Automatically bind the device at startup
+#      bind no;
+#
+#      # Bluetooth address of the device
+#      device 11:22:33:44:55:66;
+#
+#      # RFCOMM channel for the connection
+#      channel 1;
+#
+#      # Description of the connection
+#      comment "Example Bluetooth device";
+#}
diff --git a/tools/sdptool.1 b/tools/sdptool.1
new file mode 100644 (file)
index 0000000..0f100e2
--- /dev/null
@@ -0,0 +1,130 @@
+.\" $Header$
+.\"
+.\"    transcript compatibility for postscript use.
+.\"
+.\"    synopsis:  .P! <file.ps>
+.\"
+.de P!
+.fl
+\!!1 setgray
+.fl
+\\&.\"
+.fl
+\!!0 setgray
+.fl                    \" force out current output buffer
+\!!save /psv exch def currentpoint translate 0 0 moveto
+\!!/showpage{}def
+.fl                    \" prolog
+.sy sed -e 's/^/!/' \\$1\" bring in postscript file
+\!!psv restore
+.
+.de pF
+.ie     \a\\*(f1\a\a .ds f1 \\n(.f
+.el .ie \a\\*(f2\a\a .ds f2 \\n(.f
+.el .ie \a\\*(f3\a\a .ds f3 \\n(.f
+.el .ie \a\\*(f4\a\a .ds f4 \\n(.f
+.el .tm ? font overflow
+.ft \\$1
+..
+.de fP
+.ie     !\a\\*(f4\a\a \{\
+.      ft \\*(f4
+.      ds f4\"
+'      br \}
+.el .ie !\a\\*(f3\a\a \{\
+.      ft \\*(f3
+.      ds f3\"
+'      br \}
+.el .ie !\a\\*(f2\a\a \{\
+.      ft \\*(f2
+.      ds f2\"
+'      br \}
+.el .ie !\a\\*(f1\a\a \{\
+.      ft \\*(f1
+.      ds f1\"
+'      br \}
+.el .tm ? font underflow
+..
+.ds f1\"
+.ds f2\"
+.ds f3\"
+.ds f4\"
+'\" t
+.ta 8n 16n 24n 32n 40n 48n 56n 64n 72n
+.TH "sdptool" "1"
+.SH "NAME"
+sdptool \(em control and interrogate SDP servers
+.SH "SYNOPSIS"
+.PP
+\fBsdptool\fR [\fIoptions\fR]  {\fIcommand\fR}  [\fIcommand parameters\fR \&...]
+.SH "DESCRIPTION"
+.PP
+\fBsdptool\fR provides the interface for
+performing SDP queries on Bluetooth devices, and administering a
+local \fBsdpd\fR.
+.SH "COMMANDS"
+.PP
+The following commands are available.  In all cases \fBbdaddr\fR
+specifies the device to search or browse.  If \fIlocal\fP is used
+for \fBbdaddr\fP, then the local \fBsdpd\fR is searched.
+.PP
+Services are identified and manipulated with a 4-byte \fBrecord_handle\fP
+(NOT the service name).  To find a service's \fBrecord_handle\fP, look for the
+"Service RecHandle" line in the \fBsearch\fP or \fBbrowse\fP results
+.IP "\fBsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] service_name\fP" 10
+Search for services..
+.IP "" 10
+Known service names are DID, SP, DUN, LAN, FAX, OPUSH,
+FTP, HS, HF, HFAG, SAP, NAP, GN, PANU, HCRP, HID, CIP,
+A2SRC, A2SNK, AVRCT, AVRTG, UDIUE, UDITE and SYNCML.
+.IP "\fBbrowse [--tree] [--raw] [--xml] [bdaddr]\fP" 10
+Browse all available services on the device
+specified by a Bluetooth address as a parameter.
+.IP "\fBrecords [--tree] [--raw] [--xml] bdaddr\fP" 10
+Retrieve all possible service records.
+.IP "\fBadd [ --handle=N --channel=N ]\fP" 10
+Add a service to the local
+\fBsdpd\fR.
+.IP "" 10
+You can specify a handle for this record using
+the \fB--handle\fP option.
+.IP "" 10
+You can specify a channel to add the service on
+using the \fB--channel\fP option.
+.IP "\fBdel record_handle\fP" 10
+Remove a service from the local
+\fBsdpd\fR.
+.IP "\fBget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\fP" 10
+Retrieve a service from the local
+\fBsdpd\fR.
+.IP "\fBsetattr record_handle attrib_id attrib_value\fP" 10
+Set or add an attribute to an SDP record.
+
+.IP "\fBsetseq record_handle attrib_id attrib_values\fP" 10
+Set or add an attribute sequence to an
+SDP record.
+.SH "OPTIONS"
+.IP "\fB--help\fP" 10
+Displays help on using sdptool.
+
+.SH "EXAMPLES"
+.PP
+sdptool browse 00:80:98:24:15:6D
+.PP
+sdptool browse local
+.PP
+sdptool add DUN
+.PP
+sdptool del 0x10000
+.SH "BUGS"
+.PP
+Documentation needs improving.
+.SH "AUTHOR"
+.PP
+Maxim Krasnyansky <maxk@qualcomm.com>. Man page written
+by Edd Dumbill <ejad@debian.org>.
+
+.SH "SEE ALSO"
+.PP
+sdpd(8)
+.\" created by instant / docbook-to-man, Thu 15 Jan 2004, 21:01
diff --git a/tools/sdptool.c b/tools/sdptool.c
new file mode 100644 (file)
index 0000000..4e9da64
--- /dev/null
@@ -0,0 +1,4266 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2001-2002  Nokia Corporation
+ *  Copyright (C) 2002-2003  Maxim Krasnyansky <maxk@qualcomm.com>
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2002-2003  Stephen Crane <steve.crane@rococosoft.com>
+ *  Copyright (C) 2002-2003  Jean Tourrilhes <jt@hpl.hp.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <netinet/in.h>
+
+#include "sdp-xml.h"
+
+#ifndef APPLE_AGENT_SVCLASS_ID
+#define APPLE_AGENT_SVCLASS_ID 0x2112
+#endif
+
+#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, 0)) != -1)
+
+/*
+ * Convert a string to a BDADDR, with a few "enhancements" - Jean II
+ */
+static int estr2ba(char *str, bdaddr_t *ba)
+{
+       /* Only trap "local", "any" is already dealt with */
+       if(!strcmp(str, "local")) {
+               bacpy(ba, BDADDR_LOCAL);
+               return 0;
+       }
+       return str2ba(str, ba);
+}
+
+#define DEFAULT_VIEW   0       /* Display only known attribute */
+#define TREE_VIEW      1       /* Display full attribute tree */
+#define RAW_VIEW       2       /* Display raw tree */
+#define XML_VIEW       3       /* Display xml tree */
+
+/* Pass args to the inquiry/search handler */
+struct search_context {
+       char            *svc;           /* Service */
+       uuid_t          group;          /* Browse group */
+       int             view;           /* View mode */
+       uint32_t        handle;         /* Service record handle */
+};
+
+typedef int (*handler_t)(bdaddr_t *bdaddr, struct search_context *arg);
+
+static char UUID_str[MAX_LEN_UUID_STR];
+static bdaddr_t interface;
+
+/* Definition of attribute members */
+struct member_def {
+       char *name;
+};
+
+/* Definition of an attribute */
+struct attrib_def {
+       int                     num;            /* Numeric ID - 16 bits */
+       char                    *name;          /* User readable name */
+       struct member_def       *members;       /* Definition of attribute args */
+       int                     member_max;     /* Max of attribute arg definitions */
+};
+
+/* Definition of a service or protocol */
+struct uuid_def {
+       int                     num;            /* Numeric ID - 16 bits */
+       char                    *name;          /* User readable name */
+       struct attrib_def       *attribs;       /* Specific attribute definitions */
+       int                     attrib_max;     /* Max of attribute definitions */
+};
+
+/* Context information about current attribute */
+struct attrib_context {
+       struct uuid_def         *service;       /* Service UUID, if known */
+       struct attrib_def       *attrib;        /* Description of the attribute */
+       int                     member_index;   /* Index of current attribute member */
+};
+
+/* Context information about the whole service */
+struct service_context {
+       struct uuid_def         *service;       /* Service UUID, if known */
+};
+
+/* Allow us to do nice formatting of the lists */
+static char *indent_spaces = "                                         ";
+
+/* ID of the service attribute.
+ * Most attributes after 0x200 are defined based on the service, so
+ * we need to find what is the service (which is messy) - Jean II */
+#define SERVICE_ATTR   0x1
+
+/* Definition of the optional arguments in protocol list */
+static struct member_def protocol_members[] = {
+       { "Protocol"            },
+       { "Channel/Port"        },
+       { "Version"             },
+};
+
+/* Definition of the optional arguments in profile list */
+static struct member_def profile_members[] = {
+       { "Profile"     },
+       { "Version"     },
+};
+
+/* Definition of the optional arguments in Language list */
+static struct member_def language_members[] = {
+       { "Code ISO639"         },
+       { "Encoding"            },
+       { "Base Offset"         },
+};
+
+/* Name of the various common attributes. See BT assigned numbers */
+static struct attrib_def attrib_names[] = {
+       { 0x0, "ServiceRecordHandle", NULL, 0 },
+       { 0x1, "ServiceClassIDList", NULL, 0 },
+       { 0x2, "ServiceRecordState", NULL, 0 },
+       { 0x3, "ServiceID", NULL, 0 },
+       { 0x4, "ProtocolDescriptorList",
+               protocol_members, sizeof(protocol_members)/sizeof(struct member_def) },
+       { 0x5, "BrowseGroupList", NULL, 0 },
+       { 0x6, "LanguageBaseAttributeIDList",
+               language_members, sizeof(language_members)/sizeof(struct member_def) },
+       { 0x7, "ServiceInfoTimeToLive", NULL, 0 },
+       { 0x8, "ServiceAvailability", NULL, 0 },
+       { 0x9, "BluetoothProfileDescriptorList",
+               profile_members, sizeof(profile_members)/sizeof(struct member_def) },
+       { 0xA, "DocumentationURL", NULL, 0 },
+       { 0xB, "ClientExecutableURL", NULL, 0 },
+       { 0xC, "IconURL", NULL, 0 },
+       { 0xD, "AdditionalProtocolDescriptorLists", NULL, 0 },
+       /* Definitions after that are tricky (per profile or offset) */
+};
+
+const int attrib_max = sizeof(attrib_names)/sizeof(struct attrib_def);
+
+/* Name of the various SPD attributes. See BT assigned numbers */
+static struct attrib_def sdp_attrib_names[] = {
+       { 0x200, "VersionNumberList", NULL, 0 },
+       { 0x201, "ServiceDatabaseState", NULL, 0 },
+};
+
+/* Name of the various SPD attributes. See BT assigned numbers */
+static struct attrib_def browse_attrib_names[] = {
+       { 0x200, "GroupID", NULL, 0 },
+};
+
+/* Name of the various Device ID attributes. See Device Id spec. */
+static struct attrib_def did_attrib_names[] = {
+       { 0x200, "SpecificationID", NULL, 0 },
+       { 0x201, "VendorID", NULL, 0 },
+       { 0x202, "ProductID", NULL, 0 },
+       { 0x203, "Version", NULL, 0 },
+       { 0x204, "PrimaryRecord", NULL, 0 },
+       { 0x205, "VendorIDSource", NULL, 0 },
+};
+
+/* Name of the various HID attributes. See HID spec. */
+static struct attrib_def hid_attrib_names[] = {
+       { 0x200, "DeviceReleaseNum", NULL, 0 },
+       { 0x201, "ParserVersion", NULL, 0 },
+       { 0x202, "DeviceSubclass", NULL, 0 },
+       { 0x203, "CountryCode", NULL, 0 },
+       { 0x204, "VirtualCable", NULL, 0 },
+       { 0x205, "ReconnectInitiate", NULL, 0 },
+       { 0x206, "DescriptorList", NULL, 0 },
+       { 0x207, "LangIDBaseList", NULL, 0 },
+       { 0x208, "SDPDisable", NULL, 0 },
+       { 0x209, "BatteryPower", NULL, 0 },
+       { 0x20a, "RemoteWakeup", NULL, 0 },
+       { 0x20b, "ProfileVersion", NULL, 0 },
+       { 0x20c, "SupervisionTimeout", NULL, 0 },
+       { 0x20d, "NormallyConnectable", NULL, 0 },
+       { 0x20e, "BootDevice", NULL, 0 },
+};
+
+/* Name of the various PAN attributes. See BT assigned numbers */
+/* Note : those need to be double checked - Jean II */
+static struct attrib_def pan_attrib_names[] = {
+       { 0x200, "IpSubnet", NULL, 0 },         /* Obsolete ??? */
+       { 0x30A, "SecurityDescription", NULL, 0 },
+       { 0x30B, "NetAccessType", NULL, 0 },
+       { 0x30C, "MaxNetAccessrate", NULL, 0 },
+       { 0x30D, "IPv4Subnet", NULL, 0 },
+       { 0x30E, "IPv6Subnet", NULL, 0 },
+};
+
+/* Name of the various Generic-Audio attributes. See BT assigned numbers */
+/* Note : totally untested - Jean II */
+static struct attrib_def audio_attrib_names[] = {
+       { 0x302, "Remote audio volume control", NULL, 0 },
+};
+
+/* Name of the various GOEP attributes. See BT assigned numbers */
+static struct attrib_def goep_attrib_names[] = {
+       { 0x200, "GoepL2capPsm", NULL, 0 },
+};
+
+/* Same for the UUIDs. See BT assigned numbers */
+static struct uuid_def uuid16_names[] = {
+       /* -- Protocols -- */
+       { 0x0001, "SDP", NULL, 0 },
+       { 0x0002, "UDP", NULL, 0 },
+       { 0x0003, "RFCOMM", NULL, 0 },
+       { 0x0004, "TCP", NULL, 0 },
+       { 0x0005, "TCS-BIN", NULL, 0 },
+       { 0x0006, "TCS-AT", NULL, 0 },
+       { 0x0008, "OBEX", NULL, 0 },
+       { 0x0009, "IP", NULL, 0 },
+       { 0x000a, "FTP", NULL, 0 },
+       { 0x000c, "HTTP", NULL, 0 },
+       { 0x000e, "WSP", NULL, 0 },
+       { 0x000f, "BNEP", NULL, 0 },
+       { 0x0010, "UPnP/ESDP", NULL, 0 },
+       { 0x0011, "HIDP", NULL, 0 },
+       { 0x0012, "HardcopyControlChannel", NULL, 0 },
+       { 0x0014, "HardcopyDataChannel", NULL, 0 },
+       { 0x0016, "HardcopyNotification", NULL, 0 },
+       { 0x0017, "AVCTP", NULL, 0 },
+       { 0x0019, "AVDTP", NULL, 0 },
+       { 0x001b, "CMTP", NULL, 0 },
+       { 0x001d, "UDI_C-Plane", NULL, 0 },
+       { 0x0100, "L2CAP", NULL, 0 },
+       /* -- Services -- */
+       { 0x1000, "ServiceDiscoveryServerServiceClassID",
+               sdp_attrib_names, sizeof(sdp_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1001, "BrowseGroupDescriptorServiceClassID",
+               browse_attrib_names, sizeof(browse_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1002, "PublicBrowseGroup", NULL, 0 },
+       { 0x1101, "SerialPort", NULL, 0 },
+       { 0x1102, "LANAccessUsingPPP", NULL, 0 },
+       { 0x1103, "DialupNetworking (DUN)", NULL, 0 },
+       { 0x1104, "IrMCSync", NULL, 0 },
+       { 0x1105, "OBEXObjectPush",
+               goep_attrib_names, sizeof(goep_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1106, "OBEXFileTransfer",
+               goep_attrib_names, sizeof(goep_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1107, "IrMCSyncCommand", NULL, 0 },
+       { 0x1108, "Headset",
+               audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1109, "CordlessTelephony", NULL, 0 },
+       { 0x110a, "AudioSource", NULL, 0 },
+       { 0x110b, "AudioSink", NULL, 0 },
+       { 0x110c, "RemoteControlTarget", NULL, 0 },
+       { 0x110d, "AdvancedAudio", NULL, 0 },
+       { 0x110e, "RemoteControl", NULL, 0 },
+       { 0x110f, "VideoConferencing", NULL, 0 },
+       { 0x1110, "Intercom", NULL, 0 },
+       { 0x1111, "Fax", NULL, 0 },
+       { 0x1112, "HeadsetAudioGateway", NULL, 0 },
+       { 0x1113, "WAP", NULL, 0 },
+       { 0x1114, "WAP Client", NULL, 0 },
+       { 0x1115, "PANU (PAN/BNEP)",
+               pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1116, "NAP (PAN/BNEP)",
+               pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1117, "GN (PAN/BNEP)",
+               pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1118, "DirectPrinting (BPP)", NULL, 0 },
+       { 0x1119, "ReferencePrinting (BPP)", NULL, 0 },
+       { 0x111a, "Imaging (BIP)", NULL, 0 },
+       { 0x111b, "ImagingResponder (BIP)", NULL, 0 },
+       { 0x111c, "ImagingAutomaticArchive (BIP)", NULL, 0 },
+       { 0x111d, "ImagingReferencedObjects (BIP)", NULL, 0 },
+       { 0x111e, "Handsfree", NULL, 0 },
+       { 0x111f, "HandsfreeAudioGateway", NULL, 0 },
+       { 0x1120, "DirectPrintingReferenceObjectsService (BPP)", NULL, 0 },
+       { 0x1121, "ReflectedUI (BPP)", NULL, 0 },
+       { 0x1122, "BasicPrinting (BPP)", NULL, 0 },
+       { 0x1123, "PrintingStatus (BPP)", NULL, 0 },
+       { 0x1124, "HumanInterfaceDeviceService (HID)",
+               hid_attrib_names, sizeof(hid_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1125, "HardcopyCableReplacement (HCR)", NULL, 0 },
+       { 0x1126, "HCR_Print (HCR)", NULL, 0 },
+       { 0x1127, "HCR_Scan (HCR)", NULL, 0 },
+       { 0x1128, "Common ISDN Access (CIP)", NULL, 0 },
+       { 0x1129, "VideoConferencingGW (VCP)", NULL, 0 },
+       { 0x112a, "UDI-MT", NULL, 0 },
+       { 0x112b, "UDI-TA", NULL, 0 },
+       { 0x112c, "Audio/Video", NULL, 0 },
+       { 0x112d, "SIM Access (SAP)", NULL, 0 },
+       { 0x112e, "Phonebook Access (PBAP) - PCE", NULL, 0 },
+       { 0x112f, "Phonebook Access (PBAP) - PSE", NULL, 0 },
+       { 0x1130, "Phonebook Access (PBAP)", NULL, 0 },
+       /* ... */
+       { 0x1200, "PnPInformation",
+               did_attrib_names, sizeof(did_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1201, "GenericNetworking", NULL, 0 },
+       { 0x1202, "GenericFileTransfer", NULL, 0 },
+       { 0x1203, "GenericAudio",
+               audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
+       { 0x1204, "GenericTelephony", NULL, 0 },
+       /* ... */
+       { 0x1303, "VideoSource", NULL, 0 },
+       { 0x1304, "VideoSink", NULL, 0 },
+       { 0x1305, "VideoDistribution", NULL, 0 },
+       { 0x1400, "HDP", NULL, 0 },
+       { 0x1401, "HDPSource", NULL, 0 },
+       { 0x1402, "HDPSink", NULL, 0 },
+       { 0x2112, "AppleAgent", NULL, 0 },
+};
+
+static const int uuid16_max = sizeof(uuid16_names)/sizeof(struct uuid_def);
+
+static void sdp_data_printf(sdp_data_t *, struct attrib_context *, int);
+
+/*
+ * Parse a UUID.
+ * The BT assigned numbers only list UUID16, so I'm not sure the
+ * other types will ever get used...
+ */
+static void sdp_uuid_printf(uuid_t *uuid, struct attrib_context *context, int indent)
+{
+       if (uuid) {
+               if (uuid->type == SDP_UUID16) {
+                       uint16_t uuidNum = uuid->value.uuid16;
+                       struct uuid_def *uuidDef = NULL;
+                       int i;
+
+                       for (i = 0; i < uuid16_max; i++)
+                               if (uuid16_names[i].num == uuidNum) {
+                                       uuidDef = &uuid16_names[i];
+                                       break;
+                               }
+
+                       /* Check if it's the service attribute */
+                       if (context->attrib && context->attrib->num == SERVICE_ATTR) {
+                               /* We got the service ID !!! */
+                               context->service = uuidDef;
+                       }
+
+                       if (uuidDef)
+                               printf("%.*sUUID16 : 0x%.4x - %s\n",
+                                       indent, indent_spaces, uuidNum, uuidDef->name);
+                       else
+                               printf("%.*sUUID16 : 0x%.4x\n",
+                                       indent, indent_spaces, uuidNum);
+               } else if (uuid->type == SDP_UUID32) {
+                       struct uuid_def *uuidDef = NULL;
+                       int i;
+
+                       if (!(uuid->value.uuid32 & 0xffff0000)) {
+                               uint16_t uuidNum = uuid->value.uuid32;
+                               for (i = 0; i < uuid16_max; i++)
+                                       if (uuid16_names[i].num == uuidNum) {
+                                               uuidDef = &uuid16_names[i];
+                                               break;
+                                       }
+                       }
+
+                       if (uuidDef)
+                               printf("%.*sUUID32 : 0x%.8x - %s\n",
+                                       indent, indent_spaces, uuid->value.uuid32, uuidDef->name);
+                       else
+                               printf("%.*sUUID32 : 0x%.8x\n",
+                                       indent, indent_spaces, uuid->value.uuid32);
+               } else if (uuid->type == SDP_UUID128) {
+                       unsigned int data0;
+                       unsigned short data1;
+                       unsigned short data2;
+                       unsigned short data3;
+                       unsigned int data4;
+                       unsigned short data5;
+
+                       memcpy(&data0, &uuid->value.uuid128.data[0], 4);
+                       memcpy(&data1, &uuid->value.uuid128.data[4], 2);
+                       memcpy(&data2, &uuid->value.uuid128.data[6], 2);
+                       memcpy(&data3, &uuid->value.uuid128.data[8], 2);
+                       memcpy(&data4, &uuid->value.uuid128.data[10], 4);
+                       memcpy(&data5, &uuid->value.uuid128.data[14], 2);
+
+                       printf("%.*sUUID128 : 0x%.8x-%.4x-%.4x-%.4x-%.8x-%.4x\n",
+                               indent, indent_spaces,
+                               ntohl(data0), ntohs(data1), ntohs(data2),
+                               ntohs(data3), ntohl(data4), ntohs(data5));
+               } else
+                       printf("%.*sEnum type of UUID not set\n",
+                               indent, indent_spaces);
+       } else
+               printf("%.*sNull passed to print UUID\n",
+                               indent, indent_spaces);
+}
+
+/*
+ * Parse a sequence of data elements (i.e. a list)
+ */
+static void printf_dataseq(sdp_data_t * pData, struct attrib_context *context, int indent)
+{
+       sdp_data_t *sdpdata = NULL;
+
+       sdpdata = pData;
+       if (sdpdata) {
+               context->member_index = 0;
+               do {
+                       sdp_data_printf(sdpdata, context, indent + 2);
+                       sdpdata = sdpdata->next;
+                       context->member_index++;
+               } while (sdpdata);
+       } else {
+               printf("%.*sBroken dataseq link\n", indent, indent_spaces);
+       }
+}
+
+/*
+ * Parse a single data element (either in the attribute or in a data
+ * sequence).
+ */
+static void sdp_data_printf(sdp_data_t *sdpdata, struct attrib_context *context, int indent)
+{
+       char *member_name = NULL;
+
+       /* Find member name. Almost black magic ;-) */
+       if (context && context->attrib && context->attrib->members &&
+                       context->member_index < context->attrib->member_max) {
+               member_name = context->attrib->members[context->member_index].name;
+       }
+
+       switch (sdpdata->dtd) {
+       case SDP_DATA_NIL:
+               printf("%.*sNil\n", indent, indent_spaces);
+               break;
+       case SDP_BOOL:
+       case SDP_UINT8:
+       case SDP_UINT16:
+       case SDP_UINT32:
+       case SDP_UINT64:
+       case SDP_UINT128:
+       case SDP_INT8:
+       case SDP_INT16:
+       case SDP_INT32:
+       case SDP_INT64:
+       case SDP_INT128:
+               if (member_name) {
+                       printf("%.*s%s (Integer) : 0x%x\n",
+                               indent, indent_spaces, member_name, sdpdata->val.uint32);
+               } else {
+                       printf("%.*sInteger : 0x%x\n", indent, indent_spaces,
+                               sdpdata->val.uint32);
+               }
+               break;
+
+       case SDP_UUID16:
+       case SDP_UUID32:
+       case SDP_UUID128:
+               //printf("%.*sUUID\n", indent, indent_spaces);
+               sdp_uuid_printf(&sdpdata->val.uuid, context, indent);
+               break;
+
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+       case SDP_TEXT_STR32:
+               if (sdpdata->unitSize > (int) strlen(sdpdata->val.str)) {
+                       int i;
+                       printf("%.*sData :", indent, indent_spaces);
+                       for (i = 0; i < sdpdata->unitSize; i++)
+                               printf(" %02x", (unsigned char) sdpdata->val.str[i]);
+                       printf("\n");
+               } else
+                       printf("%.*sText : \"%s\"\n", indent, indent_spaces, sdpdata->val.str);
+               break;
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_URL_STR32:
+               printf("%.*sURL : %s\n", indent, indent_spaces, sdpdata->val.str);
+               break;
+
+       case SDP_SEQ8:
+       case SDP_SEQ16:
+       case SDP_SEQ32:
+               printf("%.*sData Sequence\n", indent, indent_spaces);
+               printf_dataseq(sdpdata->val.dataseq, context, indent);
+               break;
+
+       case SDP_ALT8:
+       case SDP_ALT16:
+       case SDP_ALT32:
+               printf("%.*sData Sequence Alternates\n", indent, indent_spaces);
+               printf_dataseq(sdpdata->val.dataseq, context, indent);
+               break;
+       }
+}
+
+/*
+ * Parse a single attribute.
+ */
+static void print_tree_attr_func(void *value, void *userData)
+{
+       sdp_data_t *sdpdata = value;
+       uint16_t attrId;
+       struct service_context *service = (struct service_context *) userData;
+       struct attrib_context context;
+       struct attrib_def *attrDef = NULL;
+       int i;
+
+       if (!sdpdata)
+               return;
+
+       attrId = sdpdata->attrId;
+       /* Search amongst the generic attributes */
+       for (i = 0; i < attrib_max; i++)
+               if (attrib_names[i].num == attrId) {
+                       attrDef = &attrib_names[i];
+                       break;
+               }
+       /* Search amongst the specific attributes of this service */
+       if ((attrDef == NULL) && (service->service != NULL) &&
+                               (service->service->attribs != NULL)) {
+               struct attrib_def *svc_attribs = service->service->attribs;
+               int             svc_attrib_max = service->service->attrib_max;
+               for (i = 0; i < svc_attrib_max; i++)
+                       if (svc_attribs[i].num == attrId) {
+                               attrDef = &svc_attribs[i];
+                               break;
+                       }
+       }
+
+       if (attrDef)
+               printf("Attribute Identifier : 0x%x - %s\n", attrId, attrDef->name);
+       else
+               printf("Attribute Identifier : 0x%x\n", attrId);
+       /* Build context */
+       context.service = service->service;
+       context.attrib = attrDef;
+       context.member_index = 0;
+       /* Parse attribute members */
+       sdp_data_printf(sdpdata, &context, 2);
+       /* Update service */
+       service->service = context.service;
+}
+
+/*
+ * Main entry point of this library. Parse a SDP record.
+ * We assume the record has already been read, parsed and cached
+ * locally. Jean II
+ */
+static void print_tree_attr(sdp_record_t *rec)
+{
+       if (rec && rec->attrlist) {
+               struct service_context service = { NULL };
+               sdp_list_foreach(rec->attrlist, print_tree_attr_func, &service);
+       }
+}
+
+static void print_raw_data(sdp_data_t *data, int indent)
+{
+       struct uuid_def *def;
+       int i, hex;
+
+       if (!data)
+               return;
+
+       for (i = 0; i < indent; i++)
+               printf("\t");
+
+       switch (data->dtd) {
+       case SDP_DATA_NIL:
+               printf("NIL\n");
+               break;
+       case SDP_BOOL:
+               printf("Bool %s\n", data->val.uint8 ? "True" : "False");
+               break;
+       case SDP_UINT8:
+               printf("UINT8 0x%02x\n", data->val.uint8);
+               break;
+       case SDP_UINT16:
+               printf("UINT16 0x%04x\n", data->val.uint16);
+               break;
+       case SDP_UINT32:
+               printf("UINT32 0x%08x\n", data->val.uint32);
+               break;
+       case SDP_UINT64:
+               printf("UINT64 0x%016jx\n", data->val.uint64);
+               break;
+       case SDP_UINT128:
+               printf("UINT128 ...\n");
+               break;
+       case SDP_INT8:
+               printf("INT8 %d\n", data->val.int8);
+               break;
+       case SDP_INT16:
+               printf("INT16 %d\n", data->val.int16);
+               break;
+       case SDP_INT32:
+               printf("INT32 %d\n", data->val.int32);
+               break;
+       case SDP_INT64:
+               printf("INT64 %jd\n", data->val.int64);
+               break;
+       case SDP_INT128:
+               printf("INT128 ...\n");
+               break;
+       case SDP_UUID16:
+       case SDP_UUID32:
+       case SDP_UUID128:
+               switch (data->val.uuid.type) {
+               case SDP_UUID16:
+                       def = NULL;
+                       for (i = 0; i < uuid16_max; i++)
+                               if (uuid16_names[i].num == data->val.uuid.value.uuid16) {
+                                       def = &uuid16_names[i];
+                                       break;
+                               }
+                       if (def)
+                               printf("UUID16 0x%04x - %s\n", data->val.uuid.value.uuid16, def->name);
+                       else
+                               printf("UUID16 0x%04x\n", data->val.uuid.value.uuid16);
+                       break;
+               case SDP_UUID32:
+                       def = NULL;
+                       if (!(data->val.uuid.value.uuid32 & 0xffff0000)) {
+                               uint16_t value = data->val.uuid.value.uuid32;
+                               for (i = 0; i < uuid16_max; i++)
+                                       if (uuid16_names[i].num == value) {
+                                               def = &uuid16_names[i];
+                                               break;
+                                       }
+                       }
+                       if (def)
+                               printf("UUID32 0x%08x - %s\n", data->val.uuid.value.uuid32, def->name);
+                       else
+                               printf("UUID32 0x%08x\n", data->val.uuid.value.uuid32);
+                       break;
+               case SDP_UUID128:
+                       printf("UUID128 ");
+                       for (i = 0; i < 16; i++) {
+                               switch (i) {
+                               case 4:
+                               case 6:
+                               case 8:
+                               case 10:
+                                       printf("-");
+                                       break;
+                               }
+                               printf("%02x", (unsigned char ) data->val.uuid.value.uuid128.data[i]);
+                       }
+                       printf("\n");
+                       break;
+               default:
+                       printf("UUID type 0x%02x\n", data->val.uuid.type);
+                       break;
+               }
+               break;
+       case SDP_TEXT_STR8:
+       case SDP_TEXT_STR16:
+       case SDP_TEXT_STR32:
+               hex = 0;
+               for (i = 0; i < data->unitSize; i++) {
+                       if (i == (data->unitSize - 1) && data->val.str[i] == '\0')
+                               break;
+                       if (!isprint(data->val.str[i])) {
+                               hex = 1;
+                               break;
+                       }
+               }
+               if (hex) {
+                       printf("Data");
+                       for (i = 0; i < data->unitSize; i++)
+                               printf(" %02x", (unsigned char) data->val.str[i]);
+               } else {
+                       printf("String ");
+                       for (i = 0; i < data->unitSize; i++)
+                               printf("%c", data->val.str[i]);
+               }
+               printf("\n");
+               break;
+       case SDP_URL_STR8:
+       case SDP_URL_STR16:
+       case SDP_URL_STR32:
+               printf("URL %s\n", data->val.str);
+               break;
+       case SDP_SEQ8:
+       case SDP_SEQ16:
+       case SDP_SEQ32:
+               printf("Sequence\n");
+               print_raw_data(data->val.dataseq, indent + 1);
+               break;
+       case SDP_ALT8:
+       case SDP_ALT16:
+       case SDP_ALT32:
+               printf("Alternate\n");
+               print_raw_data(data->val.dataseq, indent + 1);
+               break;
+       default:
+               printf("Unknown type 0x%02x\n", data->dtd);
+               break;
+       }
+
+       print_raw_data(data->next, indent);
+}
+
+static void print_raw_attr_func(void *value, void *userData)
+{
+       sdp_data_t *data = (sdp_data_t *) value;
+       struct attrib_def *def = NULL;
+       int i;
+
+       if (!data)
+               return;
+
+       /* Search amongst the generic attributes */
+       for (i = 0; i < attrib_max; i++)
+               if (attrib_names[i].num == data->attrId) {
+                       def = &attrib_names[i];
+                       break;
+               }
+
+       if (def)
+               printf("\tAttribute 0x%04x - %s\n", data->attrId, def->name);
+       else
+               printf("\tAttribute 0x%04x\n", data->attrId);
+
+       print_raw_data(data, 2);
+}
+
+static void print_raw_attr(sdp_record_t *rec)
+{
+       if (rec && rec->attrlist) {
+               printf("Sequence\n");
+               sdp_list_foreach(rec->attrlist, print_raw_attr_func, 0);
+       }
+}
+
+/*
+ * Set attributes with single values in SDP record
+ * Jean II
+ */
+static int set_attrib(sdp_session_t *sess, uint32_t handle, uint16_t attrib, char *value)
+{
+       sdp_list_t *attrid_list;
+       uint32_t range = 0x0000ffff;
+       sdp_record_t *rec;
+       int ret;
+
+       /* Get the old SDP record */
+       attrid_list = sdp_list_append(NULL, &range);
+       rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attrid_list);
+       sdp_list_free(attrid_list, NULL);
+
+       if (!rec) {
+               printf("Service get request failed.\n");
+               return -1;
+       }
+
+       /* Check the type of attribute */
+       if (!strncasecmp(value, "u0x", 3)) {
+               /* UUID16 */
+               uint16_t value_int = 0;
+               uuid_t value_uuid;
+               value_int = strtoul(value + 3, NULL, 16);
+               sdp_uuid16_create(&value_uuid, value_int);
+               printf("Adding attrib 0x%X uuid16 0x%X to record 0x%X\n",
+                       attrib, value_int, handle);
+
+               sdp_attr_add_new(rec, attrib, SDP_UUID16, &value_uuid.value.uuid16);
+       } else if (!strncasecmp(value, "0x", 2)) {
+               /* Int */
+               uint32_t value_int;
+               value_int = strtoul(value + 2, NULL, 16);
+               printf("Adding attrib 0x%X int 0x%X to record 0x%X\n",
+                       attrib, value_int, handle);
+
+               sdp_attr_add_new(rec, attrib, SDP_UINT32, &value_int);
+       } else {
+               /* String */
+               printf("Adding attrib 0x%X string \"%s\" to record 0x%X\n",
+                       attrib, value, handle);
+
+               /* Add/Update our attribute to the record */
+               sdp_attr_add_new(rec, attrib, SDP_TEXT_STR8, value);
+       }
+
+       /* Update on the server */
+       ret = sdp_device_record_update(sess, &interface, rec);
+       if (ret < 0)
+               printf("Service Record update failed (%d).\n", errno);
+       sdp_record_free(rec);
+       return ret;
+}
+
+static struct option set_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *set_help =
+       "Usage:\n"
+       "\tget record_handle attrib_id attrib_value\n";
+
+/*
+ * Add an attribute to an existing SDP record on the local SDP server
+ */
+static int cmd_setattr(int argc, char **argv)
+{
+       int opt, status;
+       uint32_t handle;
+       uint16_t attrib;
+       sdp_session_t *sess;
+
+       for_each_opt(opt, set_options, NULL) {
+               switch(opt) {
+               default:
+                       printf("%s", set_help);
+                       return -1;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 3) {
+               printf("%s", set_help);
+               return -1;
+       }
+
+       /* Convert command line args */
+       handle = strtoul(argv[0], NULL, 16);
+       attrib = strtoul(argv[1], NULL, 16);
+
+       /* Do it */
+       sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+       if (!sess)
+               return -1;
+
+       status = set_attrib(sess, handle, attrib, argv[2]);
+       sdp_close(sess);
+
+       return status;
+}
+
+/*
+ * We do only simple data sequences. Sequence of sequences is a pain ;-)
+ * Jean II
+ */
+static int set_attribseq(sdp_session_t *session, uint32_t handle, uint16_t attrib, int argc, char **argv)
+{
+       sdp_list_t *attrid_list;
+       uint32_t range = 0x0000ffff;
+       sdp_record_t *rec;
+       sdp_data_t *pSequenceHolder = NULL;
+       void **dtdArray;
+       void **valueArray;
+       void **allocArray;
+       uint8_t uuid16 = SDP_UUID16;
+       uint8_t uint32 = SDP_UINT32;
+       uint8_t str8 = SDP_TEXT_STR8;
+       int i, ret = 0;
+
+       /* Get the old SDP record */
+       attrid_list = sdp_list_append(NULL, &range);
+       rec = sdp_service_attr_req(session, handle, SDP_ATTR_REQ_RANGE, attrid_list);
+       sdp_list_free(attrid_list, NULL);
+
+       if (!rec) {
+               printf("Service get request failed.\n");
+               return -1;
+       }
+
+       /* Create arrays */
+       dtdArray = (void **)malloc(argc * sizeof(void *));
+       valueArray = (void **)malloc(argc * sizeof(void *));
+       allocArray = (void **)malloc(argc * sizeof(void *));
+
+       /* Loop on all args, add them in arrays */
+       for (i = 0; i < argc; i++) {
+               /* Check the type of attribute */
+               if (!strncasecmp(argv[i], "u0x", 3)) {
+                       /* UUID16 */
+                       uint16_t value_int = strtoul((argv[i]) + 3, NULL, 16);
+                       uuid_t *value_uuid = (uuid_t *) malloc(sizeof(uuid_t));
+                       allocArray[i] = value_uuid;
+                       sdp_uuid16_create(value_uuid, value_int);
+
+                       printf("Adding uuid16 0x%X to record 0x%X\n", value_int, handle);
+                       dtdArray[i] = &uuid16;
+                       valueArray[i] = &value_uuid->value.uuid16;
+               } else if (!strncasecmp(argv[i], "0x", 2)) {
+                       /* Int */
+                       uint32_t *value_int = (uint32_t *) malloc(sizeof(int));
+                       allocArray[i] = value_int;
+                       *value_int = strtoul((argv[i]) + 2, NULL, 16);
+
+                       printf("Adding int 0x%X to record 0x%X\n", *value_int, handle);
+                       dtdArray[i] = &uint32;
+                       valueArray[i] = value_int;
+               } else {
+                       /* String */
+                       printf("Adding string \"%s\" to record 0x%X\n", argv[i], handle);
+                       dtdArray[i] = &str8;
+                       valueArray[i] = argv[i];
+               }
+       }
+
+       /* Add this sequence to the attrib list */
+       pSequenceHolder = sdp_seq_alloc(dtdArray, valueArray, argc);
+       if (pSequenceHolder) {
+               sdp_attr_replace(rec, attrib, pSequenceHolder);
+
+               /* Update on the server */
+               ret = sdp_device_record_update(session, &interface, rec);
+               if (ret < 0)
+                       printf("Service Record update failed (%d).\n", errno);
+       } else
+               printf("Failed to create pSequenceHolder\n");
+
+       /* Cleanup */
+       for (i = 0; i < argc; i++)
+               free(allocArray[i]);
+
+       free(dtdArray);
+       free(valueArray);
+       free(allocArray);
+
+       sdp_record_free(rec);
+
+       return ret;
+}
+
+static struct option seq_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *seq_help =
+       "Usage:\n"
+       "\tget record_handle attrib_id attrib_values\n";
+
+/*
+ * Add an attribute sequence to an existing SDP record
+ * on the local SDP server
+ */
+static int cmd_setseq(int argc, char **argv)
+{
+       int opt, status;
+       uint32_t handle;
+       uint16_t attrib;
+       sdp_session_t *sess;
+
+       for_each_opt(opt, seq_options, NULL) {
+               switch(opt) {
+               default:
+                       printf("%s", seq_help);
+                       return -1;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 3) {
+               printf("%s", seq_help);
+               return -1;
+       }
+
+       /* Convert command line args */
+       handle = strtoul(argv[0], NULL, 16);
+       attrib = strtoul(argv[1], NULL, 16);
+
+       argc -= 2;
+       argv += 2;
+
+       /* Do it */
+       sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
+       if (!sess)
+               return -1;
+
+       status = set_attribseq(sess, handle, attrib, argc, argv);
+       sdp_close(sess);
+
+       return status;
+}
+
+static void print_service_class(void *value, void *userData)
+{
+       char ServiceClassUUID_str[MAX_LEN_SERVICECLASS_UUID_STR];
+       uuid_t *uuid = (uuid_t *)value;
+
+       sdp_uuid2strn(uuid, UUID_str, MAX_LEN_UUID_STR);
+       sdp_svclass_uuid2strn(uuid, ServiceClassUUID_str, MAX_LEN_SERVICECLASS_UUID_STR);
+       if (uuid->type != SDP_UUID128)
+               printf("  \"%s\" (0x%s)\n", ServiceClassUUID_str, UUID_str);
+       else
+               printf("  UUID 128: %s\n", UUID_str);
+}
+
+static void print_service_desc(void *value, void *user)
+{
+       char str[MAX_LEN_PROTOCOL_UUID_STR];
+       sdp_data_t *p = (sdp_data_t *)value, *s;
+       int i = 0, proto = 0;
+
+       for (; p; p = p->next, i++) {
+               switch (p->dtd) {
+               case SDP_UUID16:
+               case SDP_UUID32:
+               case SDP_UUID128:
+                       sdp_uuid2strn(&p->val.uuid, UUID_str, MAX_LEN_UUID_STR);
+                       sdp_proto_uuid2strn(&p->val.uuid, str, sizeof(str));
+                       proto = sdp_uuid_to_proto(&p->val.uuid);
+                       printf("  \"%s\" (0x%s)\n", str, UUID_str);
+                       break;
+               case SDP_UINT8:
+                       if (proto == RFCOMM_UUID)
+                               printf("    Channel: %d\n", p->val.uint8);
+                       else
+                               printf("    uint8: 0x%x\n", p->val.uint8);
+                       break;
+               case SDP_UINT16:
+                       if (proto == L2CAP_UUID) {
+                               if (i == 1)
+                                       printf("    PSM: %d\n", p->val.uint16);
+                               else
+                                       printf("    Version: 0x%04x\n", p->val.uint16);
+                       } else if (proto == BNEP_UUID)
+                               if (i == 1)
+                                       printf("    Version: 0x%04x\n", p->val.uint16);
+                               else
+                                       printf("    uint16: 0x%x\n", p->val.uint16);
+                       else
+                               printf("    uint16: 0x%x\n", p->val.uint16);
+                       break;
+               case SDP_SEQ16:
+                       printf("    SEQ16:");
+                       for (s = p->val.dataseq; s; s = s->next)
+                               printf(" %x", s->val.uint16);
+                       printf("\n");
+                       break;
+               case SDP_SEQ8:
+                       printf("    SEQ8:");
+                       for (s = p->val.dataseq; s; s = s->next)
+                               printf(" %x", s->val.uint8);
+                       printf("\n");
+                       break;
+               default:
+                       printf("    FIXME: dtd=0%x\n", p->dtd);
+                       break;
+               }
+       }
+}
+
+static void print_lang_attr(void *value, void *user)
+{
+       sdp_lang_attr_t *lang = (sdp_lang_attr_t *)value;
+       printf("  code_ISO639: 0x%02x\n", lang->code_ISO639);
+       printf("  encoding:    0x%02x\n", lang->encoding);
+       printf("  base_offset: 0x%02x\n", lang->base_offset);
+}
+
+static void print_access_protos(void *value, void *userData)
+{
+       sdp_list_t *protDescSeq = (sdp_list_t *)value;
+       sdp_list_foreach(protDescSeq, print_service_desc, 0);
+}
+
+static void print_profile_desc(void *value, void *userData)
+{
+       sdp_profile_desc_t *desc = (sdp_profile_desc_t *)value;
+       char str[MAX_LEN_PROFILEDESCRIPTOR_UUID_STR];
+
+       sdp_uuid2strn(&desc->uuid, UUID_str, MAX_LEN_UUID_STR);
+       sdp_profile_uuid2strn(&desc->uuid, str, MAX_LEN_PROFILEDESCRIPTOR_UUID_STR);
+
+       printf("  \"%s\" (0x%s)\n", str, UUID_str);
+       if (desc->version)
+               printf("    Version: 0x%04x\n", desc->version);
+}
+
+/*
+ * Parse a SDP record in user friendly form.
+ */
+static void print_service_attr(sdp_record_t *rec)
+{
+       sdp_list_t *list = 0, *proto = 0;
+
+       sdp_record_print(rec);
+
+       printf("Service RecHandle: 0x%x\n", rec->handle);
+
+       if (sdp_get_service_classes(rec, &list) == 0) {
+               printf("Service Class ID List:\n");
+               sdp_list_foreach(list, print_service_class, 0);
+               sdp_list_free(list, free);
+       }
+       if (sdp_get_access_protos(rec, &proto) == 0) {
+               printf("Protocol Descriptor List:\n");
+               sdp_list_foreach(proto, print_access_protos, 0);
+               sdp_list_foreach(proto, (sdp_list_func_t)sdp_list_free, 0);
+               sdp_list_free(proto, 0);
+       }
+       if (sdp_get_lang_attr(rec, &list) == 0) {
+               printf("Language Base Attr List:\n");
+               sdp_list_foreach(list, print_lang_attr, 0);
+               sdp_list_free(list, free);
+       }
+       if (sdp_get_profile_descs(rec, &list) == 0) {
+               printf("Profile Descriptor List:\n");
+               sdp_list_foreach(list, print_profile_desc, 0);
+               sdp_list_free(list, free);
+       }
+}
+
+/*
+ * Support for Service (de)registration
+ */
+typedef struct {
+       uint32_t handle;
+       char *name;
+       char *provider;
+       char *desc;
+       unsigned int class;
+       unsigned int profile;
+       uint16_t psm;
+       uint8_t channel;
+       uint8_t network;
+} svc_info_t;
+
+static int add_sp(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
+       uuid_t root_uuid, sp_uuid, l2cap, rfcomm;
+       sdp_profile_desc_t profile;
+       sdp_record_t record;
+       uint8_t u8 = si->channel ? si->channel : 1;
+       sdp_data_t *channel;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+       sdp_list_free(root, 0);
+
+       sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &sp_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+       sdp_list_free(svclass_id, 0);
+
+       sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
+       profile.version = 0x0100;
+       profiles = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(&record, profiles);
+       sdp_list_free(profiles, 0);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm);
+       channel = sdp_data_alloc(SDP_UINT8, &u8);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_add_lang_attr(&record);
+
+       sdp_set_info_attr(&record, "Serial Port", "BlueZ", "COM Port");
+
+       sdp_set_url_attr(&record, "http://www.bluez.org/",
+                       "http://www.bluez.org/", "http://www.bluez.org/");
+
+       sdp_set_service_id(&record, sp_uuid);
+       sdp_set_service_ttl(&record, 0xffff);
+       sdp_set_service_avail(&record, 0xff);
+       sdp_set_record_state(&record, 0x00001234);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("Serial Port service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_dun(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root, *aproto;
+       uuid_t rootu, dun, gn, l2cap, rfcomm;
+       sdp_profile_desc_t profile;
+       sdp_list_t *proto[2];
+       sdp_record_t record;
+       uint8_t u8 = si->channel ? si->channel : 2;
+       sdp_data_t *channel;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&rootu, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &rootu);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &dun);
+       sdp_uuid16_create(&gn,  GENERIC_NETWORKING_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &gn);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, DIALUP_NET_PROFILE_ID);
+       profile.version = 0x0100;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm);
+       channel = sdp_data_alloc(SDP_UINT8, &u8);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Dial-Up Networking", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("Dial-Up Networking service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_fax(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, fax_uuid, tel_uuid, l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint8_t u8 = si->channel? si->channel : 3;
+       sdp_data_t *channel;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&fax_uuid, FAX_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &fax_uuid);
+       sdp_uuid16_create(&tel_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &tel_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, FAX_PROFILE_ID);
+       profile.version = 0x0100;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &u8);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq  = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Fax", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+       printf("Fax service registered\n");
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+       return ret;
+}
+
+static int add_lan(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint8_t u8 = si->channel ? si->channel : 4;
+       sdp_data_t *channel;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&svclass_uuid, LAN_ACCESS_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &svclass_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, LAN_ACCESS_PROFILE_ID);
+       profile.version = 0x0100;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &u8);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "LAN Access over PPP", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("LAN Access service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_headset(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint8_t u8 = si->channel ? si->channel : 5;
+       sdp_data_t *channel;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &svclass_uuid);
+       sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+       profile.version = 0x0100;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &u8);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Headset", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("Headset service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_headset_ag(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint8_t u8 = si->channel ? si->channel : 7;
+       sdp_data_t *channel;
+       uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
+       sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &svclass_uuid);
+       sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
+       profile.version = 0x0100;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &u8);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
+
+       sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+       if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("Headset AG service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_handsfree(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint8_t u8 = si->channel ? si->channel : 6;
+       uint16_t u16 = 0x31;
+       sdp_data_t *channel, *features;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &svclass_uuid);
+       sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+       profile.version = 0x0101;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &u8);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       features = sdp_data_alloc(SDP_UINT16, &u16);
+       sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Handsfree", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("Handsfree service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_handsfree_ag(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint8_t u8 = si->channel ? si->channel : 7;
+       uint16_t u16 = 0x17;
+       sdp_data_t *channel, *features;
+       uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
+       sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &svclass_uuid);
+       sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+       profile.version = 0x0105;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &u8);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       features = sdp_data_alloc(SDP_UINT16, &u16);
+       sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
+
+       sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+       if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("Handsfree AG service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_simaccess(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint8_t u8 = si->channel? si->channel : 8;
+       uint16_t u16 = 0x31;
+       sdp_data_t *channel, *features;
+       int ret = 0;
+
+       memset((void *)&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&svclass_uuid, SAP_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &svclass_uuid);
+       sdp_uuid16_create(&ga_svclass_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
+       profile.version = 0x0101;
+       pfseq = sdp_list_append(0, &profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &u8);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       features = sdp_data_alloc(SDP_UINT16, &u16);
+       sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "SIM Access", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("SIM Access service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_opush(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[3];
+       sdp_record_t record;
+       uint8_t chan = si->channel ? si->channel : 9;
+       sdp_data_t *channel;
+       uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
+       void *dtds[sizeof(formats)], *values[sizeof(formats)];
+       unsigned int i;
+       uint8_t dtd = SDP_UINT8;
+       sdp_data_t *sflist;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &opush_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &chan);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+       proto[2] = sdp_list_append(0, &obex_uuid);
+       apseq = sdp_list_append(apseq, proto[2]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       for (i = 0; i < sizeof(formats); i++) {
+               dtds[i] = &dtd;
+               values[i] = &formats[i];
+       }
+       sflist = sdp_seq_alloc(dtds, values, sizeof(formats));
+       sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist);
+
+       sdp_set_info_attr(&record, "OBEX Object Push", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("OBEX Object Push service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(proto[2], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_pbap(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, pbap_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[3];
+       sdp_record_t record;
+       uint8_t chan = si->channel ? si->channel : 19;
+       sdp_data_t *channel;
+       uint8_t formats[] = {0x01};
+       uint8_t dtd = SDP_UINT8;
+       sdp_data_t *sflist;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&pbap_uuid, PBAP_PSE_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &pbap_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &chan);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+       proto[2] = sdp_list_append(0, &obex_uuid);
+       apseq = sdp_list_append(apseq, proto[2]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sflist = sdp_data_alloc(dtd,formats);
+       sdp_attr_add(&record, SDP_ATTR_SUPPORTED_REPOSITORIES, sflist);
+
+       sdp_set_info_attr(&record, "OBEX Phonebook Access Server", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record,
+                       SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("PBAP service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(proto[2], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_ftp(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, ftrn_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[3];
+       sdp_record_t record;
+       uint8_t u8 = si->channel ? si->channel: 10;
+       sdp_data_t *channel;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&ftrn_uuid, OBEX_FILETRANS_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &ftrn_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, OBEX_FILETRANS_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &u8);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+       proto[2] = sdp_list_append(0, &obex_uuid);
+       apseq = sdp_list_append(apseq, proto[2]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "OBEX File Transfer", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("OBEX File Transfer service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(proto[2], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_directprint(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[3];
+       sdp_record_t record;
+       uint8_t chan = si->channel ? si->channel : 12;
+       sdp_data_t *channel;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&opush_uuid, DIRECT_PRINTING_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &opush_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, BASIC_PRINTING_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(0, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &chan);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+       proto[2] = sdp_list_append(0, &obex_uuid);
+       apseq = sdp_list_append(apseq, proto[2]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Direct Printing", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("Direct Printing service registered\n");
+
+end:
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(proto[2], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_nap(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint16_t lp = 0x000f, ver = 0x0100;
+       sdp_data_t *psm, *version;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&ftrn_uuid, NAP_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &ftrn_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+       proto[1] = sdp_list_append(0, &bnep_uuid);
+       version  = sdp_data_alloc(SDP_UINT16, &ver);
+       proto[1] = sdp_list_append(proto[1], version);
+
+       {
+               uint16_t ptype[4] = { 0x0010, 0x0020, 0x0030, 0x0040 };
+               sdp_data_t *head, *pseq;
+               int p;
+
+               for (p = 0, head = NULL; p < 4; p++) {
+                       sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
+                       head = sdp_seq_append(head, data);
+               }
+               pseq = sdp_data_alloc(SDP_SEQ16, head);
+               proto[1] = sdp_list_append(proto[1], pseq);
+       }
+
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Network Access Point Service", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("NAP service registered\n");
+
+end:
+       sdp_data_free(version);
+       sdp_data_free(psm);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_gn(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint16_t lp = 0x000f, ver = 0x0100;
+       sdp_data_t *psm, *version;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&ftrn_uuid, GN_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &ftrn_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+       proto[1] = sdp_list_append(0, &bnep_uuid);
+       version = sdp_data_alloc(SDP_UINT16, &ver);
+       proto[1] = sdp_list_append(proto[1], version);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Group Network Service", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("GN service registered\n");
+
+end:
+       sdp_data_free(version);
+       sdp_data_free(psm);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_panu(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint16_t lp = 0x000f, ver = 0x0100;
+       sdp_data_t *psm, *version;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+       sdp_list_free(root, NULL);
+
+       sdp_uuid16_create(&ftrn_uuid, PANU_SVCLASS_ID);
+       svclass_id = sdp_list_append(NULL, &ftrn_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+       sdp_list_free(svclass_id, NULL);
+
+       sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(NULL, &profile[0]);
+       sdp_set_profile_descs(&record, pfseq);
+       sdp_list_free(pfseq, NULL);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(NULL, &l2cap_uuid);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
+       proto[1] = sdp_list_append(NULL, &bnep_uuid);
+       version = sdp_data_alloc(SDP_UINT16, &ver);
+       proto[1] = sdp_list_append(proto[1], version);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "PAN User", NULL, NULL);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("PANU service registered\n");
+
+end:
+       sdp_data_free(version);
+       sdp_data_free(psm);
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_hid_keyb(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, hidkb_uuid, l2cap_uuid, hidp_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[3];
+       sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
+       unsigned int i;
+       uint8_t dtd = SDP_UINT16;
+       uint8_t dtd2 = SDP_UINT8;
+       uint8_t dtd_data = SDP_TEXT_STR8;
+       void *dtds[2];
+       void *values[2];
+       void *dtds2[2];
+       void *values2[2];
+       int leng[2];
+       uint8_t hid_spec_type = 0x22;
+       uint16_t hid_attr_lang[] = { 0x409, 0x100 };
+       static const uint16_t ctrl = 0x11;
+       static const uint16_t intr = 0x13;
+       static const uint16_t hid_attr[] = { 0x100, 0x111, 0x40, 0x0d, 0x01, 0x01 };
+       static const uint16_t hid_attr2[] = { 0x0, 0x01, 0x100, 0x1f40, 0x01, 0x01 };
+       const uint8_t hid_spec[] = {
+               0x05, 0x01, // usage page
+               0x09, 0x06, // keyboard
+               0xa1, 0x01, // key codes
+               0x85, 0x01, // minimum
+               0x05, 0x07, // max
+               0x19, 0xe0, // logical min
+               0x29, 0xe7, // logical max
+               0x15, 0x00, // report size
+               0x25, 0x01, // report count
+               0x75, 0x01, // input data variable absolute
+               0x95, 0x08, // report count
+               0x81, 0x02, // report size
+               0x75, 0x08,
+               0x95, 0x01,
+               0x81, 0x01,
+               0x75, 0x01,
+               0x95, 0x05,
+               0x05, 0x08,
+               0x19, 0x01,
+               0x29, 0x05,
+               0x91, 0x02,
+               0x75, 0x03,
+               0x95, 0x01,
+               0x91, 0x01,
+               0x75, 0x08,
+               0x95, 0x06,
+               0x15, 0x00,
+               0x26, 0xff,
+               0x00, 0x05,
+               0x07, 0x19,
+               0x00, 0x2a,
+               0xff, 0x00,
+               0x81, 0x00,
+               0x75, 0x01,
+               0x95, 0x01,
+               0x15, 0x00,
+               0x25, 0x01,
+               0x05, 0x0c,
+               0x09, 0xb8,
+               0x81, 0x06,
+               0x09, 0xe2,
+               0x81, 0x06,
+               0x09, 0xe9,
+               0x81, 0x02,
+               0x09, 0xea,
+               0x81, 0x02,
+               0x75, 0x01,
+               0x95, 0x04,
+               0x81, 0x01,
+               0xc0         // end tag
+       };
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_add_lang_attr(&record);
+
+       sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &hidkb_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       /* protocols */
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[1] = sdp_list_append(0, &l2cap_uuid);
+       psm = sdp_data_alloc(SDP_UINT16, &ctrl);
+       proto[1] = sdp_list_append(proto[1], psm);
+       apseq = sdp_list_append(0, proto[1]);
+
+       sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+       proto[2] = sdp_list_append(0, &hidp_uuid);
+       apseq = sdp_list_append(apseq, proto[2]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       /* additional protocols */
+       proto[1] = sdp_list_append(0, &l2cap_uuid);
+       psm = sdp_data_alloc(SDP_UINT16, &intr);
+       proto[1] = sdp_list_append(proto[1], psm);
+       apseq = sdp_list_append(0, proto[1]);
+
+       sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+       proto[2] = sdp_list_append(0, &hidp_uuid);
+       apseq = sdp_list_append(apseq, proto[2]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_add_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "HID Keyboard", NULL, NULL);
+
+       for (i = 0; i < sizeof(hid_attr) / 2; i++)
+               sdp_attr_add_new(&record,
+                                       SDP_ATTR_HID_DEVICE_RELEASE_NUMBER + i,
+                                       SDP_UINT16, &hid_attr[i]);
+
+       dtds[0] = &dtd2;
+       values[0] = &hid_spec_type;
+       dtds[1] = &dtd_data;
+       values[1] = (uint8_t *) hid_spec;
+       leng[0] = 0;
+       leng[1] = sizeof(hid_spec);
+       hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
+       hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
+       sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
+
+       for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
+               dtds2[i] = &dtd;
+               values2[i] = &hid_attr_lang[i];
+       }
+
+       lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
+       lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
+       sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE, SDP_UINT16, &hid_attr2[0]);
+
+       for (i = 0; i < sizeof(hid_attr2) / 2 - 1; i++)
+               sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP + i,
+                                               SDP_UINT16, &hid_attr2[i + 1]);
+
+       if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("HID keyboard service registered\n");
+
+       return 0;
+}
+
+static int add_hid_wiimote(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, hid_uuid, l2cap_uuid, hidp_uuid;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[3];
+       sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
+       unsigned int i;
+       uint8_t dtd = SDP_UINT16;
+       uint8_t dtd2 = SDP_UINT8;
+       uint8_t dtd_data = SDP_TEXT_STR8;
+       void *dtds[2];
+       void *values[2];
+       void *dtds2[2];
+       void *values2[2];
+       int leng[2];
+       uint8_t hid_spec_type = 0x22;
+       uint16_t hid_attr_lang[] = { 0x409, 0x100 };
+       uint16_t ctrl = 0x11, intr = 0x13;
+       uint16_t hid_release = 0x0100, parser_version = 0x0111;
+       uint8_t subclass = 0x04, country = 0x33;
+       uint8_t virtual_cable = 0, reconnect = 1, sdp_disable = 0;
+       uint8_t battery = 1, remote_wakeup = 1;
+       uint16_t profile_version = 0x0100, superv_timeout = 0x0c80;
+       uint8_t norm_connect = 0, boot_device = 0;
+       const uint8_t hid_spec[] = {
+               0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10,
+               0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95,
+               0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x11, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
+               0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+               0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
+               0xc0, 0x00
+       };
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&hid_uuid, HID_SVCLASS_ID);
+       svclass_id = sdp_list_append(NULL, &hid_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(NULL, profile);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[1] = sdp_list_append(0, &l2cap_uuid);
+       psm = sdp_data_alloc(SDP_UINT16, &ctrl);
+       proto[1] = sdp_list_append(proto[1], psm);
+       apseq = sdp_list_append(0, proto[1]);
+
+       sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+       proto[2] = sdp_list_append(0, &hidp_uuid);
+       apseq = sdp_list_append(apseq, proto[2]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       proto[1] = sdp_list_append(0, &l2cap_uuid);
+       psm = sdp_data_alloc(SDP_UINT16, &intr);
+       proto[1] = sdp_list_append(proto[1], psm);
+       apseq = sdp_list_append(0, proto[1]);
+
+       sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
+       proto[2] = sdp_list_append(0, &hidp_uuid);
+       apseq = sdp_list_append(apseq, proto[2]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_add_access_protos(&record, aproto);
+
+       sdp_add_lang_attr(&record);
+
+       sdp_set_info_attr(&record, "Nintendo RVL-CNT-01",
+                                       "Nintendo", "Nintendo RVL-CNT-01");
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER,
+                                               SDP_UINT16, &hid_release);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_PARSER_VERSION,
+                                               SDP_UINT16, &parser_version);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_SUBCLASS,
+                                               SDP_UINT8, &subclass);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_COUNTRY_CODE,
+                                               SDP_UINT8, &country);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_VIRTUAL_CABLE,
+                                               SDP_BOOL, &virtual_cable);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_RECONNECT_INITIATE,
+                                               SDP_BOOL, &reconnect);
+
+       dtds[0] = &dtd2;
+       values[0] = &hid_spec_type;
+       dtds[1] = &dtd_data;
+       values[1] = (uint8_t *) hid_spec;
+       leng[0] = 0;
+       leng[1] = sizeof(hid_spec);
+       hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
+       hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
+       sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
+
+       for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
+               dtds2[i] = &dtd;
+               values2[i] = &hid_attr_lang[i];
+       }
+
+       lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
+       lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
+       sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE,
+                                               SDP_BOOL, &sdp_disable);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_BATTERY_POWER,
+                                               SDP_BOOL, &battery);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP,
+                                               SDP_BOOL, &remote_wakeup);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_PROFILE_VERSION,
+                                               SDP_UINT16, &profile_version);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_SUPERVISION_TIMEOUT,
+                                               SDP_UINT16, &superv_timeout);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_NORMALLY_CONNECTABLE,
+                                               SDP_BOOL, &norm_connect);
+
+       sdp_attr_add_new(&record, SDP_ATTR_HID_BOOT_DEVICE,
+                                               SDP_BOOL, &boot_device);
+
+       if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("Wii-Mote service registered\n");
+
+       return 0;
+}
+
+static int add_cip(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap, cmtp, cip;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint16_t psm = si->psm ? si->psm : 0x1001;
+       uint8_t netid = si->network ? si->network : 0x02; // 0x02 = ISDN, 0x03 = GSM
+       sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&cip, CIP_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &cip);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, CIP_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap);
+       apseq = sdp_list_append(0, proto[0]);
+       proto[0] = sdp_list_append(proto[0], sdp_data_alloc(SDP_UINT16, &psm));
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&cmtp, CMTP_UUID);
+       proto[1] = sdp_list_append(0, &cmtp);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+       sdp_set_info_attr(&record, "Common ISDN Access", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("CIP service registered\n");
+
+end:
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+       sdp_data_free(network);
+
+       return ret;
+}
+
+static int add_ctp(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap, tcsbin, ctp;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       uint8_t netid = si->network ? si->network : 0x02; // 0x01-0x07 cf. p120 profile document
+       sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&ctp, CORDLESS_TELEPHONY_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &ctp);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, CORDLESS_TELEPHONY_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&tcsbin, TCS_BIN_UUID);
+       proto[1] = sdp_list_append(0, &tcsbin);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+       sdp_set_info_attr(&record, "Cordless Telephony", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto end;
+       }
+
+       printf("CTP service registered\n");
+
+end:
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+       sdp_data_free(network);
+
+       return ret;
+}
+
+static int add_a2source(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap, avdtp, a2src;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       sdp_data_t *psm, *version;
+       uint16_t lp = 0x0019, ver = 0x0100;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &a2src);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&avdtp, AVDTP_UUID);
+       proto[1] = sdp_list_append(0, &avdtp);
+       version = sdp_data_alloc(SDP_UINT16, &ver);
+       proto[1] = sdp_list_append(proto[1], version);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Audio Source", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto done;
+       }
+
+       printf("Audio source service registered\n");
+
+done:
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_a2sink(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap, avdtp, a2snk;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       sdp_data_t *psm, *version;
+       uint16_t lp = 0x0019, ver = 0x0100;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&a2snk, AUDIO_SINK_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &a2snk);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&avdtp, AVDTP_UUID);
+       proto[1] = sdp_list_append(0, &avdtp);
+       version = sdp_data_alloc(SDP_UINT16, &ver);
+       proto[1] = sdp_list_append(proto[1], version);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Audio Sink", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto done;
+       }
+
+       printf("Audio sink service registered\n");
+
+done:
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_avrct(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap, avctp, avrct;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       sdp_data_t *psm, *version, *features;
+       uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &avrct);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&avctp, AVCTP_UUID);
+       proto[1] = sdp_list_append(0, &avctp);
+       version = sdp_data_alloc(SDP_UINT16, &ver);
+       proto[1] = sdp_list_append(proto[1], version);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       features = sdp_data_alloc(SDP_UINT16, &feat);
+       sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       sdp_set_info_attr(&record, "AVRCP CT", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto done;
+       }
+
+       printf("Remote control service registered\n");
+
+done:
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_avrtg(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap, avctp, avrtg;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t record;
+       sdp_data_t *psm, *version, *features;
+       uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
+       int ret = 0;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(0, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+       svclass_id = sdp_list_append(0, &avrtg);
+       sdp_set_service_classes(&record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(0, &profile[0]);
+       sdp_set_profile_descs(&record, pfseq);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(0, proto[0]);
+
+       sdp_uuid16_create(&avctp, AVCTP_UUID);
+       proto[1] = sdp_list_append(0, &avctp);
+       version = sdp_data_alloc(SDP_UINT16, &ver);
+       proto[1] = sdp_list_append(proto[1], version);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(0, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       features = sdp_data_alloc(SDP_UINT16, &feat);
+       sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       sdp_set_info_attr(&record, "AVRCP TG", 0, 0);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               ret = -1;
+               goto done;
+       }
+
+       printf("Remote target service registered\n");
+
+done:
+       sdp_list_free(proto[0], 0);
+       sdp_list_free(proto[1], 0);
+       sdp_list_free(apseq, 0);
+       sdp_list_free(aproto, 0);
+
+       return ret;
+}
+
+static int add_udi_ue(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root, *svclass, *proto;
+       uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       uint8_t channel = si->channel ? si->channel: 18;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+       sdp_list_free(root, NULL);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto = sdp_list_append(proto, sdp_list_append(
+               sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+       sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+       sdp_uuid16_create(&svclass_uuid, UDI_MT_SVCLASS_ID);
+       svclass = sdp_list_append(NULL, &svclass_uuid);
+       sdp_set_service_classes(&record, svclass);
+       sdp_list_free(svclass, NULL);
+
+       sdp_set_info_attr(&record, "UDI UE", NULL, NULL);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("UDI UE service registered\n");
+
+       return 0;
+}
+
+static int add_udi_te(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root, *svclass, *proto;
+       uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       uint8_t channel = si->channel ? si->channel: 19;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+       sdp_list_free(root, NULL);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto = sdp_list_append(proto, sdp_list_append(
+               sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+       sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+       sdp_uuid16_create(&svclass_uuid, UDI_TA_SVCLASS_ID);
+       svclass = sdp_list_append(NULL, &svclass_uuid);
+       sdp_set_service_classes(&record, svclass);
+       sdp_list_free(svclass, NULL);
+
+       sdp_set_info_attr(&record, "UDI TE", NULL, NULL);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("UDI TE service registered\n");
+
+       return 0;
+}
+
+static unsigned char sr1_uuid[] = {    0xbc, 0x19, 0x9c, 0x24, 0x95, 0x8b, 0x4c, 0xc0,
+                                       0xa2, 0xcb, 0xfd, 0x8a, 0x30, 0xbf, 0x32, 0x06 };
+
+static int add_sr1(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root, *svclass;
+       uuid_t root_uuid, svclass_uuid;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid128_create(&svclass_uuid, (void *) sr1_uuid);
+       svclass = sdp_list_append(NULL, &svclass_uuid);
+       sdp_set_service_classes(&record, svclass);
+
+       sdp_set_info_attr(&record, "TOSHIBA SR-1", NULL, NULL);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("Toshiba Speech Recognition SR-1 service record registered\n");
+
+       return 0;
+}
+
+static unsigned char syncmls_uuid[] = {        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00,
+                                       0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
+
+static unsigned char syncmlc_uuid[] = {        0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00,
+                                       0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
+
+static int add_syncml(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root, *svclass, *proto;
+       uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
+       uint8_t channel = si->channel ? si->channel: 15;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid128_create(&svclass_uuid, (void *) syncmlc_uuid);
+       svclass = sdp_list_append(NULL, &svclass_uuid);
+       sdp_set_service_classes(&record, svclass);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto = sdp_list_append(proto, sdp_list_append(
+               sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+       sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+       proto = sdp_list_append(proto, sdp_list_append(NULL, &obex_uuid));
+
+       sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+       sdp_set_info_attr(&record, "SyncML Client", NULL, NULL);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("SyncML Client service record registered\n");
+
+       return 0;
+}
+
+static unsigned char async_uuid[] = {  0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62,
+                                       0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C };
+
+static int add_activesync(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root, *svclass, *proto;
+       uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       uint8_t channel = si->channel ? si->channel: 21;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto = sdp_list_append(proto, sdp_list_append(
+       sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+       sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+       sdp_uuid128_create(&svclass_uuid, (void *) async_uuid);
+       svclass = sdp_list_append(NULL, &svclass_uuid);
+       sdp_set_service_classes(&record, svclass);
+
+       sdp_set_info_attr(&record, "Microsoft ActiveSync", NULL, NULL);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("ActiveSync service record registered\n");
+
+       return 0;
+}
+
+static unsigned char hotsync_uuid[] = {        0xD8, 0x0C, 0xF9, 0xEA, 0x13, 0x4C, 0x11, 0xD5,
+                                       0x83, 0xCE, 0x00, 0x30, 0x65, 0x7C, 0x54, 0x3C };
+
+static int add_hotsync(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root, *svclass, *proto;
+       uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       uint8_t channel = si->channel ? si->channel: 22;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto = sdp_list_append(proto, sdp_list_append(
+       sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+       sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+       sdp_uuid128_create(&svclass_uuid, (void *) hotsync_uuid);
+       svclass = sdp_list_append(NULL, &svclass_uuid);
+       sdp_set_service_classes(&record, svclass);
+
+       sdp_set_info_attr(&record, "PalmOS HotSync", NULL, NULL);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("HotSync service record registered\n");
+
+       return 0;
+}
+
+static unsigned char palmos_uuid[] = { 0xF5, 0xBE, 0xB6, 0x51, 0x41, 0x71, 0x40, 0x51,
+                                       0xAC, 0xF5, 0x6C, 0xA7, 0x20, 0x22, 0x42, 0xF0 };
+
+static int add_palmos(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root, *svclass;
+       uuid_t root_uuid, svclass_uuid;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid128_create(&svclass_uuid, (void *) palmos_uuid);
+       svclass = sdp_list_append(NULL, &svclass_uuid);
+       sdp_set_service_classes(&record, svclass);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("PalmOS service record registered\n");
+
+       return 0;
+}
+
+static unsigned char nokid_uuid[] = {  0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x10, 0x00,
+                                       0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static int add_nokiaid(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root, *svclass;
+       uuid_t root_uuid, svclass_uuid;
+       uint16_t verid = 0x005f;
+       sdp_data_t *version = sdp_data_alloc(SDP_UINT16, &verid);
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid128_create(&svclass_uuid, (void *) nokid_uuid);
+       svclass = sdp_list_append(NULL, &svclass_uuid);
+       sdp_set_service_classes(&record, svclass);
+
+       sdp_attr_add(&record, SDP_ATTR_SERVICE_VERSION, version);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               sdp_data_free(version);
+               return -1;
+       }
+
+       printf("Nokia ID service record registered\n");
+
+       return 0;
+}
+
+static unsigned char pcsuite_uuid[] = {        0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x10, 0x00,
+                                       0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static int add_pcsuite(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root, *svclass, *proto;
+       uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
+       uint8_t channel = si->channel ? si->channel: 14;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto = sdp_list_append(proto, sdp_list_append(
+               sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+       sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+       sdp_uuid128_create(&svclass_uuid, (void *) pcsuite_uuid);
+       svclass = sdp_list_append(NULL, &svclass_uuid);
+       sdp_set_service_classes(&record, svclass);
+
+       sdp_set_info_attr(&record, "Nokia PC Suite", NULL, NULL);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("Nokia PC Suite service registered\n");
+
+       return 0;
+}
+
+static unsigned char nftp_uuid[] = {   0x00, 0x00, 0x50, 0x05, 0x00, 0x00, 0x10, 0x00,
+                                       0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char nsyncml_uuid[] = {        0x00, 0x00, 0x56, 0x01, 0x00, 0x00, 0x10, 0x00,
+                                       0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char ngage_uuid[] = {  0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x10, 0x00,
+                                       0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
+
+static unsigned char apple_uuid[] = {  0xf0, 0x72, 0x2e, 0x20, 0x0f, 0x8b, 0x4e, 0x90,
+                                       0x8c, 0xc2, 0x1b, 0x46, 0xf5, 0xf2, 0xef, 0xe2 };
+
+static int add_apple(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root;
+       uuid_t root_uuid;
+       uint32_t attr783 = 0x00000000;
+       uint32_t attr785 = 0x00000002;
+       uint16_t attr786 = 0x1234;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_attr_add_new(&record, 0x0780, SDP_UUID128, (void *) apple_uuid);
+       sdp_attr_add_new(&record, 0x0781, SDP_TEXT_STR8, (void *) "Macmini");
+       sdp_attr_add_new(&record, 0x0782, SDP_TEXT_STR8, (void *) "PowerMac10,1");
+       sdp_attr_add_new(&record, 0x0783, SDP_UINT32, (void *) &attr783);
+       sdp_attr_add_new(&record, 0x0784, SDP_TEXT_STR8, (void *) "1.6.6f22");
+       sdp_attr_add_new(&record, 0x0785, SDP_UINT32, (void *) &attr785);
+       sdp_attr_add_new(&record, 0x0786, SDP_UUID16, (void *) &attr786);
+
+       sdp_set_info_attr(&record, "Apple Macintosh Attributes", NULL, NULL);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("Apple attribute service registered\n");
+
+       return 0;
+}
+
+static int add_isync(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_list_t *root, *svclass, *proto;
+       uuid_t root_uuid, svclass_uuid, serial_uuid, l2cap_uuid, rfcomm_uuid;
+       uint8_t channel = si->channel ? si->channel : 16;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto = sdp_list_append(proto, sdp_list_append(
+               sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
+
+       sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+       sdp_uuid16_create(&serial_uuid, SERIAL_PORT_SVCLASS_ID);
+       svclass = sdp_list_append(NULL, &serial_uuid);
+
+       sdp_uuid16_create(&svclass_uuid, APPLE_AGENT_SVCLASS_ID);
+       svclass = sdp_list_append(svclass, &svclass_uuid);
+
+       sdp_set_service_classes(&record, svclass);
+
+       sdp_set_info_attr(&record, "AppleAgent", "Bluetooth acceptor", "Apple Computer Ltd.");
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       printf("Apple iSync service registered\n");
+
+       return 0;
+}
+
+static int add_semchla(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_record_t record;
+       sdp_profile_desc_t profile;
+       sdp_list_t *root, *svclass, *proto, *profiles;
+       uuid_t root_uuid, service_uuid, l2cap_uuid, semchla_uuid;
+       uint16_t psm = 0xf0f9;
+
+       memset(&record, 0, sizeof(record));
+       record.handle = si->handle;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto = sdp_list_append(NULL, sdp_list_append(
+               sdp_list_append(NULL, &l2cap_uuid), sdp_data_alloc(SDP_UINT16, &psm)));
+
+       sdp_uuid32_create(&semchla_uuid, 0x8e770300);
+       proto = sdp_list_append(proto, sdp_list_append(NULL, &semchla_uuid));
+
+       sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
+
+       sdp_uuid32_create(&service_uuid, 0x8e771301);
+       svclass = sdp_list_append(NULL, &service_uuid);
+
+       sdp_set_service_classes(&record, svclass);
+
+       sdp_uuid32_create(&profile.uuid, 0x8e771302);   // Headset
+       //sdp_uuid32_create(&profile.uuid, 0x8e771303); // Phone
+       profile.version = 0x0100;
+       profiles = sdp_list_append(NULL, &profile);
+       sdp_set_profile_descs(&record, profiles);
+
+       sdp_set_info_attr(&record, "SEMC HLA", NULL, NULL);
+
+       if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
+               printf("Service Record registration failed\n");
+               return -1;
+       }
+
+       /* SEMC High Level Authentication */
+       printf("SEMC HLA service registered\n");
+
+       return 0;
+}
+
+static int add_gatt(sdp_session_t *session, svc_info_t *si)
+{
+       sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
+       uuid_t root_uuid, proto_uuid, gatt_uuid, l2cap;
+       sdp_profile_desc_t profile;
+       sdp_record_t record;
+       sdp_data_t *psm, *sh, *eh;
+       uint16_t att_psm = 27, start = 0x0001, end = 0x000f;
+       int ret;
+
+       memset(&record, 0, sizeof(sdp_record_t));
+       record.handle = si->handle;
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(&record, root);
+       sdp_list_free(root, NULL);
+
+       sdp_uuid16_create(&gatt_uuid, GENERIC_ATTRIB_SVCLASS_ID);
+       svclass_id = sdp_list_append(NULL, &gatt_uuid);
+       sdp_set_service_classes(&record, svclass_id);
+       sdp_list_free(svclass_id, NULL);
+
+       sdp_uuid16_create(&profile.uuid, GENERIC_ATTRIB_PROFILE_ID);
+       profile.version = 0x0100;
+       profiles = sdp_list_append(NULL, &profile);
+       sdp_set_profile_descs(&record, profiles);
+       sdp_list_free(profiles, NULL);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(NULL, &l2cap);
+       psm = sdp_data_alloc(SDP_UINT16, &att_psm);
+       proto[0] = sdp_list_append(proto[0], psm);
+       apseq = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&proto_uuid, ATT_UUID);
+       proto[1] = sdp_list_append(NULL, &proto_uuid);
+       sh = sdp_data_alloc(SDP_UINT16, &start);
+       proto[1] = sdp_list_append(proto[1], sh);
+       eh = sdp_data_alloc(SDP_UINT16, &end);
+       proto[1] = sdp_list_append(proto[1], eh);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(&record, aproto);
+
+       sdp_set_info_attr(&record, "Generic Attribute Profile", "BlueZ", NULL);
+
+       sdp_set_url_attr(&record, "http://www.bluez.org/",
+                       "http://www.bluez.org/", "http://www.bluez.org/");
+
+       sdp_set_service_id(&record, gatt_uuid);
+
+       ret = sdp_device_record_register(session, &interface, &record,
+                                                       SDP_RECORD_PERSIST);
+       if (ret < 0)
+               printf("Service Record registration failed\n");
+       else
+               printf("Generic Attribute Profile Service registered\n");
+
+       sdp_data_free(psm);
+       sdp_data_free(sh);
+       sdp_data_free(eh);
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(aproto, NULL);
+
+       return ret;
+}
+
+struct {
+       char            *name;
+       uint32_t        class;
+       int             (*add)(sdp_session_t *sess, svc_info_t *si);
+       unsigned char *uuid;
+} service[] = {
+       { "DID",        PNP_INFO_SVCLASS_ID,            NULL,           },
+
+       { "SP",         SERIAL_PORT_SVCLASS_ID,         add_sp          },
+       { "DUN",        DIALUP_NET_SVCLASS_ID,          add_dun         },
+       { "LAN",        LAN_ACCESS_SVCLASS_ID,          add_lan         },
+       { "FAX",        FAX_SVCLASS_ID,                 add_fax         },
+       { "OPUSH",      OBEX_OBJPUSH_SVCLASS_ID,        add_opush       },
+       { "FTP",        OBEX_FILETRANS_SVCLASS_ID,      add_ftp         },
+       { "PRINT",      DIRECT_PRINTING_SVCLASS_ID,     add_directprint },
+
+       { "HS",         HEADSET_SVCLASS_ID,             add_headset     },
+       { "HSAG",       HEADSET_AGW_SVCLASS_ID,         add_headset_ag  },
+       { "HF",         HANDSFREE_SVCLASS_ID,           add_handsfree   },
+       { "HFAG",       HANDSFREE_AGW_SVCLASS_ID,       add_handsfree_ag},
+       { "SAP",        SAP_SVCLASS_ID,                 add_simaccess   },
+       { "PBAP",       PBAP_SVCLASS_ID,                add_pbap,       },
+
+       { "NAP",        NAP_SVCLASS_ID,                 add_nap         },
+       { "GN",         GN_SVCLASS_ID,                  add_gn          },
+       { "PANU",       PANU_SVCLASS_ID,                add_panu        },
+
+       { "HCRP",       HCR_SVCLASS_ID,                 NULL            },
+       { "HID",        HID_SVCLASS_ID,                 NULL            },
+       { "KEYB",       HID_SVCLASS_ID,                 add_hid_keyb    },
+       { "WIIMOTE",    HID_SVCLASS_ID,                 add_hid_wiimote },
+       { "CIP",        CIP_SVCLASS_ID,                 add_cip         },
+       { "CTP",        CORDLESS_TELEPHONY_SVCLASS_ID,  add_ctp         },
+
+       { "A2SRC",      AUDIO_SOURCE_SVCLASS_ID,        add_a2source    },
+       { "A2SNK",      AUDIO_SINK_SVCLASS_ID,          add_a2sink      },
+       { "AVRCT",      AV_REMOTE_SVCLASS_ID,           add_avrct       },
+       { "AVRTG",      AV_REMOTE_TARGET_SVCLASS_ID,    add_avrtg       },
+
+       { "UDIUE",      UDI_MT_SVCLASS_ID,              add_udi_ue      },
+       { "UDITE",      UDI_TA_SVCLASS_ID,              add_udi_te      },
+
+       { "SEMCHLA",    0x8e771301,                     add_semchla     },
+
+       { "SR1",        0,                              add_sr1,        sr1_uuid        },
+       { "SYNCML",     0,                              add_syncml,     syncmlc_uuid    },
+       { "SYNCMLSERV", 0,                              NULL,           syncmls_uuid    },
+       { "ACTIVESYNC", 0,                              add_activesync, async_uuid      },
+       { "HOTSYNC",    0,                              add_hotsync,    hotsync_uuid    },
+       { "PALMOS",     0,                              add_palmos,     palmos_uuid     },
+       { "NOKID",      0,                              add_nokiaid,    nokid_uuid      },
+       { "PCSUITE",    0,                              add_pcsuite,    pcsuite_uuid    },
+       { "NFTP",       0,                              NULL,           nftp_uuid       },
+       { "NSYNCML",    0,                              NULL,           nsyncml_uuid    },
+       { "NGAGE",      0,                              NULL,           ngage_uuid      },
+       { "APPLE",      0,                              add_apple,      apple_uuid      },
+
+       { "ISYNC",      APPLE_AGENT_SVCLASS_ID,         add_isync,      },
+       { "GATT",       GENERIC_ATTRIB_SVCLASS_ID,      add_gatt,       },
+
+       { 0 }
+};
+
+/* Add local service */
+static int add_service(bdaddr_t *bdaddr, svc_info_t *si)
+{
+       sdp_session_t *sess;
+       int i, ret = -1;
+
+       if (!si->name)
+               return -1;
+
+       sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+       if (!sess)
+               return -1;
+
+       for (i = 0; service[i].name; i++)
+               if (!strcasecmp(service[i].name, si->name)) {
+                       if (service[i].add)
+                               ret = service[i].add(sess, si);
+                       goto done;
+               }
+
+       printf("Unknown service name: %s\n", si->name);
+
+done:
+       free(si->name);
+       sdp_close(sess);
+
+       return ret;
+}
+
+static struct option add_options[] = {
+       { "help",       0, 0, 'h' },
+       { "handle",     1, 0, 'r' },
+       { "psm",        1, 0, 'p' },
+       { "channel",    1, 0, 'c' },
+       { "network",    1, 0, 'n' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *add_help =
+       "Usage:\n"
+       "\tadd [--handle=RECORD_HANDLE --channel=CHANNEL] service\n";
+
+static int cmd_add(int argc, char **argv)
+{
+       svc_info_t si;
+       int opt;
+
+       memset(&si, 0, sizeof(si));
+       si.handle = 0xffffffff;
+
+       for_each_opt(opt, add_options, 0) {
+               switch (opt) {
+               case 'r':
+                       if (strncasecmp(optarg, "0x", 2))
+                               si.handle = atoi(optarg);
+                       else
+                               si.handle = strtol(optarg + 2, NULL, 16);
+                       break;
+               case 'p':
+                       if (strncasecmp(optarg, "0x", 2))
+                               si.psm = atoi(optarg);
+                       else
+                               si.psm = strtol(optarg + 2, NULL, 16);
+                       break;
+               case 'c':
+                       if (strncasecmp(optarg, "0x", 2))
+                               si.channel = atoi(optarg);
+                       else
+                               si.channel = strtol(optarg + 2, NULL, 16);
+                       break;
+               case 'n':
+                       if (strncasecmp(optarg, "0x", 2))
+                               si.network = atoi(optarg);
+                       else
+                               si.network = strtol(optarg + 2, NULL, 16);
+                       break;
+               default:
+                       printf("%s", add_help);
+                       return -1;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               printf("%s", add_help);
+               return -1;
+       }
+
+       si.name = strdup(argv[0]);
+
+       return add_service(0, &si);
+}
+
+/* Delete local service */
+static int del_service(bdaddr_t *bdaddr, void *arg)
+{
+       uint32_t handle, range = 0x0000ffff;
+       sdp_list_t *attr;
+       sdp_session_t *sess;
+       sdp_record_t *rec;
+
+       if (!arg) {
+               printf("Record handle was not specified.\n");
+               return -1;
+       }
+
+       sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
+       if (!sess) {
+               printf("No local SDP server!\n");
+               return -1;
+       }
+
+       handle = strtoul((char *)arg, 0, 16);
+       attr = sdp_list_append(0, &range);
+       rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attr);
+       sdp_list_free(attr, 0);
+
+       if (!rec) {
+               printf("Service Record not found.\n");
+               sdp_close(sess);
+               return -1;
+       }
+
+       if (sdp_device_record_unregister(sess, &interface, rec)) {
+               printf("Failed to unregister service record: %s\n", strerror(errno));
+               sdp_close(sess);
+               return -1;
+       }
+
+       printf("Service Record deleted.\n");
+       sdp_close(sess);
+
+       return 0;
+}
+
+static struct option del_options[] = {
+       { "help",       0, 0, 'h' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *del_help =
+       "Usage:\n"
+       "\tdel record_handle\n";
+
+static int cmd_del(int argc, char **argv)
+{
+       int opt;
+
+       for_each_opt(opt, del_options, 0) {
+               switch (opt) {
+               default:
+                       printf("%s", del_help);
+                       return -1;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               printf("%s", del_help);
+               return -1;
+       }
+
+       return del_service(NULL, argv[0]);
+}
+
+/*
+ * Perform an inquiry and search/browse all peer found.
+ */
+static void inquiry(handler_t handler, void *arg)
+{
+       inquiry_info ii[20];
+       uint8_t count = 0;
+       int i;
+
+       printf("Inquiring ...\n");
+       if (sdp_general_inquiry(ii, 20, 8, &count) < 0) {
+               printf("Inquiry failed\n");
+               return;
+       }
+
+       for (i = 0; i < count; i++)
+               handler(&ii[i].bdaddr, arg);
+}
+
+static void doprintf(void *data, const char *str)
+{
+       printf("%s", str);
+}
+
+/*
+ * Search for a specific SDP service
+ */
+static int do_search(bdaddr_t *bdaddr, struct search_context *context)
+{
+       sdp_list_t *attrid, *search, *seq, *next;
+       uint32_t range = 0x0000ffff;
+       char str[20];
+       sdp_session_t *sess;
+
+       if (!bdaddr) {
+               inquiry(do_search, context);
+               return 0;
+       }
+
+       sess = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
+       ba2str(bdaddr, str);
+       if (!sess) {
+               printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
+               return -1;
+       }
+
+       if (context->view != RAW_VIEW) {
+               if (context->svc)
+                       printf("Searching for %s on %s ...\n", context->svc, str);
+               else
+                       printf("Browsing %s ...\n", str);
+       }
+
+       attrid = sdp_list_append(0, &range);
+       search = sdp_list_append(0, &context->group);
+       if (sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq)) {
+               printf("Service Search failed: %s\n", strerror(errno));
+               sdp_close(sess);
+               return -1;
+       }
+       sdp_list_free(attrid, 0);
+       sdp_list_free(search, 0);
+
+       for (; seq; seq = next) {
+               sdp_record_t *rec = (sdp_record_t *) seq->data;
+               struct search_context sub_context;
+
+               switch (context->view) {
+               case DEFAULT_VIEW:
+                       /* Display user friendly form */
+                       print_service_attr(rec);
+                       printf("\n");
+                       break;
+               case TREE_VIEW:
+                       /* Display full tree */
+                       print_tree_attr(rec);
+                       printf("\n");
+                       break;
+               case XML_VIEW:
+                       /* Display raw XML tree */
+                       convert_sdp_record_to_xml(rec, 0, doprintf);
+                       break;
+               default:
+                       /* Display raw tree */
+                       print_raw_attr(rec);
+                       break;
+               }
+
+               if (sdp_get_group_id(rec, &sub_context.group) != -1) {
+                       /* Set the subcontext for browsing the sub tree */
+                       memcpy(&sub_context, context, sizeof(struct search_context));
+                       /* Browse the next level down if not done */
+                       if (sub_context.group.value.uuid16 != context->group.value.uuid16)
+                               do_search(bdaddr, &sub_context);
+               }
+               next = seq->next;
+               free(seq);
+               sdp_record_free(rec);
+       }
+
+       sdp_close(sess);
+       return 0;
+}
+
+static struct option browse_options[] = {
+       { "help",       0, 0, 'h' },
+       { "tree",       0, 0, 't' },
+       { "raw",        0, 0, 'r' },
+       { "xml",        0, 0, 'x' },
+       { "uuid",       1, 0, 'u' },
+       { "l2cap",      0, 0, 'l' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *browse_help =
+       "Usage:\n"
+       "\tbrowse [--tree] [--raw] [--xml] [--uuid uuid] [--l2cap] [bdaddr]\n";
+
+/*
+ * Browse the full SDP database (i.e. list all services starting from the
+ * root/top-level).
+ */
+static int cmd_browse(int argc, char **argv)
+{
+       struct search_context context;
+       int opt, num;
+
+       /* Initialise context */
+       memset(&context, '\0', sizeof(struct search_context));
+       /* We want to browse the top-level/root */
+       sdp_uuid16_create(&context.group, PUBLIC_BROWSE_GROUP);
+
+       for_each_opt(opt, browse_options, 0) {
+               switch (opt) {
+               case 't':
+                       context.view = TREE_VIEW;
+                       break;
+               case 'r':
+                       context.view = RAW_VIEW;
+                       break;
+               case 'x':
+                       context.view = XML_VIEW;
+                       break;
+               case 'u':
+                       if (sscanf(optarg, "%i", &num) != 1 || num < 0 || num > 0xffff) {
+                               printf("Invalid uuid %s\n", optarg);
+                               return -1;
+                       }
+                       sdp_uuid16_create(&context.group, num);
+                       break;
+               case 'l':
+                       sdp_uuid16_create(&context.group, L2CAP_UUID);
+                       break;
+               default:
+                       printf("%s", browse_help);
+                       return -1;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc >= 1) {
+               bdaddr_t bdaddr;
+               estr2ba(argv[0], &bdaddr);
+               return do_search(&bdaddr, &context);
+       }
+
+       return do_search(NULL, &context);
+}
+
+static struct option search_options[] = {
+       { "help",       0, 0, 'h' },
+       { "bdaddr",     1, 0, 'b' },
+       { "tree",       0, 0, 't' },
+       { "raw",        0, 0, 'r' },
+       { "xml",        0, 0, 'x' },
+       { 0, 0, 0, 0}
+};
+
+static const char *search_help =
+       "Usage:\n"
+       "\tsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] SERVICE\n"
+       "SERVICE is a name (string) or UUID (0x1002)\n";
+
+/*
+ * Search for a specific SDP service
+ *
+ * Note : we should support multiple services on the command line :
+ *          sdptool search 0x0100 0x000f 0x1002
+ * (this would search a service supporting both L2CAP and BNEP directly in
+ * the top level browse group)
+ */
+static int cmd_search(int argc, char **argv)
+{
+       struct search_context context;
+       unsigned char *uuid = NULL;
+       uint32_t class = 0;
+       bdaddr_t bdaddr;
+       int has_addr = 0;
+       int i;
+       int opt;
+
+       /* Initialise context */
+       memset(&context, '\0', sizeof(struct search_context));
+
+       for_each_opt(opt, search_options, 0) {
+               switch (opt) {
+               case 'b':
+                       estr2ba(optarg, &bdaddr);
+                       has_addr = 1;
+                       break;
+               case 't':
+                       context.view = TREE_VIEW;
+                       break;
+               case 'r':
+                       context.view = RAW_VIEW;
+                       break;
+               case 'x':
+                       context.view = XML_VIEW;
+                       break;
+               default:
+                       printf("%s", search_help);
+                       return -1;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               printf("%s", search_help);
+               return -1;
+       }
+
+       /* Note : we need to find a way to support search combining
+        * multiple services */
+       context.svc = strdup(argv[0]);
+       if (!strncasecmp(context.svc, "0x", 2)) {
+               int num;
+               /* This is a UUID16, just convert to int */
+               sscanf(context.svc + 2, "%X", &num);
+               class = num;
+               printf("Class 0x%X\n", class);
+       } else {
+               /* Convert class name to an UUID */
+
+               for (i = 0; service[i].name; i++)
+                       if (strcasecmp(context.svc, service[i].name) == 0) {
+                               class = service[i].class;
+                               uuid = service[i].uuid;
+                               break;
+                       }
+               if (!class && !uuid) {
+                       printf("Unknown service %s\n", context.svc);
+                       return -1;
+               }
+       }
+
+       if (class) {
+               if (class & 0xffff0000)
+                       sdp_uuid32_create(&context.group, class);
+               else {
+                       uint16_t class16 = class & 0xffff;
+                       sdp_uuid16_create(&context.group, class16);
+               }
+       } else
+               sdp_uuid128_create(&context.group, uuid);
+
+       if (has_addr)
+               return do_search(&bdaddr, &context);
+
+       return do_search(NULL, &context);
+}
+
+/*
+ * Show how to get a specific SDP record by its handle.
+ * Not really useful to the user, just show how it can be done...
+ */
+static int get_service(bdaddr_t *bdaddr, struct search_context *context, int quite)
+{
+       sdp_list_t *attrid;
+       uint32_t range = 0x0000ffff;
+       sdp_record_t *rec;
+       sdp_session_t *session = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
+
+       if (!session) {
+               char str[20];
+               ba2str(bdaddr, str);
+               printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
+               return -1;
+       }
+
+       attrid = sdp_list_append(0, &range);
+       rec = sdp_service_attr_req(session, context->handle, SDP_ATTR_REQ_RANGE, attrid);
+       sdp_list_free(attrid, 0);
+       sdp_close(session);
+
+       if (!rec) {
+               if (!quite) {
+                       printf("Service get request failed.\n");
+                       return -1;
+               } else
+                       return 0;
+       }
+
+       switch (context->view) {
+       case DEFAULT_VIEW:
+               /* Display user friendly form */
+               print_service_attr(rec);
+               printf("\n");
+               break;
+       case TREE_VIEW:
+               /* Display full tree */
+               print_tree_attr(rec);
+               printf("\n");
+               break;
+       case XML_VIEW:
+               /* Display raw XML tree */
+               convert_sdp_record_to_xml(rec, 0, doprintf);
+               break;
+       default:
+               /* Display raw tree */
+               print_raw_attr(rec);
+               break;
+       }
+
+       sdp_record_free(rec);
+       return 0;
+}
+
+static struct option records_options[] = {
+       { "help",       0, 0, 'h' },
+       { "tree",       0, 0, 't' },
+       { "raw",        0, 0, 'r' },
+       { "xml",        0, 0, 'x' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *records_help =
+       "Usage:\n"
+       "\trecords [--tree] [--raw] [--xml] bdaddr\n";
+
+/*
+ * Request possible SDP service records
+ */
+static int cmd_records(int argc, char **argv)
+{
+       struct search_context context;
+       uint32_t base[] = { 0x10000, 0x10300, 0x10500,
+                               0x1002e, 0x110b, 0x90000, 0x2008000,
+                                       0x4000000, 0x100000, 0x1000000,
+                                               0x4f491100, 0x4f491200 };
+       bdaddr_t bdaddr;
+       unsigned int i, n, num = 32;
+       int opt, err = 0;
+
+       /* Initialise context */
+       memset(&context, '\0', sizeof(struct search_context));
+
+       for_each_opt(opt, records_options, 0) {
+               switch (opt) {
+               case 't':
+                       context.view = TREE_VIEW;
+                       break;
+               case 'r':
+                       context.view = RAW_VIEW;
+                       break;
+               case 'x':
+                       context.view = XML_VIEW;
+                       break;
+               default:
+                       printf("%s", records_help);
+                       return -1;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               printf("%s", records_help);
+               return -1;
+       }
+
+       /* Convert command line parameters */
+       estr2ba(argv[0], &bdaddr);
+
+       for (i = 0; i < sizeof(base) / sizeof(uint32_t); i++)
+               for (n = 0; n < num; n++) {
+                       context.handle = base[i] + n;
+                       err = get_service(&bdaddr, &context, 1);
+                       if (err < 0)
+                               return 0;
+               }
+
+       return 0;
+}
+
+static struct option get_options[] = {
+       { "help",       0, 0, 'h' },
+       { "bdaddr",     1, 0, 'b' },
+       { "tree",       0, 0, 't' },
+       { "raw",        0, 0, 'r' },
+       { "xml",        0, 0, 'x' },
+       { 0, 0, 0, 0 }
+};
+
+static const char *get_help =
+       "Usage:\n"
+       "\tget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\n";
+
+/*
+ * Get a specific SDP record on the local SDP server
+ */
+static int cmd_get(int argc, char **argv)
+{
+       struct search_context context;
+       bdaddr_t bdaddr;
+       int has_addr = 0;
+       int opt;
+
+       /* Initialise context */
+       memset(&context, '\0', sizeof(struct search_context));
+
+       for_each_opt(opt, get_options, 0) {
+               switch (opt) {
+               case 'b':
+                       estr2ba(optarg, &bdaddr);
+                       has_addr = 1;
+                       break;
+               case 't':
+                       context.view = TREE_VIEW;
+                       break;
+               case 'r':
+                       context.view = RAW_VIEW;
+                       break;
+               case 'x':
+                       context.view = XML_VIEW;
+                       break;
+               default:
+                       printf("%s", get_help);
+                       return -1;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               printf("%s", get_help);
+               return -1;
+       }
+
+       /* Convert command line parameters */
+       context.handle = strtoul(argv[0], 0, 16);
+
+       return get_service(has_addr ? &bdaddr : BDADDR_LOCAL, &context, 0);
+}
+
+static struct {
+       char *cmd;
+       int (*func)(int argc, char **argv);
+       char *doc;
+} command[] = {
+       { "search",  cmd_search,      "Search for a service"          },
+       { "browse",  cmd_browse,      "Browse all available services" },
+       { "records", cmd_records,     "Request all records"           },
+       { "add",     cmd_add,         "Add local service"             },
+       { "del",     cmd_del,         "Delete local service"          },
+       { "get",     cmd_get,         "Get local service"             },
+       { "setattr", cmd_setattr,     "Set/Add attribute to a SDP record"          },
+       { "setseq",  cmd_setseq,      "Set/Add attribute sequence to a SDP record" },
+       { 0, 0, 0 }
+};
+
+static void usage(void)
+{
+       int i, pos = 0;
+
+       printf("sdptool - SDP tool v%s\n", VERSION);
+       printf("Usage:\n"
+               "\tsdptool [options] <command> [command parameters]\n");
+       printf("Options:\n"
+               "\t-h\t\tDisplay help\n"
+               "\t-i\t\tSpecify source interface\n");
+
+       printf("Commands:\n");
+       for (i = 0; command[i].cmd; i++)
+               printf("\t%-4s\t\t%s\n", command[i].cmd, command[i].doc);
+
+       printf("\nServices:\n\t");
+       for (i = 0; service[i].name; i++) {
+               printf("%s ", service[i].name);
+               pos += strlen(service[i].name) + 1;
+               if (pos > 60) {
+                       printf("\n\t");
+                       pos = 0;
+               }
+       }
+       printf("\n");
+}
+
+static struct option main_options[] = {
+       { "help",       0, 0, 'h' },
+       { "device",     1, 0, 'i' },
+       { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+       int i, opt;
+
+       bacpy(&interface, BDADDR_ANY);
+
+       while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+               switch(opt) {
+               case 'i':
+                       if (!strncmp(optarg, "hci", 3))
+                               hci_devba(atoi(optarg + 3), &interface);
+                       else
+                               str2ba(optarg, &interface);
+                       break;
+
+               case 'h':
+                       usage();
+                       exit(0);
+
+               default:
+                       exit(1);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               usage();
+               exit(1);
+       }
+
+       for (i = 0; command[i].cmd; i++)
+               if (strncmp(command[i].cmd, argv[0], 4) == 0)
+                       return command[i].func(argc, argv);
+
+       return 1;
+}
diff --git a/tools/ubcsp.c b/tools/ubcsp.c
new file mode 100644 (file)
index 0000000..b3f883a
--- /dev/null
@@ -0,0 +1,1180 @@
+/*\r
+ *\r
+ *  BlueZ - Bluetooth protocol stack for Linux\r
+ *\r
+ *  Copyright (C) 2000-2005  CSR Ltd.\r
+ *\r
+ *\r
+ *  Permission is hereby granted, free of charge, to any person obtaining\r
+ *  a copy of this software and associated documentation files (the\r
+ *  "Software"), to deal in the Software without restriction, including\r
+ *  without limitation the rights to use, copy, modify, merge, publish,\r
+ *  distribute, sublicense, and/or sell copies of the Software, and to\r
+ *  permit persons to whom the Software is furnished to do so, subject to\r
+ *  the following conditions:\r
+ *\r
+ *  The above copyright notice and this permission notice shall be included\r
+ *  in all copies or substantial portions of the Software.\r
+ *\r
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ *  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\r
+ *  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\r
+ *  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r
+ *  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ */\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include <config.h>\r
+#endif\r
+\r
+/*****************************************************************************/\r
+/*****************************************************************************/\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp,c                                                                 **/\r
+/**                                                                         **/\r
+/** MicroBCSP - a very low cost implementation of the BCSP protocol         **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+#include "ubcsp.h"\r
+\r
+#if SHOW_PACKET_ERRORS || SHOW_LE_STATES\r
+#include <stdio.h>\r
+#include <windows.h>\r
+#endif\r
+\r
+static uint16 ubcsp_calc_crc (uint8 ch, uint16 crc);\r
+static uint16 ubcsp_crc_reverse (uint16);\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** Constant Data - ROM                                                     **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+/* This is the storage for the link establishment messages */\r
+\r
+static const uint8 ubcsp_le_buffer[4][4] =\r
+       {\r
+               { 0xDA, 0xDC, 0xED, 0xED },\r
+               { 0xAC, 0xAF, 0xEF, 0xEE },\r
+               { 0xAD, 0xEF, 0xAC, 0xED },\r
+               { 0xDE, 0xAD, 0xD0, 0xD0 },\r
+       };\r
+\r
+/* These are the link establishment headers */\r
+/* The two version are for the CRC and non-CRC varients */\r
+\r
+#if UBCSP_CRC\r
+static const uint8 ubcsp_send_le_header[4] = \r
+       {\r
+               0x40, 0x41, 0x00, 0x7E\r
+       };\r
+#else\r
+static const uint8 ubcsp_send_le_header[4] = \r
+       {\r
+               0x00, 0x41, 0x00, 0xBE\r
+       };\r
+#endif\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** Static Data - RAM                                                       **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+/* This is the storage for all state data for ubcsp */\r
+\r
+static struct ubcsp_configuration ubcsp_config;\r
+\r
+/* This is the ACK packet header - this will be overwritten when\r
+   we create an ack packet */\r
+\r
+static uint8 ubcsp_send_ack_header[4] = \r
+       {\r
+               0x00, 0x00, 0x00, 0x00\r
+       };\r
+\r
+/* This is the deslip lookup table */\r
+\r
+static const uint8 ubcsp_deslip[2] =\r
+       {\r
+               SLIP_FRAME, SLIP_ESCAPE,\r
+       };\r
+\r
+/* This is a state machine table for link establishment */\r
+\r
+static uint8 next_le_packet[16] =\r
+       {\r
+               ubcsp_le_sync,                  // uninit\r
+               ubcsp_le_conf,                  // init\r
+               ubcsp_le_none,                  // active\r
+               ubcsp_le_none,\r
+               ubcsp_le_sync_resp,             // sync_resp\r
+               ubcsp_le_sync_resp,\r
+               ubcsp_le_none,\r
+               ubcsp_le_none,\r
+               ubcsp_le_none,                  // conf_resp\r
+               ubcsp_le_conf_resp,\r
+               ubcsp_le_conf_resp,\r
+               ubcsp_le_none,\r
+       };\r
+\r
+/* This is the storage required for building send and crc data */\r
+\r
+static uint8 ubcsp_send_header[4];\r
+static uint8 ubcsp_send_crc[2];\r
+\r
+/* This is where the receive header is stored before the payload arrives */\r
+\r
+static uint8 ubcsp_receive_header[4];\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** Code - ROM or RAM                                                       **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_initialize                                                        **/\r
+/**                                                                         **/\r
+/** This initializes the state of the ubcsp engine to a known values        **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+void ubcsp_initialize (void)\r
+{\r
+       ubcsp_config.ack_number = 0;\r
+       ubcsp_config.sequence_number = 0;\r
+       ubcsp_config.send_ptr = 0;\r
+       ubcsp_config.send_size = 0;\r
+       ubcsp_config.receive_index = -4;\r
+\r
+       ubcsp_config.delay = 0;\r
+\r
+#if SHOW_LE_STATES\r
+       printf ("Hello Link Uninitialized\n");\r
+#endif\r
+\r
+       ubcsp_config.link_establishment_state = ubcsp_le_uninitialized;\r
+       ubcsp_config.link_establishment_packet = ubcsp_le_sync;\r
+}\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_send_packet                                                       **/\r
+/**                                                                         **/\r
+/** This sends a packet structure for sending to the ubcsp engine           **/\r
+/** This can only be called when the activity indication from ubcsp_poll    **/\r
+/** indicates that a packet can be sent with UBCSP_PACKET_SENT              **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+void ubcsp_send_packet (struct ubcsp_packet *send_packet)\r
+{\r
+       /* Initialize the send data to the packet we want to send */\r
+\r
+       ubcsp_config.send_packet = send_packet;\r
+\r
+       /* we cannot send the packet at the moment\r
+          when we can at the moment, just set things to 0 */\r
+\r
+       ubcsp_config.send_size = 0;\r
+       ubcsp_config.send_ptr = 0;\r
+}\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_receive_packet                                                    **/\r
+/**                                                                         **/\r
+/** This sends a packet structure for receiving to the ubcsp engine         **/\r
+/** This can only be called when the activity indication from ubcsp_poll    **/\r
+/** indicates that a packet can be sent with UBCSP_PACKET_RECEIVED          **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+void ubcsp_receive_packet (struct ubcsp_packet *receive_packet)\r
+{\r
+       /* Initialize the receive data to the packet we want to receive */\r
+\r
+       ubcsp_config.receive_packet = receive_packet;\r
+\r
+       /* setup to receive the header first */\r
+\r
+       ubcsp_config.receive_index = -4;\r
+}\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_calc_crc                                                          **/\r
+/**                                                                         **/\r
+/** Takes the next 8 bit value ch, and updates the crc with this value      **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+\r
+#ifdef UBCSP_CRC\r
+\r
+static uint16 ubcsp_calc_crc (uint8 ch, uint16 crc)\r
+{\r
+       /* Calculate the CRC using the above 16 entry lookup table */\r
+\r
+       static const uint16 crc_table[] =\r
+               {\r
+                       0x0000, 0x1081, 0x2102, 0x3183,\r
+                       0x4204, 0x5285, 0x6306, 0x7387,\r
+                       0x8408, 0x9489, 0xa50a, 0xb58b,\r
+                       0xc60c, 0xd68d, 0xe70e, 0xf78f\r
+               };\r
+\r
+       /* Do this four bits at a time - more code, less space */\r
+\r
+    crc = (crc >> 4) ^ crc_table[(crc ^ ch) & 0x000f];\r
+    crc = (crc >> 4) ^ crc_table[(crc ^ (ch >> 4)) & 0x000f];\r
+\r
+       return crc;\r
+}\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_crc_reverse                                                       **/\r
+/**                                                                         **/\r
+/** Reserves the bits in crc and returns the new value                      **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+static uint16 ubcsp_crc_reverse (uint16 crc)\r
+{\r
+       int32\r
+               b,\r
+               rev;\r
+\r
+       /* Reserse the bits to compute the actual CRC value */\r
+\r
+       for (b = 0, rev=0; b < 16; b++)\r
+       {\r
+               rev = rev << 1;\r
+               rev |= (crc & 1);\r
+               crc = crc >> 1;\r
+       }\r
+\r
+       return rev;\r
+}\r
+\r
+#endif\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_put_slip_uart                                                     **/\r
+/**                                                                         **/\r
+/** Outputs a single octet to the uart                                      **/\r
+/** If the octet needs to be escaped, then output the escape value          **/\r
+/** and then store the second octet to be output later                      **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+static void ubcsp_put_slip_uart (uint8 ch)\r
+{\r
+       /* output a single UART octet */\r
+\r
+       /* If it needs to be escaped, then output the escape octet\r
+          and set the send_slip_escape so that the next time we\r
+          output the second octet for the escape correctly.\r
+          This is done right at the top of ubcsp_poll */\r
+\r
+       if (ch == SLIP_FRAME)\r
+       {\r
+               put_uart (SLIP_ESCAPE);\r
+               ubcsp_config.send_slip_escape = SLIP_ESCAPE_FRAME;\r
+       }\r
+       else if (ch == SLIP_ESCAPE)\r
+       {\r
+               put_uart (SLIP_ESCAPE);\r
+               ubcsp_config.send_slip_escape = SLIP_ESCAPE_ESCAPE;\r
+       }\r
+       else\r
+       {\r
+               /* Not escaped, so just output octet */\r
+\r
+               put_uart (ch);\r
+       }\r
+}\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_which_le_payload                                                  **/\r
+/**                                                                         **/\r
+/** Check the payload of this packet, and determine which of the four       **/\r
+/** link establishment packets this was.                                    **/\r
+/** Can return 5 if it is not a valid link establishment packet             **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+static uint32 ubcsp_which_le_payload (const uint8 *payload)\r
+{\r
+       static int32\r
+               octet,\r
+               loop;\r
+\r
+       /* Search through the various link establishment payloads to find\r
+          which one we have received */\r
+\r
+       for (loop = 0; loop < 4; loop ++)\r
+       {\r
+               for (octet = 0; octet < 4; octet ++)\r
+               {\r
+                       if (payload[octet] != ubcsp_le_buffer[loop][octet])\r
+                       {\r
+                               /* Bad match, just to loop again */\r
+                               goto bad_match_loop;\r
+                       }\r
+               }\r
+\r
+               /* All the octets matched, return the value */\r
+\r
+               return loop;\r
+\r
+               /* Jumps out of octet loop if we got a bad match */\r
+bad_match_loop:\r
+               {}\r
+       }\r
+\r
+       /* Non of the link establishment payloads matched - return invalid value */\r
+\r
+       return 5;\r
+}\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_recevied_packet                                                   **/\r
+/**                                                                         **/\r
+/** This function is called when we have a SLIP END octet and a full        **/\r
+/** packet header and possibly data in the receive packet                   **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+static uint8 ubcsp_recevied_packet (void)\r
+{\r
+       static uint8\r
+               receive_crc,\r
+               receive_seq,\r
+               receive_ack,\r
+               activity;\r
+\r
+#if UBCSP_CRC\r
+       static int32\r
+               loop;\r
+\r
+       static uint16\r
+               crc;\r
+#endif\r
+\r
+       static uint16\r
+               length;\r
+\r
+       /* Keep track of what activity this received packet will cause */\r
+\r
+       activity = 0;\r
+\r
+       /*** Do all error checks that we can ***/\r
+\r
+       /* First check the header checksum */\r
+\r
+       if (((ubcsp_receive_header[0] + ubcsp_receive_header[1] + ubcsp_receive_header[2] + ubcsp_receive_header[3]) & 0xff) != 0xff)\r
+       {\r
+               /* Header Checksum Error */\r
+\r
+#if SHOW_PACKET_ERRORS\r
+               printf ("\n######################## Header Checksum Error %02X %02X %02X %02X\n",\r
+                       ubcsp_receive_header[0],\r
+                       ubcsp_receive_header[1],\r
+                       ubcsp_receive_header[2],\r
+                       ubcsp_receive_header[3]);\r
+#endif\r
+\r
+               /* If we have a header checksum error, send an ack in return\r
+                  this gets a packet to be resent as quickly as possible */\r
+\r
+               ubcsp_config.send_ack = 1;\r
+\r
+               return activity;\r
+       }\r
+\r
+       /* Decode the received packets header */\r
+\r
+       ubcsp_config.receive_packet->reliable = (ubcsp_receive_header[0] & 0x80) >> 7;\r
+\r
+       receive_crc = (ubcsp_receive_header[0] & 0x40) >> 6;\r
+       receive_ack = (ubcsp_receive_header[0] & 0x38) >> 3;\r
+       receive_seq = (ubcsp_receive_header[0] & 0x07);\r
+\r
+       ubcsp_config.receive_packet->channel = (ubcsp_receive_header[1] & 0x0f);\r
+\r
+       length =\r
+               ((ubcsp_receive_header[1] & 0xf0) >> 4) |\r
+               (ubcsp_receive_header[2] << 4);\r
+\r
+#if SHOW_PACKET_ERRORS\r
+       if (ubcsp_config.receive_packet->reliable)\r
+       {\r
+               printf (" : %10d         Recv SEQ: %d ACK %d\n",\r
+                       GetTickCount () % 100000,\r
+                       receive_seq,\r
+                       receive_ack);\r
+       }\r
+       else if (ubcsp_config.receive_packet->channel != 1)\r
+       {\r
+               printf (" : %10d          Recv        ACK %d\n",\r
+                       GetTickCount () % 100000,\r
+                       receive_ack);\r
+       }\r
+#endif\r
+\r
+       /* Check for length errors */\r
+\r
+#if UBCSP_CRC\r
+       if (receive_crc)\r
+       {\r
+               /* If this packet had a CRC, then the length of the payload \r
+                  should be 2 less than the received size of the payload */\r
+\r
+               if (length + 2 != ubcsp_config.receive_index)\r
+               {\r
+                       /* Slip Length Error */\r
+\r
+#if SHOW_PACKET_ERRORS\r
+                       printf ("\n######################## Slip Length Error (With CRC) %d,%d\n", length, ubcsp_config.receive_index - 2);\r
+#endif\r
+\r
+                       /* If we have a payload length error, send an ack in return\r
+                          this gets a packet to be resent as quickly as possible */\r
+\r
+                       ubcsp_config.send_ack = 1;\r
+                       return activity;\r
+               }\r
+\r
+               /* We have a CRC at the end of this packet */\r
+\r
+               ubcsp_config.receive_index -= 2;\r
+\r
+               /* Calculate the packet CRC */\r
+\r
+               crc = 0xffff;\r
+\r
+               /* CRC the packet header */\r
+\r
+               for (loop = 0; loop < 4; loop ++)\r
+               {\r
+                       crc = ubcsp_calc_crc (ubcsp_receive_header[loop], crc);\r
+               }\r
+\r
+               /* CRC the packet payload - without the CRC bytes */\r
+\r
+               for (loop = 0; loop < ubcsp_config.receive_index; loop ++)\r
+               {\r
+                       crc = ubcsp_calc_crc (ubcsp_config.receive_packet->payload[loop], crc);\r
+               }\r
+\r
+               /* Reverse the CRC */\r
+\r
+               crc = ubcsp_crc_reverse (crc);\r
+\r
+               /* Check the CRC is correct */\r
+\r
+               if\r
+               (\r
+                       (((crc & 0xff00) >> 8) != ubcsp_config.receive_packet->payload[ubcsp_config.receive_index]) ||\r
+                       ((crc & 0xff) != ubcsp_config.receive_packet->payload[ubcsp_config.receive_index + 1])\r
+               )\r
+               {\r
+#if SHOW_PACKET_ERRORS\r
+                       printf ("\n######################## CRC Error\n");\r
+#endif\r
+\r
+                       /* If we have a packet crc error, send an ack in return\r
+                          this gets a packet to be resent as quickly as possible */\r
+\r
+                       ubcsp_config.send_ack = 1;\r
+                       return activity;\r
+               }\r
+       }\r
+       else\r
+       {\r
+#endif\r
+               /* No CRC present, so just check the length of payload with that received */\r
+\r
+               if (length != ubcsp_config.receive_index)\r
+               {\r
+                       /* Slip Length Error */\r
+\r
+#if SHOW_PACKET_ERRORS\r
+                       printf ("\n######################## Slip Length Error (No CRC) %d,%d\n", length, ubcsp_config.receive_index);\r
+#endif\r
+\r
+                       /* If we have a payload length error, send an ack in return\r
+                          this gets a packet to be resent as quickly as possible */\r
+\r
+                       ubcsp_config.send_ack = 1;\r
+                       return activity;\r
+               }\r
+#if UBCSP_CRC\r
+       }\r
+#endif\r
+\r
+       /*** We have a fully formed packet having passed all data integrity checks ***/\r
+\r
+       /* Check if we have an ACK for the last packet we sent */\r
+\r
+       if (receive_ack != ubcsp_config.sequence_number)\r
+       {\r
+               /* Since we only have a window size of 1, if the ACK is not equal to SEQ\r
+                  then the packet was sent */\r
+\r
+               if\r
+               (\r
+                       (ubcsp_config.send_packet) &&\r
+                       (ubcsp_config.send_packet->reliable)\r
+               )\r
+               {\r
+                       /* We had sent a reliable packet, so clear this packet\r
+                          Then increament the sequence number for the next packet */\r
+\r
+                       ubcsp_config.send_packet = 0;\r
+                       ubcsp_config.sequence_number ++;\r
+                       ubcsp_config.delay = 0;\r
+\r
+                       /* Notify the caller that we have SENT a packet */\r
+\r
+                       activity |= UBCSP_PACKET_SENT;\r
+               }\r
+       }\r
+\r
+       /*** Now we can concentrate of the packet we have received ***/\r
+\r
+       /* Check for Link Establishment packets */\r
+\r
+       if (ubcsp_config.receive_packet->channel == 1)\r
+       {\r
+               /* Link Establishment */\r
+\r
+               ubcsp_config.delay = 0;\r
+\r
+               /* Find which link establishment packet this payload means\r
+                  This could return 5, meaning none */\r
+\r
+               switch (ubcsp_which_le_payload (ubcsp_config.receive_packet->payload))\r
+               {\r
+                       case 0:\r
+                       {\r
+                               /* SYNC Recv'd */\r
+\r
+#if SHOW_LE_STATES\r
+                               printf ("Recv SYNC\n");\r
+#endif\r
+\r
+                               /* If we receive a SYNC, then we respond to it with a SYNC RESP\r
+                                  but only if we are not active.\r
+                                  If we are active, then we have a PEER RESET */\r
+\r
+                               if (ubcsp_config.link_establishment_state < ubcsp_le_active)\r
+                               {\r
+                                       ubcsp_config.link_establishment_resp = 1;\r
+                               }\r
+                               else\r
+                               {\r
+                                       /* Peer reset !!!! */\r
+\r
+#if SHOW_LE_STATES\r
+                                       printf ("\n\n\n\n\nPEER RESET\n\n");\r
+#endif\r
+\r
+                                       /* Reinitialize the link */\r
+\r
+                                       ubcsp_initialize ();\r
+\r
+                                       /* Tell the host what has happened */\r
+\r
+                                       return UBCSP_PEER_RESET;\r
+                               }\r
+                               break;\r
+                       }\r
+\r
+                       case 1:\r
+                       {\r
+                               /* SYNC RESP Recv'd */\r
+\r
+#if SHOW_LE_STATES\r
+                               printf ("Recv SYNC RESP\n");\r
+#endif\r
+\r
+                               /* If we receive a SYNC RESP, push us into the initialized state */\r
+\r
+                               if (ubcsp_config.link_establishment_state < ubcsp_le_initialized)\r
+                               {\r
+#if SHOW_LE_STATES\r
+                                       printf ("Link Initialized\n");\r
+#endif\r
+                                       ubcsp_config.link_establishment_state = ubcsp_le_initialized;\r
+                               }\r
+\r
+                               break;\r
+                       }\r
+\r
+                       case 2:\r
+                       {\r
+                               /* CONF Recv'd */\r
+\r
+#if SHOW_LE_STATES\r
+                               printf ("Recv CONF\n");\r
+#endif\r
+\r
+                               /* If we receive a CONF, and we are initialized or active\r
+                                  then respond with a CONF RESP */\r
+\r
+                               if (ubcsp_config.link_establishment_state >= ubcsp_le_initialized)\r
+                               {\r
+                                       ubcsp_config.link_establishment_resp = 2;\r
+                               }\r
+\r
+                               break;\r
+                       }\r
+\r
+                       case 3:\r
+                       {\r
+                               /* CONF RESP Recv'd */\r
+\r
+#if SHOW_LE_STATES\r
+                               printf ("Recv CONF RESP\n");\r
+#endif\r
+\r
+                               /* If we received a CONF RESP, then push us into the active state */\r
+\r
+                               if (ubcsp_config.link_establishment_state < ubcsp_le_active)\r
+                               {\r
+#if SHOW_LE_STATES\r
+                                       printf ("Link Active\n");\r
+#endif\r
+\r
+                                       ubcsp_config.link_establishment_state = ubcsp_le_active;\r
+                                       ubcsp_config.send_size = 0;\r
+\r
+                                       return activity | UBCSP_PACKET_SENT;\r
+                               }\r
+\r
+                               break;\r
+                       }\r
+               }\r
+\r
+               /* We have finished processing Link Establishment packets */\r
+       }\r
+       else if (ubcsp_config.receive_index)\r
+       {\r
+               /* We have some payload data we need to process\r
+                  but only if we are active - otherwise, we just ignore it */\r
+\r
+               if (ubcsp_config.link_establishment_state == ubcsp_le_active)\r
+               {\r
+                       if (ubcsp_config.receive_packet->reliable)\r
+                       {\r
+                               /* If the packet we've just received was reliable\r
+                                  then send an ACK */\r
+\r
+                               ubcsp_config.send_ack = 1;\r
+\r
+                               /* We the sequence number we received is the same as \r
+                                  the last ACK we sent, then we have received a packet in sequence */\r
+\r
+                               if (receive_seq == ubcsp_config.ack_number)\r
+                               {\r
+                                       /* Increase the ACK number - which will be sent in the next ACK \r
+                                          or normal packet we send */\r
+\r
+                                       ubcsp_config.ack_number ++;\r
+\r
+                                       /* Set the values in the receive_packet structure, so the caller\r
+                                          knows how much data we have */\r
+\r
+                                       ubcsp_config.receive_packet->length = length;\r
+                                       ubcsp_config.receive_packet = 0;\r
+\r
+                                       /* Tell the caller that we have received a packet, and that it\r
+                                          will be ACK'ed */\r
+\r
+                                       activity |= UBCSP_PACKET_RECEIVED | UBCSP_PACKET_ACK;\r
+                               }\r
+                       }\r
+                       else \r
+                       {\r
+                               /* Set the values in the receive_packet structure, so the caller\r
+                                  knows how much data we have */\r
+\r
+                               ubcsp_config.receive_packet->length = length;\r
+                               ubcsp_config.receive_packet = 0;\r
+\r
+                               /* Tell the caller that we have received a packet */\r
+\r
+                               activity |= UBCSP_PACKET_RECEIVED;\r
+                       }\r
+               }
+       }
+
+       /* Just return any activity that occurred */
+
+       return activity;
+}
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_setup_packet                                                      **/\r
+/**                                                                         **/\r
+/** This function is called to setup a packet to be sent                    **/\r
+/** This allows just a header, or a header and payload to be sent           **/\r
+/** It also allows the header checksum to be precalcuated                   **/\r
+/** or calculated here                                                      **/\r
+/** part1 is always 4 bytes                                                 **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+static void ubcsp_setup_packet (uint8 *part1, uint8 calc, uint8 *part2, uint16 len2)\r
+{\r
+       /* If we need to calculate the checksum, do that now */\r
+\r
+       if (calc)\r
+       {\r
+               part1[3] =\r
+                       ~(part1[0] + part1[1] + part1[2]);\r
+       }\r
+\r
+       /* Setup the header send pointer and size so we can clock this out */\r
+\r
+       ubcsp_config.send_ptr = part1;\r
+       ubcsp_config.send_size = 4;\r
+\r
+       /* Setup the payload send pointer and size */\r
+\r
+       ubcsp_config.next_send_ptr = part2;\r
+       ubcsp_config.next_send_size = len2;\r
+\r
+#if UBCSP_CRC\r
+       /* Initialize the crc as required */\r
+\r
+       ubcsp_config.send_crc = -1;\r
+\r
+       ubcsp_config.need_send_crc = 1;\r
+#endif\r
+}\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_sent_packet                                                       **/\r
+/**                                                                         **/\r
+/** Called when we have finished sending a packet                           **/\r
+/** If this packet was unreliable, then notify caller, and clear the data   **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+static uint8 ubcsp_sent_packet (void)\r
+{\r
+       if (ubcsp_config.send_packet)\r
+       {\r
+               if (!ubcsp_config.send_packet->reliable)\r
+               {\r
+                       /* We had a packet sent that was unreliable */\r
+\r
+                       /* Forget about this packet */\r
+\r
+                       ubcsp_config.send_packet = 0;\r
+\r
+                       /* Notify caller that they can send another one */\r
+\r
+                       return UBCSP_PACKET_SENT;\r
+               }\r
+       }\r
+\r
+       /* We didn't have a packet, or it was reliable\r
+          Must wait for ACK before allowing another packet to be sent */\r
+\r
+       return 0;\r
+}\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_poll                                                              **/\r
+/**                                                                         **/\r
+/** This is the main function for ubcsp                                     **/\r
+/** It performs a number of tasks                                           **/\r
+/**                                                                         **/\r
+/** 1) Send another octet to the UART - escaping as required                **/\r
+/** 2) Setup the payload to be sent after the header has been sent          **/\r
+/** 3) Send the CRC for the packet if required                              **/\r
+/**                                                                         **/\r
+/** 4) Calculate the next Link Establishment State                          **/\r
+/** 5) Send a Link Establishment packet                                     **/\r
+/** 6) Send a normal packet if available                                    **/\r
+/** 7) Send an ACK packet if required                                       **/\r
+/**                                                                         **/\r
+/** 8) Receive octets from UART and deslip them as required                 **/\r
+/** 9) Place received octets into receive header or receive payload buffer  **/\r
+/** 10) Process received packet when SLIP_END is received                   **/\r
+/**                                                                         **/\r
+/** 11) Keep track of ability of caller to delay recalling                  **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+uint8 ubcsp_poll (uint8 *activity)\r
+{\r
+       uint8\r
+               delay = UBCSP_POLL_TIME_IMMEDIATE;\r
+\r
+       uint8\r
+               value;\r
+\r
+       /* Assume no activity to start with */\r
+\r
+       *activity = 0;\r
+\r
+       /* If we don't have to delay, then send something if we can */\r
+\r
+       if (!ubcsp_config.delay)\r
+       {\r
+               /* Do we have something we are sending to send */\r
+\r
+               if (ubcsp_config.send_size)\r
+               {\r
+                       /* We have something to send so send it */\r
+\r
+                       if (ubcsp_config.send_slip_escape)\r
+                       {\r
+                               /* Last time we send a SLIP_ESCAPE octet\r
+                                  this time send the second escape code */\r
+\r
+                               put_uart (ubcsp_config.send_slip_escape);\r
+\r
+                               ubcsp_config.send_slip_escape = 0;\r
+                       }\r
+                       else\r
+                       {\r
+#if UBCSP_CRC\r
+                               /* get the value to send, and calculate CRC as we go */\r
+\r
+                               value = *ubcsp_config.send_ptr ++;\r
+\r
+                               ubcsp_config.send_crc = ubcsp_calc_crc (value, ubcsp_config.send_crc);\r
+\r
+                               /* Output the octet */\r
+\r
+                               ubcsp_put_slip_uart (value);\r
+#else\r
+                               /* Just output the octet*/\r
+\r
+                               ubcsp_put_slip_uart (*ubcsp_config.send_ptr ++);\r
+#endif\r
+                       }\r
+\r
+                       /* If we did output a SLIP_ESCAPE, then don't process the end of a block */\r
+\r
+                       if ((!ubcsp_config.send_slip_escape) && ((ubcsp_config.send_size = ubcsp_config.send_size - 1) == 0))\r
+                       {\r
+                               /*** We are at the end of a block - either header or payload ***/\r
+\r
+                               /* setup the next block */\r
+\r
+                               ubcsp_config.send_ptr = ubcsp_config.next_send_ptr;\r
+                               ubcsp_config.send_size = ubcsp_config.next_send_size;\r
+                               ubcsp_config.next_send_ptr = 0;\r
+                               ubcsp_config.next_send_size = 0;\r
+\r
+#if UBCSP_CRC\r
+                               /* If we have no successor block\r
+                                  then we might need to send the CRC */\r
+\r
+                               if (!ubcsp_config.send_ptr)\r
+                               {\r
+                                       if (ubcsp_config.need_send_crc)\r
+                                       {\r
+                                               /* reverse the CRC from what we computed along the way */\r
+\r
+                                               ubcsp_config.need_send_crc = 0;\r
+\r
+                                               ubcsp_config.send_crc = ubcsp_crc_reverse (ubcsp_config.send_crc);\r
+\r
+                                               /* Save in the send_crc buffer */\r
+\r
+                                               ubcsp_send_crc[0] = (uint8) (ubcsp_config.send_crc >> 8);\r
+                                               ubcsp_send_crc[1] = (uint8) ubcsp_config.send_crc;\r
+\r
+                                               /* Setup to send this buffer */\r
+\r
+                                               ubcsp_config.send_ptr = ubcsp_send_crc;\r
+                                               ubcsp_config.send_size = 2;\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               /* We don't need to send the crc\r
+                                                  either we just have, or this packet doesn't include it */\r
+\r
+                                               /* Output the end of FRAME marker */\r
+\r
+                                               put_uart (SLIP_FRAME);\r
+\r
+                                               /* Check if this is an unreliable packet */\r
+\r
+                                               *activity |= ubcsp_sent_packet ();\r
+\r
+                                               /* We've sent the packet, so don't need to have be called quickly soon */\r
+\r
+                                               delay = UBCSP_POLL_TIME_DELAY;\r
+                                       }\r
+                               }\r
+#else\r
+                               /* If we have no successor block\r
+                                  then we might need to send the CRC */\r
+\r
+                               if (!ubcsp_config.send_ptr)\r
+                               {\r
+                                       /* Output the end of FRAME marker */\r
+\r
+                                       put_uart (SLIP_FRAME);\r
+\r
+                                       /* Check if this is an unreliable packet */\r
+\r
+                                       *activity |= ubcsp_sent_packet ();\r
+\r
+                                       /* We've sent the packet, so don't need to have be called quickly soon */\r
+\r
+                                       delay = UBCSP_POLL_TIME_DELAY;\r
+                               }\r
+#endif\r
+                       }\r
+               }\r
+               else if (ubcsp_config.link_establishment_packet == ubcsp_le_none)\r
+               {\r
+                       /* We didn't have something to send\r
+                          AND we have no Link Establishment packet to send */\r
+\r
+                       if (ubcsp_config.link_establishment_resp & 2)\r
+                       {\r
+                               /* Send the start of FRAME packet */\r
+\r
+                               put_uart (SLIP_FRAME);\r
+\r
+                               /* We did require a RESP packet - so setup the send */\r
+\r
+                               ubcsp_setup_packet ((uint8*) ubcsp_send_le_header, 0, (uint8*) ubcsp_le_buffer[ubcsp_le_conf_resp], 4);\r
+\r
+                               /* We have now "sent" this packet */\r
+\r
+                               ubcsp_config.link_establishment_resp = 0;\r
+                       }\r
+                       else if (ubcsp_config.send_packet)\r
+                       {\r
+                               /* There is a packet ready to be sent */\r
+\r
+                               /* Send the start of FRAME packet */\r
+\r
+                               put_uart (SLIP_FRAME);\r
+\r
+                               /* Encode up the packet header using ACK and SEQ numbers */\r
+\r
+                               ubcsp_send_header[0] =\r
+                                       (ubcsp_config.send_packet->reliable << 7) |\r
+#if UBCSP_CRC\r
+                                       0x40 |  /* Always use CRC's */\r
+#endif\r
+                                       (ubcsp_config.ack_number << 3) | \r
+                                       (ubcsp_config.sequence_number);\r
+\r
+                               /* Encode up the packet header's channel and length */\r
+                               ubcsp_send_header[1] =\r
+                                       (ubcsp_config.send_packet->channel & 0x0f) |\r
+                                       ((ubcsp_config.send_packet->length << 4) & 0xf0);\r
+\r
+                               ubcsp_send_header[2] =\r
+                                       (ubcsp_config.send_packet->length >> 4) & 0xff;\r
+\r
+                               /* Let the ubcsp_setup_packet function calculate the header checksum */\r
+\r
+                               ubcsp_setup_packet ((uint8*) ubcsp_send_header, 1, ubcsp_config.send_packet->payload, ubcsp_config.send_packet->length);\r
+\r
+                               /* Don't need to send an ACK - we just place on in this packet */\r
+\r
+                               ubcsp_config.send_ack = 0;\r
+                               \r
+#if SHOW_PACKET_ERRORS\r
+                               printf (" : %10d Send %d Ack %d\n",\r
+                                       GetTickCount () % 100000,\r
+                                       ubcsp_config.sequence_number,\r
+                                       ubcsp_config.ack_number);\r
+#endif\r
+                       }\r
+                       else if (ubcsp_config.send_ack)\r
+                       {\r
+                               /* Send the start of FRAME packet */\r
+\r
+                               put_uart (SLIP_FRAME);\r
+\r
+#if SHOW_PACKET_ERRORS\r
+                               printf (" : %10d Send ACK %d\n",\r
+                                       GetTickCount () % 100000,\r
+                                       ubcsp_config.ack_number);\r
+#endif\r
+\r
+                               /* The ack packet is already computed apart from the first octet */\r
+\r
+                               ubcsp_send_ack_header[0] =\r
+#if UBCSP_CRC\r
+                                       0x40 | \r
+#endif\r
+                                       (ubcsp_config.ack_number << 3);\r
+\r
+                               /* Let the ubcsp_setup_packet function calculate the header checksum */\r
+\r
+                               ubcsp_setup_packet (ubcsp_send_ack_header, 1, 0, 0);\r
+\r
+                               /* We've now sent the ack */\r
+\r
+                               ubcsp_config.send_ack = 0;\r
+                       }\r
+                       else\r
+                       {\r
+                               /* We didn't have a Link Establishment response packet,\r
+                                  a normal packet or an ACK packet to send */\r
+\r
+                               delay = UBCSP_POLL_TIME_DELAY;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+#if SHOW_PACKET_ERRORS\r
+//                     printf (" : %10d Send LE %d\n",\r
+//                             GetTickCount () % 100000,\r
+//                             ubcsp_config.link_establishment_packet);\r
+#endif\r
+\r
+                       /* Send A Link Establishment Message */\r
+\r
+                       put_uart (SLIP_FRAME);\r
+\r
+                       /* Send the Link Establishment header followed by the \r
+                          Link Establishment packet */\r
+\r
+                       ubcsp_setup_packet ((uint8*) ubcsp_send_le_header, 0, (uint8*) ubcsp_le_buffer[ubcsp_config.link_establishment_packet], 4);\r
+\r
+                       /* start sending immediately */\r
+\r
+                       ubcsp_config.delay = 0;\r
+\r
+                       /* workout what the next link establishment packet should be */\r
+\r
+                       ubcsp_config.link_establishment_packet = next_le_packet[ubcsp_config.link_establishment_state + ubcsp_config.link_establishment_resp * 4];\r
+\r
+                       /* We have now delt with any response packet that we needed */\r
+\r
+                       ubcsp_config.link_establishment_resp = 0;\r
+\r
+                       return 0;\r
+               }\r
+       }\r
+\r
+       /* We now need to receive any octets from the UART */\r
+\r
+       while ((ubcsp_config.receive_packet) && (get_uart (&value)))\r
+       {\r
+               /* If the last octet was SLIP_ESCAPE, then special processing is required */\r
+\r
+               if (ubcsp_config.receive_slip_escape)\r
+               {\r
+                       /* WARNING - out of range values are not detected !!!\r
+                          This will probably be caught with the checksum or CRC check */\r
+\r
+                       value = ubcsp_deslip[value - SLIP_ESCAPE_FRAME];\r
+\r
+                       ubcsp_config.receive_slip_escape = 0;\r
+               }\r
+               else\r
+               {\r
+                       /* Check for the SLIP_FRAME octet - must be start or end of packet */\r
+                       if (value == SLIP_FRAME)\r
+                       {\r
+                               /* If we had a full header then we have a packet */\r
+\r
+                               if (ubcsp_config.receive_index >= 0)\r
+                               {\r
+                                       /* process the received packet */\r
+\r
+                                       *activity |= ubcsp_recevied_packet ();\r
+\r
+                                       if (*activity & UBCSP_PACKET_ACK)\r
+                                       {\r
+                                               /* We need to ACK this packet, then don't delay its sending */\r
+                                               ubcsp_config.delay = 0;\r
+                                       }\r
+                               }\r
+\r
+                               /* Setup to receive the next packet */\r
+\r
+                               ubcsp_config.receive_index = -4;\r
+\r
+                               /* Ok, next octet */\r
+\r
+                               goto finished_receive;\r
+                       }\r
+                       else if (value == SLIP_ESCAPE)\r
+                       {\r
+                               /* If we receive a SLIP_ESCAPE,\r
+                                  then remember to process the next special octet */\r
+\r
+                               ubcsp_config.receive_slip_escape = 1;\r
+\r
+                               goto finished_receive;\r
+                       }\r
+               }\r
+\r
+               if (ubcsp_config.receive_index < 0)\r
+               {\r
+                       /* We are still receiving the header */\r
+\r
+                       ubcsp_receive_header[ubcsp_config.receive_index + 4] = value;\r
+\r
+                       ubcsp_config.receive_index ++;\r
+               }\r
+               else if (ubcsp_config.receive_index < ubcsp_config.receive_packet->length)
+               {
+                       /* We are receiving the payload */
+                       /* We might stop coming here if we are receiving a
+                          packet which is longer than the receive_packet->length
+                          given by the host */
+
+                       ubcsp_config.receive_packet->payload[ubcsp_config.receive_index] = value;\r
+\r
+                       ubcsp_config.receive_index ++;\r
+               }\r
+\r
+finished_receive:\r
+               {\r
+               }\r
+       }\r
+\r
+       if (ubcsp_config.delay > 0)\r
+       {\r
+               /* We were delayed so delay some more\r
+                  this could be cancelled if we received something */\r
+\r
+               ubcsp_config.delay --;\r
+       }\r
+       else\r
+       {\r
+               /* We had no delay, so use the delay we just decided to us */\r
+\r
+               ubcsp_config.delay = delay;\r
+       }\r
+\r
+       /* Report the current delay to the user */\r
+\r
+       return ubcsp_config.delay;\r
+}\r
diff --git a/tools/ubcsp.h b/tools/ubcsp.h
new file mode 100644 (file)
index 0000000..6a74e9a
--- /dev/null
@@ -0,0 +1,208 @@
+/*\r
+ *\r
+ *  BlueZ - Bluetooth protocol stack for Linux\r
+ *\r
+ *  Copyright (C) 2000-2005  CSR Ltd.\r
+ *\r
+ *\r
+ *  Permission is hereby granted, free of charge, to any person obtaining\r
+ *  a copy of this software and associated documentation files (the\r
+ *  "Software"), to deal in the Software without restriction, including\r
+ *  without limitation the rights to use, copy, modify, merge, publish,\r
+ *  distribute, sublicense, and/or sell copies of the Software, and to\r
+ *  permit persons to whom the Software is furnished to do so, subject to\r
+ *  the following conditions:\r
+ *\r
+ *  The above copyright notice and this permission notice shall be included\r
+ *  in all copies or substantial portions of the Software.\r
+ *\r
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ *  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\r
+ *  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\r
+ *  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r
+ *  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ */\r
+\r
+#ifndef UBCSP_INCLUDE_H\r
+#define UBCSP_INCLUDE_H\r
+\r
+/*****************************************************************************/\r
+/*****************************************************************************/\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp.h                                                                 **/\r
+/**                                                                         **/\r
+/** MicroBCSP - a very low cost implementation of the BCSP protocol         **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+/* If we wish to use CRC's, then change 0 to 1 in the next line */\r
+#define UBCSP_CRC 1\r
+\r
+/* Define some basic types - change these for your architecture */\r
+typedef unsigned char uint8;\r
+typedef unsigned short uint16;\r
+typedef unsigned int uint32;\r
+typedef signed char int8;\r
+typedef signed short int16;\r
+typedef signed int int32;\r
+\r
+/* The defines below require a printf function to be available */\r
+\r
+/* Do we want to show packet errors in debug output */\r
+#define SHOW_PACKET_ERRORS     0\r
+\r
+/* Do we want to show Link Establishment State transitions in debug output */\r
+#define SHOW_LE_STATES         0\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_packet                                                            **/\r
+/**                                                                         **/\r
+/** This is description of a bcsp packet for the upper layer                **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+struct ubcsp_packet\r
+{\r
+       uint8 channel;          /* Which Channel this packet is to/from */\r
+       uint8 reliable;         /* Is this packet reliable */\r
+       uint8 use_crc;          /* Does this packet use CRC data protection */\r
+       uint16 length;          /* What is the length of the payload data */\r
+       uint8 *payload;         /* The payload data itself - size of length */\r
+};\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_configuration                                                     **/\r
+/**                                                                         **/\r
+/** This is the main configuration of the ubcsp engine                      **/\r
+/** All state variables are stored in this structure                        **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+enum ubcsp_link_establishment_state\r
+{\r
+       ubcsp_le_uninitialized,\r
+       ubcsp_le_initialized,\r
+       ubcsp_le_active\r
+};\r
+\r
+enum ubcsp_link_establishment_packet\r
+{\r
+       ubcsp_le_sync,\r
+       ubcsp_le_sync_resp,\r
+       ubcsp_le_conf,\r
+       ubcsp_le_conf_resp,\r
+       ubcsp_le_none\r
+};\r
+\r
+struct ubcsp_configuration\r
+{\r
+       uint8 link_establishment_state;\r
+       uint8 link_establishment_resp;\r
+       uint8 link_establishment_packet;\r
+\r
+       uint8 sequence_number:3;\r
+       uint8 ack_number:3;\r
+       uint8 send_ack;\r
+       struct ubcsp_packet *send_packet;\r
+       struct ubcsp_packet *receive_packet;\r
+\r
+       uint8 receive_header_checksum;\r
+       uint8 receive_slip_escape;\r
+       int32 receive_index;\r
+\r
+       uint8 send_header_checksum;\r
+#ifdef UBCSP_CRC\r
+       uint8 need_send_crc;\r
+       uint16 send_crc;\r
+#endif\r
+       uint8 send_slip_escape;\r
+\r
+       uint8 *send_ptr;\r
+       int32 send_size;\r
+       uint8 *next_send_ptr;\r
+       int32 next_send_size;\r
+\r
+       int8 delay;\r
+};\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** ubcsp_poll sets activity from an OR of these flags                      **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+#define UBCSP_PACKET_SENT 0x01\r
+#define UBCSP_PACKET_RECEIVED 0x02\r
+#define UBCSP_PEER_RESET 0x04\r
+#define UBCSP_PACKET_ACK 0x08\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** This is the functional interface for ucbsp                              **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+void ubcsp_initialize (void);\r
+void ubcsp_send_packet (struct ubcsp_packet *send_packet);\r
+void ubcsp_receive_packet (struct ubcsp_packet *receive_packet);\r
+uint8 ubcsp_poll (uint8 *activity);\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** Slip Escape Values                                                      **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+#define SLIP_FRAME 0xC0\r
+#define SLIP_ESCAPE 0xDB\r
+#define SLIP_ESCAPE_FRAME 0xDC\r
+#define SLIP_ESCAPE_ESCAPE 0xDD\r
+\r
+/*****************************************************************************/\r
+/*****************************************************************************/\r
+/*****************************************************************************/\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** These functions need to be linked into your system                      **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** put_uart outputs a single octet over the UART Tx line                   **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+extern void put_uart (uint8);\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** get_uart receives a single octet over the UART Rx line                  **/\r
+/** if no octet is available, then this returns 0                           **/\r
+/** if an octet was read, then this is returned in the argument and         **/\r
+/**   the function returns 1                                                **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+extern uint8 get_uart (uint8 *);\r
+\r
+/*****************************************************************************/\r
+/**                                                                         **/\r
+/** These defines should be changed to your systems concept of 100ms        **/\r
+/**                                                                         **/\r
+/*****************************************************************************/\r
+\r
+#define UBCSP_POLL_TIME_IMMEDIATE   0\r
+#define UBCSP_POLL_TIME_DELAY       25\r
+\r
+/*****************************************************************************/\r
+/*****************************************************************************/\r
+/*****************************************************************************/\r
+#endif
diff --git a/unit/test-eir.c b/unit/test-eir.c
new file mode 100644 (file)
index 0000000..cefcacd
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011  Intel Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <stdint.h>
+
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "eir.h"
+
+START_TEST(test_basic)
+{
+       struct eir_data data;
+       unsigned char buf[HCI_MAX_EIR_LENGTH];
+       int err;
+
+       memset(buf, 0, sizeof(buf));
+       memset(&data, 0, sizeof(data));
+
+       err = eir_parse(&data, buf, HCI_MAX_EIR_LENGTH);
+       ck_assert(err == 0);
+       ck_assert(data.services == NULL);
+       ck_assert(data.name == NULL);
+
+       eir_data_free(&data);
+}
+END_TEST
+
+static void add_test(Suite *s, const char *name, TFun func)
+{
+       TCase *t;
+
+       t = tcase_create(name);
+       tcase_add_test(t, func);
+       suite_add_tcase(s, t);
+}
+
+int main(int argc, char *argv[])
+{
+       int fails;
+       SRunner *sr;
+       Suite *s;
+
+       s = suite_create("EIR");
+
+       add_test(s, "basic", test_basic);
+
+       sr = srunner_create(s);
+
+       srunner_run_all(sr, CK_NORMAL);
+
+       fails = srunner_ntests_failed(sr);
+
+       srunner_free(sr);
+
+       if (fails > 0)
+               return -1;
+
+       return 0;
+}
diff --git a/ylwrap b/ylwrap
new file mode 100755 (executable)
index 0000000..9253635
--- /dev/null
+++ b/ylwrap
@@ -0,0 +1,226 @@
+#! /bin/sh
+# ylwrap - wrapper for lex/yacc invocations.
+
+scriptversion=2011-08-25.18; # UTC
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005,
+# 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
+#
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+case "$1" in
+  '')
+    echo "$0: No files given.  Try \`$0 --help' for more information." 1>&2
+    exit 1
+    ;;
+  --basedir)
+    basedir=$2
+    shift 2
+    ;;
+  -h|--h*)
+    cat <<\EOF
+Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]...
+
+Wrapper for lex/yacc invocations, renaming files as desired.
+
+  INPUT is the input file
+  OUTPUT is one file PROG generates
+  DESIRED is the file we actually want instead of OUTPUT
+  PROGRAM is program to run
+  ARGS are passed to PROG
+
+Any number of OUTPUT,DESIRED pairs may be used.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit $?
+    ;;
+  -v|--v*)
+    echo "ylwrap $scriptversion"
+    exit $?
+    ;;
+esac
+
+
+# The input.
+input="$1"
+shift
+case "$input" in
+  [\\/]* | ?:[\\/]*)
+    # Absolute path; do nothing.
+    ;;
+  *)
+    # Relative path.  Make it absolute.
+    input="`pwd`/$input"
+    ;;
+esac
+
+pairlist=
+while test "$#" -ne 0; do
+  if test "$1" = "--"; then
+    shift
+    break
+  fi
+  pairlist="$pairlist $1"
+  shift
+done
+
+# The program to run.
+prog="$1"
+shift
+# Make any relative path in $prog absolute.
+case "$prog" in
+  [\\/]* | ?:[\\/]*) ;;
+  *[\\/]*) prog="`pwd`/$prog" ;;
+esac
+
+# FIXME: add hostname here for parallel makes that run commands on
+# other machines.  But that might take us over the 14-char limit.
+dirname=ylwrap$$
+do_exit="cd '`pwd`' && rm -rf $dirname > /dev/null 2>&1;"' (exit $ret); exit $ret'
+trap "ret=129; $do_exit" 1
+trap "ret=130; $do_exit" 2
+trap "ret=141; $do_exit" 13
+trap "ret=143; $do_exit" 15
+mkdir $dirname || exit 1
+
+cd $dirname
+
+case $# in
+  0) "$prog" "$input" ;;
+  *) "$prog" "$@" "$input" ;;
+esac
+ret=$?
+
+if test $ret -eq 0; then
+  set X $pairlist
+  shift
+  first=yes
+  # Since DOS filename conventions don't allow two dots,
+  # the DOS version of Bison writes out y_tab.c instead of y.tab.c
+  # and y_tab.h instead of y.tab.h. Test to see if this is the case.
+  y_tab_nodot="no"
+  if test -f y_tab.c || test -f y_tab.h; then
+    y_tab_nodot="yes"
+  fi
+
+  # The directory holding the input.
+  input_dir=`echo "$input" | sed -e 's,\([\\/]\)[^\\/]*$,\1,'`
+  # Quote $INPUT_DIR so we can use it in a regexp.
+  # FIXME: really we should care about more than `.' and `\'.
+  input_rx=`echo "$input_dir" | sed 's,\\\\,\\\\\\\\,g;s,\\.,\\\\.,g'`
+
+  while test "$#" -ne 0; do
+    from="$1"
+    # Handle y_tab.c and y_tab.h output by DOS
+    if test $y_tab_nodot = "yes"; then
+      if test $from = "y.tab.c"; then
+        from="y_tab.c"
+      else
+        if test $from = "y.tab.h"; then
+          from="y_tab.h"
+        fi
+      fi
+    fi
+    if test -f "$from"; then
+      # If $2 is an absolute path name, then just use that,
+      # otherwise prepend `../'.
+      case "$2" in
+        [\\/]* | ?:[\\/]*) target="$2";;
+        *) target="../$2";;
+      esac
+
+      # We do not want to overwrite a header file if it hasn't
+      # changed.  This avoid useless recompilations.  However the
+      # parser itself (the first file) should always be updated,
+      # because it is the destination of the .y.c rule in the
+      # Makefile.  Divert the output of all other files to a temporary
+      # file so we can compare them to existing versions.
+      if test $first = no; then
+        realtarget="$target"
+        target="tmp-`echo $target | sed s/.*[\\/]//g`"
+      fi
+      # Edit out `#line' or `#' directives.
+      #
+      # We don't want the resulting debug information to point at
+      # an absolute srcdir; it is better for it to just mention the
+      # .y file with no path.
+      #
+      # We want to use the real output file name, not yy.lex.c for
+      # instance.
+      #
+      # We want the include guards to be adjusted too.
+      FROM=`echo "$from" | sed \
+            -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
+            -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
+      TARGET=`echo "$2" | sed \
+            -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
+            -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
+
+      sed -e "/^#/!b" -e "s,$input_rx,," -e "s,$from,$2," \
+          -e "s,$FROM,$TARGET," "$from" >"$target" || ret=$?
+
+      # Check whether header files must be updated.
+      if test $first = no; then
+        if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then
+          echo "$2" is unchanged
+          rm -f "$target"
+        else
+          echo updating "$2"
+          mv -f "$target" "$realtarget"
+        fi
+      fi
+    else
+      # A missing file is only an error for the first file.  This
+      # is a blatant hack to let us support using "yacc -d".  If -d
+      # is not specified, we don't want an error when the header
+      # file is "missing".
+      if test $first = yes; then
+        ret=1
+      fi
+    fi
+    shift
+    shift
+    first=no
+  done
+else
+  ret=$?
+fi
+
+# Remove the directory.
+cd ..
+rm -rf $dirname
+
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End: